0001
0002
0003
0004
0005
0006
0007 #include <linux/clk-provider.h>
0008 #include <linux/module.h>
0009
0010 #include "clk-regmap.h"
0011 #include "vid-pll-div.h"
0012
0013 static inline struct meson_vid_pll_div_data *
0014 meson_vid_pll_div_data(struct clk_regmap *clk)
0015 {
0016 return (struct meson_vid_pll_div_data *)clk->data;
0017 }
0018
0019
0020
0021
0022
0023
0024
0025
0026 struct vid_pll_div {
0027 unsigned int shift_val;
0028 unsigned int shift_sel;
0029 unsigned int divider;
0030 unsigned int multiplier;
0031 };
0032
0033 #define VID_PLL_DIV(_val, _sel, _ft, _fb) \
0034 { \
0035 .shift_val = (_val), \
0036 .shift_sel = (_sel), \
0037 .divider = (_ft), \
0038 .multiplier = (_fb), \
0039 }
0040
0041 static const struct vid_pll_div vid_pll_div_table[] = {
0042 VID_PLL_DIV(0x0aaa, 0, 2, 1),
0043 VID_PLL_DIV(0x5294, 2, 5, 2),
0044 VID_PLL_DIV(0x0db6, 0, 3, 1),
0045 VID_PLL_DIV(0x36cc, 1, 7, 2),
0046 VID_PLL_DIV(0x6666, 2, 15, 4),
0047 VID_PLL_DIV(0x0ccc, 0, 4, 1),
0048 VID_PLL_DIV(0x739c, 2, 5, 1),
0049 VID_PLL_DIV(0x0e38, 0, 6, 1),
0050 VID_PLL_DIV(0x0000, 3, 25, 4),
0051 VID_PLL_DIV(0x3c78, 1, 7, 1),
0052 VID_PLL_DIV(0x78f0, 2, 15, 2),
0053 VID_PLL_DIV(0x0fc0, 0, 12, 1),
0054 VID_PLL_DIV(0x3f80, 1, 14, 1),
0055 VID_PLL_DIV(0x7f80, 2, 15, 1),
0056 };
0057
0058 #define to_meson_vid_pll_div(_hw) \
0059 container_of(_hw, struct meson_vid_pll_div, hw)
0060
0061 static const struct vid_pll_div *_get_table_val(unsigned int shift_val,
0062 unsigned int shift_sel)
0063 {
0064 int i;
0065
0066 for (i = 0 ; i < ARRAY_SIZE(vid_pll_div_table) ; ++i) {
0067 if (vid_pll_div_table[i].shift_val == shift_val &&
0068 vid_pll_div_table[i].shift_sel == shift_sel)
0069 return &vid_pll_div_table[i];
0070 }
0071
0072 return NULL;
0073 }
0074
0075 static unsigned long meson_vid_pll_div_recalc_rate(struct clk_hw *hw,
0076 unsigned long parent_rate)
0077 {
0078 struct clk_regmap *clk = to_clk_regmap(hw);
0079 struct meson_vid_pll_div_data *pll_div = meson_vid_pll_div_data(clk);
0080 const struct vid_pll_div *div;
0081
0082 div = _get_table_val(meson_parm_read(clk->map, &pll_div->val),
0083 meson_parm_read(clk->map, &pll_div->sel));
0084 if (!div || !div->divider) {
0085 pr_debug("%s: Invalid config value for vid_pll_div\n", __func__);
0086 return 0;
0087 }
0088
0089 return DIV_ROUND_UP_ULL(parent_rate * div->multiplier, div->divider);
0090 }
0091
0092 const struct clk_ops meson_vid_pll_div_ro_ops = {
0093 .recalc_rate = meson_vid_pll_div_recalc_rate,
0094 };
0095 EXPORT_SYMBOL_GPL(meson_vid_pll_div_ro_ops);
0096
0097 MODULE_DESCRIPTION("Amlogic video pll divider driver");
0098 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
0099 MODULE_LICENSE("GPL v2");