drivers/media/cec/platform/meson/ao-cec-g12a.c

Source file repositories/reference/linux-study-clean/drivers/media/cec/platform/meson/ao-cec-g12a.c

File Facts

System
Linux kernel
Corpus path
drivers/media/cec/platform/meson/ao-cec-g12a.c
Extension
.c
Size
21293 bytes
Lines
793
Domain
Driver Families
Bucket
drivers/media
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct meson_ao_cec_g12a_data {
	/* Setup the internal CECB_CTRL2 register */
	bool				ctrl2_setup;
};

struct meson_ao_cec_g12a_device {
	struct platform_device		*pdev;
	struct regmap			*regmap;
	struct regmap			*regmap_cec;
	spinlock_t			cec_reg_lock;
	struct cec_notifier		*notify;
	struct cec_adapter		*adap;
	struct cec_msg			rx_msg;
	struct clk			*oscin;
	struct clk			*core;
	const struct meson_ao_cec_g12a_data *data;
};

static const struct regmap_config meson_ao_cec_g12a_regmap_conf = {
	.reg_bits = 8,
	.val_bits = 32,
	.reg_stride = 4,
	.max_register = CECB_INTR_STAT_REG,
};

/*
 * The AO-CECB embeds a dual/divider to generate a more precise
 * 32,768KHz clock for CEC core clock.
 *                      ______   ______
 *                     |      | |      |
 *         ______      | Div1 |-| Cnt1 |       ______
 *        |      |    /|______| |______|\     |      |
 * Xtal-->| Gate |---|  ______   ______  X-X--| Gate |-->
 *        |______| |  \|      | |      |/  |  |______|
 *                 |   | Div2 |-| Cnt2 |   |
 *                 |   |______| |______|   |
 *                 |_______________________|
 *
 * The dividing can be switched to single or dual, with a counter
 * for each divider to set when the switching is done.
 * The entire dividing mechanism can be also bypassed.
 */

struct meson_ao_cec_g12a_dualdiv_clk {
	struct clk_hw hw;
	struct regmap *regmap;
};

#define hw_to_meson_ao_cec_g12a_dualdiv_clk(_hw)			\
	container_of(_hw, struct meson_ao_cec_g12a_dualdiv_clk, hw)	\

static unsigned long
meson_ao_cec_g12a_dualdiv_clk_recalc_rate(struct clk_hw *hw,
					  unsigned long parent_rate)
{
	struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk =
		hw_to_meson_ao_cec_g12a_dualdiv_clk(hw);
	unsigned long n1;
	u32 reg0, reg1;

	regmap_read(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0, &reg0);
	regmap_read(dualdiv_clk->regmap, CECB_CLK_CNTL_REG0, &reg1);

	if (reg1 & CECB_CLK_CNTL_BYPASS_EN)
		return parent_rate;

	if (reg0 & CECB_CLK_CNTL_DUAL_EN) {
		unsigned long n2, m1, m2, f1, f2, p1, p2;

		n1 = FIELD_GET(CECB_CLK_CNTL_N1, reg0) + 1;
		n2 = FIELD_GET(CECB_CLK_CNTL_N2, reg0) + 1;

		m1 = FIELD_GET(CECB_CLK_CNTL_M1, reg1) + 1;
		m2 = FIELD_GET(CECB_CLK_CNTL_M1, reg1) + 1;

		f1 = DIV_ROUND_CLOSEST(parent_rate, n1);
		f2 = DIV_ROUND_CLOSEST(parent_rate, n2);

		p1 = DIV_ROUND_CLOSEST(100000000 * m1, f1 * (m1 + m2));
		p2 = DIV_ROUND_CLOSEST(100000000 * m2, f2 * (m1 + m2));

		return DIV_ROUND_UP(100000000, p1 + p2);
	}

	n1 = FIELD_GET(CECB_CLK_CNTL_N1, reg0) + 1;

	return DIV_ROUND_CLOSEST(parent_rate, n1);
}

static int meson_ao_cec_g12a_dualdiv_clk_enable(struct clk_hw *hw)

Annotation

Implementation Notes