Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
0002 /*
0003  * Copyright (c) 2018 BayLibre, SAS.
0004  * Author: Jerome Brunet <jbrunet@baylibre.com>
0005  */
0006 
0007 #include <linux/clk-provider.h>
0008 #include <linux/module.h>
0009 
0010 #include "clk-regmap.h"
0011 #include "clk-phase.h"
0012 
0013 #define phase_step(_width) (360 / (1 << (_width)))
0014 
0015 static inline struct meson_clk_phase_data *
0016 meson_clk_phase_data(struct clk_regmap *clk)
0017 {
0018     return (struct meson_clk_phase_data *)clk->data;
0019 }
0020 
0021 static int meson_clk_degrees_from_val(unsigned int val, unsigned int width)
0022 {
0023     return phase_step(width) * val;
0024 }
0025 
0026 static unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width)
0027 {
0028     unsigned int val = DIV_ROUND_CLOSEST(degrees, phase_step(width));
0029 
0030     /*
0031      * This last calculation is here for cases when degrees is rounded
0032      * to 360, in which case val == (1 << width).
0033      */
0034     return val % (1 << width);
0035 }
0036 
0037 static int meson_clk_phase_get_phase(struct clk_hw *hw)
0038 {
0039     struct clk_regmap *clk = to_clk_regmap(hw);
0040     struct meson_clk_phase_data *phase = meson_clk_phase_data(clk);
0041     unsigned int val;
0042 
0043     val = meson_parm_read(clk->map, &phase->ph);
0044 
0045     return meson_clk_degrees_from_val(val, phase->ph.width);
0046 }
0047 
0048 static int meson_clk_phase_set_phase(struct clk_hw *hw, int degrees)
0049 {
0050     struct clk_regmap *clk = to_clk_regmap(hw);
0051     struct meson_clk_phase_data *phase = meson_clk_phase_data(clk);
0052     unsigned int val;
0053 
0054     val = meson_clk_degrees_to_val(degrees, phase->ph.width);
0055     meson_parm_write(clk->map, &phase->ph, val);
0056 
0057     return 0;
0058 }
0059 
0060 const struct clk_ops meson_clk_phase_ops = {
0061     .get_phase  = meson_clk_phase_get_phase,
0062     .set_phase  = meson_clk_phase_set_phase,
0063 };
0064 EXPORT_SYMBOL_GPL(meson_clk_phase_ops);
0065 
0066 /*
0067  * This is a special clock for the audio controller.
0068  * The phase of mst_sclk clock output can be controlled independently
0069  * for the outside world (ph0), the tdmout (ph1) and tdmin (ph2).
0070  * Controlling these 3 phases as just one makes things simpler and
0071  * give the same clock view to all the element on the i2s bus.
0072  * If necessary, we can still control the phase in the tdm block
0073  * which makes these independent control redundant.
0074  */
0075 static inline struct meson_clk_triphase_data *
0076 meson_clk_triphase_data(struct clk_regmap *clk)
0077 {
0078     return (struct meson_clk_triphase_data *)clk->data;
0079 }
0080 
0081 static int meson_clk_triphase_sync(struct clk_hw *hw)
0082 {
0083     struct clk_regmap *clk = to_clk_regmap(hw);
0084     struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
0085     unsigned int val;
0086 
0087     /* Get phase 0 and sync it to phase 1 and 2 */
0088     val = meson_parm_read(clk->map, &tph->ph0);
0089     meson_parm_write(clk->map, &tph->ph1, val);
0090     meson_parm_write(clk->map, &tph->ph2, val);
0091 
0092     return 0;
0093 }
0094 
0095 static int meson_clk_triphase_get_phase(struct clk_hw *hw)
0096 {
0097     struct clk_regmap *clk = to_clk_regmap(hw);
0098     struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
0099     unsigned int val;
0100 
0101     /* Phase are in sync, reading phase 0 is enough */
0102     val = meson_parm_read(clk->map, &tph->ph0);
0103 
0104     return meson_clk_degrees_from_val(val, tph->ph0.width);
0105 }
0106 
0107 static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees)
0108 {
0109     struct clk_regmap *clk = to_clk_regmap(hw);
0110     struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
0111     unsigned int val;
0112 
0113     val = meson_clk_degrees_to_val(degrees, tph->ph0.width);
0114     meson_parm_write(clk->map, &tph->ph0, val);
0115     meson_parm_write(clk->map, &tph->ph1, val);
0116     meson_parm_write(clk->map, &tph->ph2, val);
0117 
0118     return 0;
0119 }
0120 
0121 const struct clk_ops meson_clk_triphase_ops = {
0122     .init       = meson_clk_triphase_sync,
0123     .get_phase  = meson_clk_triphase_get_phase,
0124     .set_phase  = meson_clk_triphase_set_phase,
0125 };
0126 EXPORT_SYMBOL_GPL(meson_clk_triphase_ops);
0127 
0128 /*
0129  * This is a special clock for the audio controller.
0130  * This drive a bit clock inverter for which the
0131  * opposite value of the inverter bit needs to be manually
0132  * set into another bit
0133  */
0134 static inline struct meson_sclk_ws_inv_data *
0135 meson_sclk_ws_inv_data(struct clk_regmap *clk)
0136 {
0137     return (struct meson_sclk_ws_inv_data *)clk->data;
0138 }
0139 
0140 static int meson_sclk_ws_inv_sync(struct clk_hw *hw)
0141 {
0142     struct clk_regmap *clk = to_clk_regmap(hw);
0143     struct meson_sclk_ws_inv_data *tph = meson_sclk_ws_inv_data(clk);
0144     unsigned int val;
0145 
0146     /* Get phase and sync the inverted value to ws */
0147     val = meson_parm_read(clk->map, &tph->ph);
0148     meson_parm_write(clk->map, &tph->ws, val ? 0 : 1);
0149 
0150     return 0;
0151 }
0152 
0153 static int meson_sclk_ws_inv_get_phase(struct clk_hw *hw)
0154 {
0155     struct clk_regmap *clk = to_clk_regmap(hw);
0156     struct meson_sclk_ws_inv_data *tph = meson_sclk_ws_inv_data(clk);
0157     unsigned int val;
0158 
0159     val = meson_parm_read(clk->map, &tph->ph);
0160 
0161     return meson_clk_degrees_from_val(val, tph->ph.width);
0162 }
0163 
0164 static int meson_sclk_ws_inv_set_phase(struct clk_hw *hw, int degrees)
0165 {
0166     struct clk_regmap *clk = to_clk_regmap(hw);
0167     struct meson_sclk_ws_inv_data *tph = meson_sclk_ws_inv_data(clk);
0168     unsigned int val;
0169 
0170     val = meson_clk_degrees_to_val(degrees, tph->ph.width);
0171     meson_parm_write(clk->map, &tph->ph, val);
0172     meson_parm_write(clk->map, &tph->ws, val ? 0 : 1);
0173     return 0;
0174 }
0175 
0176 const struct clk_ops meson_sclk_ws_inv_ops = {
0177     .init       = meson_sclk_ws_inv_sync,
0178     .get_phase  = meson_sclk_ws_inv_get_phase,
0179     .set_phase  = meson_sclk_ws_inv_set_phase,
0180 };
0181 EXPORT_SYMBOL_GPL(meson_sclk_ws_inv_ops);
0182 
0183 
0184 MODULE_DESCRIPTION("Amlogic phase driver");
0185 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
0186 MODULE_LICENSE("GPL v2");