You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							829 lines
						
					
					
						
							22 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							829 lines
						
					
					
						
							22 KiB
						
					
					
				| From e17d3b5c4e0de81f0dd3cc349d338617423b60e5 Mon Sep 17 00:00:00 2001 | |
| From: Antti Palosaari <crope@iki.fi> | |
| Date: Fri, 13 May 2016 12:19:19 -0300 | |
| Subject: [PATCH 5/5] mn88472: finalize driver | |
| 
 | |
| Finalize driver in order to move out of staging. | |
| 
 | |
| Signed-off-by: Antti Palosaari <crope@iki.fi> | |
| Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com> | |
| --- | |
|  drivers/media/dvb-frontends/mn88472.h        |   45 +-- | |
|  drivers/staging/media/mn88472/mn88472.c      |  519 ++++++++++++++------------ | |
|  drivers/staging/media/mn88472/mn88472_priv.h |   11 +- | |
|  3 files changed, 307 insertions(+), 268 deletions(-) | |
| 
 | |
| diff --git a/drivers/media/dvb-frontends/mn88472.h b/drivers/media/dvb-frontends/mn88472.h | |
| index 095294d..3236325 100644 | |
| --- a/drivers/media/dvb-frontends/mn88472.h | |
| +++ b/drivers/media/dvb-frontends/mn88472.h | |
| @@ -19,23 +19,33 @@ | |
|   | |
|  #include <linux/dvb/frontend.h> | |
|   | |
| -enum ts_clock { | |
| -	VARIABLE_TS_CLOCK, | |
| -	FIXED_TS_CLOCK, | |
| -}; | |
| +/** | |
| + * struct mn88472_config - Platform data for the mn88472 driver | |
| + * @xtal: Clock frequency. | |
| + * @ts_mode: TS mode. | |
| + * @ts_clock: TS clock config. | |
| + * @i2c_wr_max: Max number of bytes driver writes to I2C at once. | |
| + * @get_dvb_frontend: Get DVB frontend. | |
| + */ | |
|   | |
| -enum ts_mode { | |
| -	SERIAL_TS_MODE, | |
| -	PARALLEL_TS_MODE, | |
| -}; | |
| +/* Define old names for backward compatibility */ | |
| +#define VARIABLE_TS_CLOCK   MN88472_TS_CLK_VARIABLE | |
| +#define FIXED_TS_CLOCK      MN88472_TS_CLK_FIXED | |
| +#define SERIAL_TS_MODE      MN88472_TS_MODE_SERIAL | |
| +#define PARALLEL_TS_MODE    MN88472_TS_MODE_PARALLEL | |
|   | |
|  struct mn88472_config { | |
| -	/* | |
| -	 * Max num of bytes given I2C adapter could write at once. | |
| -	 * Default: none | |
| -	 */ | |
| -	u16 i2c_wr_max; | |
| +	unsigned int xtal; | |
| + | |
| +#define MN88472_TS_MODE_SERIAL      0 | |
| +#define MN88472_TS_MODE_PARALLEL    1 | |
| +	int ts_mode; | |
|   | |
| +#define MN88472_TS_CLK_FIXED        0 | |
| +#define MN88472_TS_CLK_VARIABLE     1 | |
| +	int ts_clock; | |
| + | |
| +	u16 i2c_wr_max; | |
|   | |
|  	/* Everything after that is returned by the driver. */ | |
|   | |
| @@ -43,14 +53,7 @@ struct mn88472_config { | |
|  	 * DVB frontend. | |
|  	 */ | |
|  	struct dvb_frontend **fe; | |
| - | |
| -	/* | |
| -	 * Xtal frequency. | |
| -	 * Hz | |
| -	 */ | |
| -	u32 xtal; | |
| -	int ts_mode; | |
| -	int ts_clock; | |
| +	struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); | |
|  }; | |
|   | |
|  #endif | |
| diff --git a/drivers/staging/media/mn88472/mn88472.c b/drivers/staging/media/mn88472/mn88472.c | |
| index 522a46e..b791d72 100644 | |
| --- a/drivers/staging/media/mn88472/mn88472.c | |
| +++ b/drivers/staging/media/mn88472/mn88472.c | |
| @@ -17,28 +17,90 @@ | |
|  #include "mn88472_priv.h" | |
|   | |
|  static int mn88472_get_tune_settings(struct dvb_frontend *fe, | |
| -	struct dvb_frontend_tune_settings *s) | |
| +				     struct dvb_frontend_tune_settings *s) | |
|  { | |
| -	s->min_delay_ms = 800; | |
| +	s->min_delay_ms = 1000; | |
|  	return 0; | |
|  } | |
|   | |
| +static int mn88472_read_status(struct dvb_frontend *fe, enum fe_status *status) | |
| +{ | |
| +	struct i2c_client *client = fe->demodulator_priv; | |
| +	struct mn88472_dev *dev = i2c_get_clientdata(client); | |
| +	struct dtv_frontend_properties *c = &fe->dtv_property_cache; | |
| +	int ret; | |
| +	unsigned int utmp; | |
| + | |
| +	if (!dev->active) { | |
| +		ret = -EAGAIN; | |
| +		goto err; | |
| +	} | |
| + | |
| +	switch (c->delivery_system) { | |
| +	case SYS_DVBT: | |
| +		ret = regmap_read(dev->regmap[0], 0x7f, &utmp); | |
| +		if (ret) | |
| +			goto err; | |
| +		if ((utmp & 0x0f) >= 0x09) | |
| +			*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | | |
| +				  FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; | |
| +		else | |
| +			*status = 0; | |
| +		break; | |
| +	case SYS_DVBT2: | |
| +		ret = regmap_read(dev->regmap[2], 0x92, &utmp); | |
| +		if (ret) | |
| +			goto err; | |
| +		if ((utmp & 0x0f) >= 0x0d) | |
| +			*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | | |
| +				  FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; | |
| +		else if ((utmp & 0x0f) >= 0x0a) | |
| +			*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | | |
| +				  FE_HAS_VITERBI; | |
| +		else if ((utmp & 0x0f) >= 0x07) | |
| +			*status = FE_HAS_SIGNAL | FE_HAS_CARRIER; | |
| +		else | |
| +			*status = 0; | |
| +		break; | |
| +	case SYS_DVBC_ANNEX_A: | |
| +		ret = regmap_read(dev->regmap[1], 0x84, &utmp); | |
| +		if (ret) | |
| +			goto err; | |
| +		if ((utmp & 0x0f) >= 0x08) | |
| +			*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | | |
| +				  FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; | |
| +		else | |
| +			*status = 0; | |
| +		break; | |
| +	default: | |
| +		ret = -EINVAL; | |
| +		goto err; | |
| +	} | |
| + | |
| +	return 0; | |
| +err: | |
| +	dev_dbg(&client->dev, "failed=%d\n", ret); | |
| +	return ret; | |
| +} | |
| + | |
|  static int mn88472_set_frontend(struct dvb_frontend *fe) | |
|  { | |
|  	struct i2c_client *client = fe->demodulator_priv; | |
|  	struct mn88472_dev *dev = i2c_get_clientdata(client); | |
|  	struct dtv_frontend_properties *c = &fe->dtv_property_cache; | |
|  	int ret, i; | |
| -	u32 if_frequency = 0; | |
| -	u64 tmp; | |
| -	u8 delivery_system_val, if_val[3], bw_val[7], bw_val2; | |
| +	unsigned int utmp; | |
| +	u32 if_frequency; | |
| +	u8 buf[3], delivery_system_val, bandwidth_val, *bandwidth_vals_ptr; | |
| +	u8 reg_bank0_b4_val, reg_bank0_cd_val, reg_bank0_d4_val; | |
| +	u8 reg_bank0_d6_val; | |
|   | |
|  	dev_dbg(&client->dev, | |
| -			"delivery_system=%d modulation=%d frequency=%d symbol_rate=%d inversion=%d\n", | |
| -			c->delivery_system, c->modulation, | |
| -			c->frequency, c->symbol_rate, c->inversion); | |
| +		"delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%d stream_id=%d\n", | |
| +		c->delivery_system, c->modulation, c->frequency, | |
| +		c->bandwidth_hz, c->symbol_rate, c->inversion, c->stream_id); | |
|   | |
| -	if (!dev->warm) { | |
| +	if (!dev->active) { | |
|  		ret = -EAGAIN; | |
|  		goto err; | |
|  	} | |
| @@ -46,39 +108,64 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) | |
|  	switch (c->delivery_system) { | |
|  	case SYS_DVBT: | |
|  		delivery_system_val = 0x02; | |
| +		reg_bank0_b4_val = 0x00; | |
| +		reg_bank0_cd_val = 0x1f; | |
| +		reg_bank0_d4_val = 0x0a; | |
| +		reg_bank0_d6_val = 0x48; | |
|  		break; | |
|  	case SYS_DVBT2: | |
|  		delivery_system_val = 0x03; | |
| +		reg_bank0_b4_val = 0xf6; | |
| +		reg_bank0_cd_val = 0x01; | |
| +		reg_bank0_d4_val = 0x09; | |
| +		reg_bank0_d6_val = 0x46; | |
|  		break; | |
|  	case SYS_DVBC_ANNEX_A: | |
|  		delivery_system_val = 0x04; | |
| +		reg_bank0_b4_val = 0x00; | |
| +		reg_bank0_cd_val = 0x17; | |
| +		reg_bank0_d4_val = 0x09; | |
| +		reg_bank0_d6_val = 0x48; | |
|  		break; | |
|  	default: | |
|  		ret = -EINVAL; | |
|  		goto err; | |
|  	} | |
|   | |
| -	if (c->bandwidth_hz <= 5000000) { | |
| -		memcpy(bw_val, "\xe5\x99\x9a\x1b\xa9\x1b\xa9", 7); | |
| -		bw_val2 = 0x03; | |
| -	} else if (c->bandwidth_hz <= 6000000) { | |
| -		/* IF 3570000 Hz, BW 6000000 Hz */ | |
| -		memcpy(bw_val, "\xbf\x55\x55\x15\x6b\x15\x6b", 7); | |
| -		bw_val2 = 0x02; | |
| -	} else if (c->bandwidth_hz <= 7000000) { | |
| -		/* IF 4570000 Hz, BW 7000000 Hz */ | |
| -		memcpy(bw_val, "\xa4\x00\x00\x0f\x2c\x0f\x2c", 7); | |
| -		bw_val2 = 0x01; | |
| -	} else if (c->bandwidth_hz <= 8000000) { | |
| -		/* IF 4570000 Hz, BW 8000000 Hz */ | |
| -		memcpy(bw_val, "\x8f\x80\x00\x08\xee\x08\xee", 7); | |
| -		bw_val2 = 0x00; | |
| -	} else { | |
| -		ret = -EINVAL; | |
| -		goto err; | |
| +	switch (c->delivery_system) { | |
| +	case SYS_DVBT: | |
| +	case SYS_DVBT2: | |
| +		switch (c->bandwidth_hz) { | |
| +		case 5000000: | |
| +			bandwidth_vals_ptr = "\xe5\x99\x9a\x1b\xa9\x1b\xa9"; | |
| +			bandwidth_val = 0x03; | |
| +			break; | |
| +		case 6000000: | |
| +			bandwidth_vals_ptr = "\xbf\x55\x55\x15\x6b\x15\x6b"; | |
| +			bandwidth_val = 0x02; | |
| +			break; | |
| +		case 7000000: | |
| +			bandwidth_vals_ptr = "\xa4\x00\x00\x0f\x2c\x0f\x2c"; | |
| +			bandwidth_val = 0x01; | |
| +			break; | |
| +		case 8000000: | |
| +			bandwidth_vals_ptr = "\x8f\x80\x00\x08\xee\x08\xee"; | |
| +			bandwidth_val = 0x00; | |
| +			break; | |
| +		default: | |
| +			ret = -EINVAL; | |
| +			goto err; | |
| +		} | |
| +		break; | |
| +	case SYS_DVBC_ANNEX_A: | |
| +		bandwidth_vals_ptr = NULL; | |
| +		bandwidth_val = 0x00; | |
| +		break; | |
| +	default: | |
| +		break; | |
|  	} | |
|   | |
| -	/* program tuner */ | |
| +	/* Program tuner */ | |
|  	if (fe->ops.tuner_ops.set_params) { | |
|  		ret = fe->ops.tuner_ops.set_params(fe); | |
|  		if (ret) | |
| @@ -91,20 +178,10 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) | |
|  			goto err; | |
|   | |
|  		dev_dbg(&client->dev, "get_if_frequency=%d\n", if_frequency); | |
| -	} | |
| - | |
| -	/* Calculate IF registers ( (1<<24)*IF / Xtal ) */ | |
| -	tmp =  div_u64(if_frequency * (u64)(1<<24) + (dev->xtal / 2), | |
| -				   dev->xtal); | |
| -	if_val[0] = (tmp >> 16) & 0xff; | |
| -	if_val[1] = (tmp >>  8) & 0xff; | |
| -	if_val[2] = (tmp >>  0) & 0xff; | |
| - | |
| -	ret = regmap_write(dev->regmap[2], 0xfb, 0x13); | |
| -	ret = regmap_write(dev->regmap[2], 0xef, 0x13); | |
| -	ret = regmap_write(dev->regmap[2], 0xf9, 0x13); | |
| -	if (ret) | |
| +	} else { | |
| +		ret = -EINVAL; | |
|  		goto err; | |
| +	} | |
|   | |
|  	ret = regmap_write(dev->regmap[2], 0x00, 0x66); | |
|  	if (ret) | |
| @@ -118,157 +195,81 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) | |
|  	ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val); | |
|  	if (ret) | |
|  		goto err; | |
| -	ret = regmap_write(dev->regmap[2], 0x04, bw_val2); | |
| +	ret = regmap_write(dev->regmap[2], 0x04, bandwidth_val); | |
|  	if (ret) | |
|  		goto err; | |
|   | |
| -	for (i = 0; i < sizeof(if_val); i++) { | |
| -		ret = regmap_write(dev->regmap[2], 0x10 + i, if_val[i]); | |
| +	/* IF */ | |
| +	utmp = DIV_ROUND_CLOSEST_ULL((u64)if_frequency * 0x1000000, dev->clk); | |
| +	buf[0] = (utmp >> 16) & 0xff; | |
| +	buf[1] = (utmp >>  8) & 0xff; | |
| +	buf[2] = (utmp >>  0) & 0xff; | |
| +	for (i = 0; i < 3; i++) { | |
| +		ret = regmap_write(dev->regmap[2], 0x10 + i, buf[i]); | |
|  		if (ret) | |
|  			goto err; | |
|  	} | |
|   | |
| -	for (i = 0; i < sizeof(bw_val); i++) { | |
| -		ret = regmap_write(dev->regmap[2], 0x13 + i, bw_val[i]); | |
| -		if (ret) | |
| -			goto err; | |
| +	/* Bandwidth */ | |
| +	if (bandwidth_vals_ptr) { | |
| +		for (i = 0; i < 7; i++) { | |
| +			ret = regmap_write(dev->regmap[2], 0x13 + i, | |
| +					   bandwidth_vals_ptr[i]); | |
| +			if (ret) | |
| +				goto err; | |
| +		} | |
|  	} | |
|   | |
| +	ret = regmap_write(dev->regmap[0], 0xb4, reg_bank0_b4_val); | |
| +	if (ret) | |
| +		goto err; | |
| +	ret = regmap_write(dev->regmap[0], 0xcd, reg_bank0_cd_val); | |
| +	if (ret) | |
| +		goto err; | |
| +	ret = regmap_write(dev->regmap[0], 0xd4, reg_bank0_d4_val); | |
| +	if (ret) | |
| +		goto err; | |
| +	ret = regmap_write(dev->regmap[0], 0xd6, reg_bank0_d6_val); | |
| +	if (ret) | |
| +		goto err; | |
| + | |
|  	switch (c->delivery_system) { | |
|  	case SYS_DVBT: | |
|  		ret = regmap_write(dev->regmap[0], 0x07, 0x26); | |
| -		ret = regmap_write(dev->regmap[0], 0xb0, 0x0a); | |
| -		ret = regmap_write(dev->regmap[0], 0xb4, 0x00); | |
| -		ret = regmap_write(dev->regmap[0], 0xcd, 0x1f); | |
| -		ret = regmap_write(dev->regmap[0], 0xd4, 0x0a); | |
| -		ret = regmap_write(dev->regmap[0], 0xd6, 0x48); | |
| +		if (ret) | |
| +			goto err; | |
|  		ret = regmap_write(dev->regmap[0], 0x00, 0xba); | |
| +		if (ret) | |
| +			goto err; | |
|  		ret = regmap_write(dev->regmap[0], 0x01, 0x13); | |
|  		if (ret) | |
|  			goto err; | |
|  		break; | |
|  	case SYS_DVBT2: | |
|  		ret = regmap_write(dev->regmap[2], 0x2b, 0x13); | |
| +		if (ret) | |
| +			goto err; | |
|  		ret = regmap_write(dev->regmap[2], 0x4f, 0x05); | |
| +		if (ret) | |
| +			goto err; | |
|  		ret = regmap_write(dev->regmap[1], 0xf6, 0x05); | |
| -		ret = regmap_write(dev->regmap[0], 0xb0, 0x0a); | |
| -		ret = regmap_write(dev->regmap[0], 0xb4, 0xf6); | |
| -		ret = regmap_write(dev->regmap[0], 0xcd, 0x01); | |
| -		ret = regmap_write(dev->regmap[0], 0xd4, 0x09); | |
| -		ret = regmap_write(dev->regmap[0], 0xd6, 0x46); | |
| -		ret = regmap_write(dev->regmap[2], 0x30, 0x80); | |
| -		ret = regmap_write(dev->regmap[2], 0x32, 0x00); | |
|  		if (ret) | |
|  			goto err; | |
| -		break; | |
| -	case SYS_DVBC_ANNEX_A: | |
| -		ret = regmap_write(dev->regmap[0], 0xb0, 0x0b); | |
| -		ret = regmap_write(dev->regmap[0], 0xb4, 0x00); | |
| -		ret = regmap_write(dev->regmap[0], 0xcd, 0x17); | |
| -		ret = regmap_write(dev->regmap[0], 0xd4, 0x09); | |
| -		ret = regmap_write(dev->regmap[0], 0xd6, 0x48); | |
| -		ret = regmap_write(dev->regmap[1], 0x00, 0xb0); | |
| +		ret = regmap_write(dev->regmap[2], 0x32, c->stream_id); | |
|  		if (ret) | |
|  			goto err; | |
|  		break; | |
| -	default: | |
| -		ret = -EINVAL; | |
| -		goto err; | |
| -	} | |
| - | |
| -	ret = regmap_write(dev->regmap[0], 0x46, 0x00); | |
| -	ret = regmap_write(dev->regmap[0], 0xae, 0x00); | |
| - | |
| -	switch (dev->ts_mode) { | |
| -	case SERIAL_TS_MODE: | |
| -		ret = regmap_write(dev->regmap[2], 0x08, 0x1d); | |
| -		break; | |
| -	case PARALLEL_TS_MODE: | |
| -		ret = regmap_write(dev->regmap[2], 0x08, 0x00); | |
| +	case SYS_DVBC_ANNEX_A: | |
|  		break; | |
|  	default: | |
| -		dev_dbg(&client->dev, "ts_mode error: %d\n", dev->ts_mode); | |
| -		ret = -EINVAL; | |
| -		goto err; | |
| -	} | |
| - | |
| -	switch (dev->ts_clock) { | |
| -	case VARIABLE_TS_CLOCK: | |
| -		ret = regmap_write(dev->regmap[0], 0xd9, 0xe3); | |
|  		break; | |
| -	case FIXED_TS_CLOCK: | |
| -		ret = regmap_write(dev->regmap[0], 0xd9, 0xe1); | |
| -		break; | |
| -	default: | |
| -		dev_dbg(&client->dev, "ts_clock error: %d\n", dev->ts_clock); | |
| -		ret = -EINVAL; | |
| -		goto err; | |
|  	} | |
|   | |
| -	/* Reset demod */ | |
| +	/* Reset FSM */ | |
|  	ret = regmap_write(dev->regmap[2], 0xf8, 0x9f); | |
|  	if (ret) | |
|  		goto err; | |
|   | |
| -	dev->delivery_system = c->delivery_system; | |
| - | |
| -	return 0; | |
| -err: | |
| -	dev_dbg(&client->dev, "failed=%d\n", ret); | |
| -	return ret; | |
| -} | |
| - | |
| -static int mn88472_read_status(struct dvb_frontend *fe, fe_status_t *status) | |
| -{ | |
| -	struct i2c_client *client = fe->demodulator_priv; | |
| -	struct mn88472_dev *dev = i2c_get_clientdata(client); | |
| -	struct dtv_frontend_properties *c = &fe->dtv_property_cache; | |
| -	int ret; | |
| -	unsigned int utmp; | |
| -	int lock = 0; | |
| - | |
| -	*status = 0; | |
| - | |
| -	if (!dev->warm) { | |
| -		ret = -EAGAIN; | |
| -		goto err; | |
| -	} | |
| - | |
| -	switch (c->delivery_system) { | |
| -	case SYS_DVBT: | |
| -		ret = regmap_read(dev->regmap[0], 0x7F, &utmp); | |
| -		if (ret) | |
| -			goto err; | |
| -		if ((utmp & 0xF) >= 0x09) | |
| -			lock = 1; | |
| -		break; | |
| -	case SYS_DVBT2: | |
| -		ret = regmap_read(dev->regmap[2], 0x92, &utmp); | |
| -		if (ret) | |
| -			goto err; | |
| -		if ((utmp & 0xF) >= 0x07) | |
| -			*status |= FE_HAS_SIGNAL; | |
| -		if ((utmp & 0xF) >= 0x0a) | |
| -			*status |= FE_HAS_CARRIER; | |
| -		if ((utmp & 0xF) >= 0x0d) | |
| -			*status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; | |
| -		break; | |
| -	case SYS_DVBC_ANNEX_A: | |
| -		ret = regmap_read(dev->regmap[1], 0x84, &utmp); | |
| -		if (ret) | |
| -			goto err; | |
| -		if ((utmp & 0xF) >= 0x08) | |
| -			lock = 1; | |
| -		break; | |
| -	default: | |
| -		ret = -EINVAL; | |
| -		goto err; | |
| -	} | |
| - | |
| -	if (lock) | |
| -		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | | |
| -				FE_HAS_SYNC | FE_HAS_LOCK; | |
| - | |
|  	return 0; | |
|  err: | |
|  	dev_dbg(&client->dev, "failed=%d\n", ret); | |
| @@ -279,93 +280,107 @@ static int mn88472_init(struct dvb_frontend *fe) | |
|  { | |
|  	struct i2c_client *client = fe->demodulator_priv; | |
|  	struct mn88472_dev *dev = i2c_get_clientdata(client); | |
| -	int ret, len, remaining; | |
| -	const struct firmware *fw = NULL; | |
| -	u8 *fw_file = MN88472_FIRMWARE; | |
| -	unsigned int tmp; | |
| +	int ret, len, rem; | |
| +	unsigned int utmp; | |
| +	const struct firmware *firmware; | |
| +	const char *name = MN88472_FIRMWARE; | |
|   | |
|  	dev_dbg(&client->dev, "\n"); | |
|   | |
| -	/* set cold state by default */ | |
| -	dev->warm = false; | |
| - | |
| -	/* power on */ | |
| +	/* Power up */ | |
|  	ret = regmap_write(dev->regmap[2], 0x05, 0x00); | |
|  	if (ret) | |
|  		goto err; | |
| - | |
| -	ret = regmap_bulk_write(dev->regmap[2], 0x0b, "\x00\x00", 2); | |
| +	ret = regmap_write(dev->regmap[2], 0x0b, 0x00); | |
|  	if (ret) | |
|  		goto err; | |
| - | |
| -	/* check if firmware is already running */ | |
| -	ret = regmap_read(dev->regmap[0], 0xf5, &tmp); | |
| +	ret = regmap_write(dev->regmap[2], 0x0c, 0x00); | |
|  	if (ret) | |
|  		goto err; | |
|   | |
| -	if (!(tmp & 0x1)) { | |
| -		dev_info(&client->dev, "firmware already running\n"); | |
| -		dev->warm = true; | |
| -		return 0; | |
| -	} | |
| +	/* Check if firmware is already running */ | |
| +	ret = regmap_read(dev->regmap[0], 0xf5, &utmp); | |
| +	if (ret) | |
| +		goto err; | |
| +	if (!(utmp & 0x01)) | |
| +		goto warm; | |
|   | |
| -	/* request the firmware, this will block and timeout */ | |
| -	ret = request_firmware(&fw, fw_file, &client->dev); | |
| +	ret = request_firmware(&firmware, name, &client->dev); | |
|  	if (ret) { | |
| -		dev_err(&client->dev, "firmware file '%s' not found\n", | |
| -				fw_file); | |
| +		dev_err(&client->dev, "firmware file '%s' not found\n", name); | |
|  		goto err; | |
|  	} | |
|   | |
| -	dev_info(&client->dev, "downloading firmware from file '%s'\n", | |
| -			fw_file); | |
| +	dev_info(&client->dev, "downloading firmware from file '%s'\n", name); | |
|   | |
|  	ret = regmap_write(dev->regmap[0], 0xf5, 0x03); | |
|  	if (ret) | |
| -		goto firmware_release; | |
| - | |
| -	for (remaining = fw->size; remaining > 0; | |
| -			remaining -= (dev->i2c_wr_max - 1)) { | |
| -		len = remaining; | |
| -		if (len > (dev->i2c_wr_max - 1)) | |
| -			len = dev->i2c_wr_max - 1; | |
| +		goto err_release_firmware; | |
|   | |
| +	for (rem = firmware->size; rem > 0; rem -= (dev->i2c_write_max - 1)) { | |
| +		len = min(dev->i2c_write_max - 1, rem); | |
|  		ret = regmap_bulk_write(dev->regmap[0], 0xf6, | |
| -				&fw->data[fw->size - remaining], len); | |
| +					&firmware->data[firmware->size - rem], | |
| +					len); | |
|  		if (ret) { | |
| -			dev_err(&client->dev, | |
| -					"firmware download failed=%d\n", ret); | |
| -			goto firmware_release; | |
| +			dev_err(&client->dev, "firmware download failed %d\n", | |
| +				ret); | |
| +			goto err_release_firmware; | |
|  		} | |
|  	} | |
|   | |
| -	/* parity check of firmware */ | |
| -	ret = regmap_read(dev->regmap[0], 0xf8, &tmp); | |
| -	if (ret) { | |
| -		dev_err(&client->dev, | |
| -				"parity reg read failed=%d\n", ret); | |
| -		goto firmware_release; | |
| -	} | |
| -	if (tmp & 0x10) { | |
| -		dev_err(&client->dev, | |
| -				"firmware parity check failed=0x%x\n", tmp); | |
| -		goto firmware_release; | |
| +	/* Parity check of firmware */ | |
| +	ret = regmap_read(dev->regmap[0], 0xf8, &utmp); | |
| +	if (ret) | |
| +		goto err_release_firmware; | |
| +	if (utmp & 0x10) { | |
| +		ret = -EINVAL; | |
| +		dev_err(&client->dev, "firmware did not run\n"); | |
| +		goto err_release_firmware; | |
|  	} | |
| -	dev_err(&client->dev, "firmware parity check succeeded=0x%x\n", tmp); | |
|   | |
|  	ret = regmap_write(dev->regmap[0], 0xf5, 0x00); | |
|  	if (ret) | |
| -		goto firmware_release; | |
| +		goto err_release_firmware; | |
|   | |
| -	release_firmware(fw); | |
| -	fw = NULL; | |
| +	release_firmware(firmware); | |
| +warm: | |
| +	/* TS config */ | |
| +	switch (dev->ts_mode) { | |
| +	case SERIAL_TS_MODE: | |
| +		utmp = 0x1d; | |
| +		break; | |
| +	case PARALLEL_TS_MODE: | |
| +		utmp = 0x00; | |
| +		break; | |
| +	default: | |
| +		ret = -EINVAL; | |
| +		goto err; | |
| +	} | |
| +	ret = regmap_write(dev->regmap[2], 0x08, utmp); | |
| +	if (ret) | |
| +		goto err; | |
|   | |
| -	/* warm state */ | |
| -	dev->warm = true; | |
| +	switch (dev->ts_clk) { | |
| +	case VARIABLE_TS_CLOCK: | |
| +		utmp = 0xe3; | |
| +		break; | |
| +	case FIXED_TS_CLOCK: | |
| +		utmp = 0xe1; | |
| +		break; | |
| +	default: | |
| +		ret = -EINVAL; | |
| +		goto err; | |
| +	} | |
| +	ret = regmap_write(dev->regmap[0], 0xd9, utmp); | |
| +	if (ret) | |
| +		goto err; | |
| + | |
| +	dev->active = true; | |
|   | |
|  	return 0; | |
| -firmware_release: | |
| -	release_firmware(fw); | |
| +err_release_firmware: | |
| +	release_firmware(firmware); | |
|  err: | |
|  	dev_dbg(&client->dev, "failed=%d\n", ret); | |
|  	return ret; | |
| @@ -379,18 +394,17 @@ static int mn88472_sleep(struct dvb_frontend *fe) | |
|   | |
|  	dev_dbg(&client->dev, "\n"); | |
|   | |
| -	/* power off */ | |
| +	/* Power down */ | |
| +	ret = regmap_write(dev->regmap[2], 0x0c, 0x30); | |
| +	if (ret) | |
| +		goto err; | |
|  	ret = regmap_write(dev->regmap[2], 0x0b, 0x30); | |
| - | |
|  	if (ret) | |
|  		goto err; | |
| - | |
|  	ret = regmap_write(dev->regmap[2], 0x05, 0x3e); | |
|  	if (ret) | |
|  		goto err; | |
|   | |
| -	dev->delivery_system = SYS_UNDEFINED; | |
| - | |
|  	return 0; | |
|  err: | |
|  	dev_dbg(&client->dev, "failed=%d\n", ret); | |
| @@ -434,10 +448,19 @@ static struct dvb_frontend_ops mn88472_ops = { | |
|  	.read_status = mn88472_read_status, | |
|  }; | |
|   | |
| +static struct dvb_frontend *mn88472_get_dvb_frontend(struct i2c_client *client) | |
| +{ | |
| +	struct mn88472_dev *dev = i2c_get_clientdata(client); | |
| + | |
| +	dev_dbg(&client->dev, "\n"); | |
| + | |
| +	return &dev->fe; | |
| +} | |
| + | |
|  static int mn88472_probe(struct i2c_client *client, | |
| -		const struct i2c_device_id *id) | |
| +			 const struct i2c_device_id *id) | |
|  { | |
| -	struct mn88472_config *config = client->dev.platform_data; | |
| +	struct mn88472_config *pdata = client->dev.platform_data; | |
|  	struct mn88472_dev *dev; | |
|  	int ret; | |
|  	unsigned int utmp; | |
| @@ -448,23 +471,16 @@ static int mn88472_probe(struct i2c_client *client, | |
|   | |
|  	dev_dbg(&client->dev, "\n"); | |
|   | |
| -	/* Caller really need to provide pointer for frontend we create. */ | |
| -	if (config->fe == NULL) { | |
| -		dev_err(&client->dev, "frontend pointer not defined\n"); | |
| -		ret = -EINVAL; | |
| -		goto err; | |
| -	} | |
| - | |
|  	dev = kzalloc(sizeof(*dev), GFP_KERNEL); | |
|  	if (!dev) { | |
|  		ret = -ENOMEM; | |
|  		goto err; | |
|  	} | |
|   | |
| -	dev->i2c_wr_max = config->i2c_wr_max; | |
| -	dev->xtal = config->xtal; | |
| -	dev->ts_mode = config->ts_mode; | |
| -	dev->ts_clock = config->ts_clock; | |
| +	dev->i2c_write_max = pdata->i2c_wr_max ? pdata->i2c_wr_max : ~0; | |
| +	dev->clk = pdata->xtal; | |
| +	dev->ts_mode = pdata->ts_mode; | |
| +	dev->ts_clk = pdata->ts_clock; | |
|  	dev->client[0] = client; | |
|  	dev->regmap[0] = regmap_init_i2c(dev->client[0], ®map_config); | |
|  	if (IS_ERR(dev->regmap[0])) { | |
| @@ -472,15 +488,25 @@ static int mn88472_probe(struct i2c_client *client, | |
|  		goto err_kfree; | |
|  	} | |
|   | |
| -	/* check demod answers to I2C */ | |
| -	ret = regmap_read(dev->regmap[0], 0x00, &utmp); | |
| +	/* Check demod answers with correct chip id */ | |
| +	ret = regmap_read(dev->regmap[0], 0xff, &utmp); | |
|  	if (ret) | |
|  		goto err_regmap_0_regmap_exit; | |
|   | |
| +	dev_dbg(&client->dev, "chip id=%02x\n", utmp); | |
| + | |
| +	if (utmp != 0x02) { | |
| +		ret = -ENODEV; | |
| +		goto err_regmap_0_regmap_exit; | |
| +	} | |
| + | |
|  	/* | |
| -	 * Chip has three I2C addresses for different register pages. Used | |
| +	 * Chip has three I2C addresses for different register banks. Used | |
|  	 * addresses are 0x18, 0x1a and 0x1c. We register two dummy clients, | |
| -	 * 0x1a and 0x1c, in order to get own I2C client for each register page. | |
| +	 * 0x1a and 0x1c, in order to get own I2C client for each register bank. | |
| +	 * | |
| +	 * Also, register bank 2 do not support sequential I/O. Only single | |
| +	 * register write or read is allowed to that bank. | |
|  	 */ | |
|  	dev->client[1] = i2c_new_dummy(client->adapter, 0x1a); | |
|  	if (!dev->client[1]) { | |
| @@ -510,15 +536,25 @@ static int mn88472_probe(struct i2c_client *client, | |
|  	} | |
|  	i2c_set_clientdata(dev->client[2], dev); | |
|   | |
| -	/* create dvb_frontend */ | |
| +	/* Sleep because chip is active by default */ | |
| +	ret = regmap_write(dev->regmap[2], 0x05, 0x3e); | |
| +	if (ret) | |
| +		goto err_regmap_2_regmap_exit; | |
| + | |
| +	/* Create dvb frontend */ | |
|  	memcpy(&dev->fe.ops, &mn88472_ops, sizeof(struct dvb_frontend_ops)); | |
|  	dev->fe.demodulator_priv = client; | |
| -	*config->fe = &dev->fe; | |
| +	*pdata->fe = &dev->fe; | |
|  	i2c_set_clientdata(client, dev); | |
|   | |
| -	dev_info(&client->dev, "Panasonic MN88472 successfully attached\n"); | |
| -	return 0; | |
| +	/* Setup callbacks */ | |
| +	pdata->get_dvb_frontend = mn88472_get_dvb_frontend; | |
|   | |
| +	dev_info(&client->dev, "Panasonic MN88472 successfully identified\n"); | |
| + | |
| +	return 0; | |
| +err_regmap_2_regmap_exit: | |
| +	regmap_exit(dev->regmap[2]); | |
|  err_client_2_i2c_unregister_device: | |
|  	i2c_unregister_device(dev->client[2]); | |
|  err_regmap_1_regmap_exit: | |
| @@ -562,11 +598,12 @@ MODULE_DEVICE_TABLE(i2c, mn88472_id_table); | |
|  static struct i2c_driver mn88472_driver = { | |
|  	.driver = { | |
|  		.owner	= THIS_MODULE, | |
| -		.name	= "mn88472", | |
| +		.name = "mn88472", | |
| +		.suppress_bind_attrs = true, | |
|  	}, | |
| -	.probe		= mn88472_probe, | |
| -	.remove		= mn88472_remove, | |
| -	.id_table	= mn88472_id_table, | |
| +	.probe    = mn88472_probe, | |
| +	.remove   = mn88472_remove, | |
| +	.id_table = mn88472_id_table, | |
|  }; | |
|   | |
|  module_i2c_driver(mn88472_driver); | |
| diff --git a/drivers/staging/media/mn88472/mn88472_priv.h b/drivers/staging/media/mn88472/mn88472_priv.h | |
| index 9ba8c8b..cdf2597 100644 | |
| --- a/drivers/staging/media/mn88472/mn88472_priv.h | |
| +++ b/drivers/staging/media/mn88472/mn88472_priv.h | |
| @@ -28,12 +28,11 @@ struct mn88472_dev { | |
|  	struct i2c_client *client[3]; | |
|  	struct regmap *regmap[3]; | |
|  	struct dvb_frontend fe; | |
| -	u16 i2c_wr_max; | |
| -	fe_delivery_system_t delivery_system; | |
| -	bool warm; /* FW running */ | |
| -	u32 xtal; | |
| -	int ts_mode; | |
| -	int ts_clock; | |
| +	u16 i2c_write_max; | |
| +	unsigned int clk; | |
| +	unsigned int active:1; | |
| +	unsigned int ts_mode:1; | |
| +	unsigned int ts_clk:1; | |
|  }; | |
|   | |
|  #endif | |
| --  | |
| 1.7.10.4 | |
| 
 | |
| 
 |