brintos

brintos / linux-shallow public Read only

0
0
Text · 5.3 KiB · c1526fb Raw
187 lines · c
1// SPDX-License-Identifier: (GPL-2.0 OR MIT)2/*3 * Copyright (c) 2018 BayLibre, SAS.4 * Author: Jerome Brunet <jbrunet@baylibre.com>5 */6 7#include <linux/clk-provider.h>8#include <linux/module.h>9 10#include "clk-regmap.h"11#include "clk-phase.h"12 13#define phase_step(_width) (360 / (1 << (_width)))14 15static inline struct meson_clk_phase_data *16meson_clk_phase_data(struct clk_regmap *clk)17{18	return (struct meson_clk_phase_data *)clk->data;19}20 21static int meson_clk_degrees_from_val(unsigned int val, unsigned int width)22{23	return phase_step(width) * val;24}25 26static unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width)27{28	unsigned int val = DIV_ROUND_CLOSEST(degrees, phase_step(width));29 30	/*31	 * This last calculation is here for cases when degrees is rounded32	 * to 360, in which case val == (1 << width).33	 */34	return val % (1 << width);35}36 37static int meson_clk_phase_get_phase(struct clk_hw *hw)38{39	struct clk_regmap *clk = to_clk_regmap(hw);40	struct meson_clk_phase_data *phase = meson_clk_phase_data(clk);41	unsigned int val;42 43	val = meson_parm_read(clk->map, &phase->ph);44 45	return meson_clk_degrees_from_val(val, phase->ph.width);46}47 48static int meson_clk_phase_set_phase(struct clk_hw *hw, int degrees)49{50	struct clk_regmap *clk = to_clk_regmap(hw);51	struct meson_clk_phase_data *phase = meson_clk_phase_data(clk);52	unsigned int val;53 54	val = meson_clk_degrees_to_val(degrees, phase->ph.width);55	meson_parm_write(clk->map, &phase->ph, val);56 57	return 0;58}59 60const struct clk_ops meson_clk_phase_ops = {61	.get_phase	= meson_clk_phase_get_phase,62	.set_phase	= meson_clk_phase_set_phase,63};64EXPORT_SYMBOL_NS_GPL(meson_clk_phase_ops, CLK_MESON);65 66/*67 * This is a special clock for the audio controller.68 * The phase of mst_sclk clock output can be controlled independently69 * for the outside world (ph0), the tdmout (ph1) and tdmin (ph2).70 * Controlling these 3 phases as just one makes things simpler and71 * give the same clock view to all the element on the i2s bus.72 * If necessary, we can still control the phase in the tdm block73 * which makes these independent control redundant.74 */75static inline struct meson_clk_triphase_data *76meson_clk_triphase_data(struct clk_regmap *clk)77{78	return (struct meson_clk_triphase_data *)clk->data;79}80 81static int meson_clk_triphase_sync(struct clk_hw *hw)82{83	struct clk_regmap *clk = to_clk_regmap(hw);84	struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);85	unsigned int val;86 87	/* Get phase 0 and sync it to phase 1 and 2 */88	val = meson_parm_read(clk->map, &tph->ph0);89	meson_parm_write(clk->map, &tph->ph1, val);90	meson_parm_write(clk->map, &tph->ph2, val);91 92	return 0;93}94 95static int meson_clk_triphase_get_phase(struct clk_hw *hw)96{97	struct clk_regmap *clk = to_clk_regmap(hw);98	struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);99	unsigned int val;100 101	/* Phase are in sync, reading phase 0 is enough */102	val = meson_parm_read(clk->map, &tph->ph0);103 104	return meson_clk_degrees_from_val(val, tph->ph0.width);105}106 107static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees)108{109	struct clk_regmap *clk = to_clk_regmap(hw);110	struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);111	unsigned int val;112 113	val = meson_clk_degrees_to_val(degrees, tph->ph0.width);114	meson_parm_write(clk->map, &tph->ph0, val);115	meson_parm_write(clk->map, &tph->ph1, val);116	meson_parm_write(clk->map, &tph->ph2, val);117 118	return 0;119}120 121const struct clk_ops meson_clk_triphase_ops = {122	.init		= meson_clk_triphase_sync,123	.get_phase	= meson_clk_triphase_get_phase,124	.set_phase	= meson_clk_triphase_set_phase,125};126EXPORT_SYMBOL_NS_GPL(meson_clk_triphase_ops, CLK_MESON);127 128/*129 * This is a special clock for the audio controller.130 * This drive a bit clock inverter for which the131 * opposite value of the inverter bit needs to be manually132 * set into another bit133 */134static inline struct meson_sclk_ws_inv_data *135meson_sclk_ws_inv_data(struct clk_regmap *clk)136{137	return (struct meson_sclk_ws_inv_data *)clk->data;138}139 140static int meson_sclk_ws_inv_sync(struct clk_hw *hw)141{142	struct clk_regmap *clk = to_clk_regmap(hw);143	struct meson_sclk_ws_inv_data *tph = meson_sclk_ws_inv_data(clk);144	unsigned int val;145 146	/* Get phase and sync the inverted value to ws */147	val = meson_parm_read(clk->map, &tph->ph);148	meson_parm_write(clk->map, &tph->ws, val ? 0 : 1);149 150	return 0;151}152 153static int meson_sclk_ws_inv_get_phase(struct clk_hw *hw)154{155	struct clk_regmap *clk = to_clk_regmap(hw);156	struct meson_sclk_ws_inv_data *tph = meson_sclk_ws_inv_data(clk);157	unsigned int val;158 159	val = meson_parm_read(clk->map, &tph->ph);160 161	return meson_clk_degrees_from_val(val, tph->ph.width);162}163 164static int meson_sclk_ws_inv_set_phase(struct clk_hw *hw, int degrees)165{166	struct clk_regmap *clk = to_clk_regmap(hw);167	struct meson_sclk_ws_inv_data *tph = meson_sclk_ws_inv_data(clk);168	unsigned int val;169 170	val = meson_clk_degrees_to_val(degrees, tph->ph.width);171	meson_parm_write(clk->map, &tph->ph, val);172	meson_parm_write(clk->map, &tph->ws, val ? 0 : 1);173	return 0;174}175 176const struct clk_ops meson_sclk_ws_inv_ops = {177	.init		= meson_sclk_ws_inv_sync,178	.get_phase	= meson_sclk_ws_inv_get_phase,179	.set_phase	= meson_sclk_ws_inv_set_phase,180};181EXPORT_SYMBOL_NS_GPL(meson_sclk_ws_inv_ops, CLK_MESON);182 183MODULE_DESCRIPTION("Amlogic phase driver");184MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");185MODULE_LICENSE("GPL");186MODULE_IMPORT_NS(CLK_MESON);187