drivers/media/dvb-frontends/l64781.c

Source file repositories/reference/linux-study-clean/drivers/media/dvb-frontends/l64781.c

File Facts

System
Linux kernel
Corpus path
drivers/media/dvb-frontends/l64781.c
Extension
.c
Size
15110 bytes
Lines
597
Domain
Driver Families
Bucket
drivers/media
Inferred role
Driver Families: exported/initcall integration point
Status
integration 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 l64781_state {
	struct i2c_adapter* i2c;
	const struct l64781_config* config;
	struct dvb_frontend frontend;

	/* private demodulator data */
	unsigned int first:1;
};

#define dprintk(args...) \
	do { \
		if (debug) printk(KERN_DEBUG "l64781: " args); \
	} while (0)

static int debug;

module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");


static int l64781_writereg (struct l64781_state* state, u8 reg, u8 data)
{
	int ret;
	u8 buf [] = { reg, data };
	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };

	if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1)
		dprintk ("%s: write_reg error (reg == %02x) = %02x!\n",
			 __func__, reg, ret);

	return (ret != 1) ? -1 : 0;
}

static int l64781_readreg (struct l64781_state* state, u8 reg)
{
	int ret;
	u8 b0 [] = { reg };
	u8 b1 [] = { 0 };
	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
			   { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };

	ret = i2c_transfer(state->i2c, msg, 2);

	if (ret != 2) return ret;

	return b1[0];
}

static void apply_tps (struct l64781_state* state)
{
	l64781_writereg (state, 0x2a, 0x00);
	l64781_writereg (state, 0x2a, 0x01);

	/* This here is a little bit questionable because it enables
	   the automatic update of TPS registers. I think we'd need to
	   handle the IRQ from FE to update some other registers as
	   well, or at least implement some magic to tuning to correct
	   to the TPS received from transmission. */
	l64781_writereg (state, 0x2a, 0x02);
}


static void reset_afc (struct l64781_state* state)
{
	/* Set AFC stall for the AFC_INIT_FRQ setting, TIM_STALL for
	   timing offset */
	l64781_writereg (state, 0x07, 0x9e); /* stall AFC */
	l64781_writereg (state, 0x08, 0);    /* AFC INIT FREQ */
	l64781_writereg (state, 0x09, 0);
	l64781_writereg (state, 0x0a, 0);
	l64781_writereg (state, 0x07, 0x8e);
	l64781_writereg (state, 0x0e, 0);    /* AGC gain to zero in beginning */
	l64781_writereg (state, 0x11, 0x80); /* stall TIM */
	l64781_writereg (state, 0x10, 0);    /* TIM_OFFSET_LSB */
	l64781_writereg (state, 0x12, 0);
	l64781_writereg (state, 0x13, 0);
	l64781_writereg (state, 0x11, 0x00);
}

static int reset_and_configure (struct l64781_state* state)
{
	u8 buf [] = { 0x06 };
	struct i2c_msg msg = { .addr = 0x00, .flags = 0, .buf = buf, .len = 1 };
	// NOTE: this is correct in writing to address 0x00

	return (i2c_transfer(state->i2c, &msg, 1) == 1) ? 0 : -ENODEV;
}

static int apply_frontend_param(struct dvb_frontend *fe)
{

Annotation

Implementation Notes