forked from mirrors/linux
		
	[media] mn88473: finalize driver
Finalize the driver. It still lacks a lot of features, like all statistics and PLP filtering, but basic functionality and sensitivity is pretty good shape. Signed-off-by: Antti Palosaari <crope@iki.fi> Reviewed-by: Benjamin Larsson <benjamin@southpole.se> Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
This commit is contained in:
		
							parent
							
								
									877ba50b05
								
							
						
					
					
						commit
						7908fad99a
					
				
					 3 changed files with 246 additions and 163 deletions
				
			
		|  | @ -29,21 +29,17 @@ static int mn88473_set_frontend(struct dvb_frontend *fe) | ||||||
| 	struct mn88473_dev *dev = i2c_get_clientdata(client); | 	struct mn88473_dev *dev = i2c_get_clientdata(client); | ||||||
| 	struct dtv_frontend_properties *c = &fe->dtv_property_cache; | 	struct dtv_frontend_properties *c = &fe->dtv_property_cache; | ||||||
| 	int ret, i; | 	int ret, i; | ||||||
|  | 	unsigned int uitmp; | ||||||
| 	u32 if_frequency; | 	u32 if_frequency; | ||||||
| 	u64 tmp; | 	u8 delivery_system_val, if_val[3], *conf_val_ptr; | ||||||
| 	u8 delivery_system_val, if_val[3], bw_val[7]; | 	u8 reg_bank2_2d_val, reg_bank0_d2_val; | ||||||
| 
 | 
 | ||||||
| 	dev_dbg(&client->dev, | 	dev_dbg(&client->dev, | ||||||
| 		"delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%d stream_id=%d\n", | 		"delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%d stream_id=%d\n", | ||||||
| 		c->delivery_system, | 		c->delivery_system, c->modulation, c->frequency, | ||||||
| 		c->modulation, | 		c->bandwidth_hz, c->symbol_rate, c->inversion, c->stream_id); | ||||||
| 		c->frequency, |  | ||||||
| 		c->bandwidth_hz, |  | ||||||
| 		c->symbol_rate, |  | ||||||
| 		c->inversion, |  | ||||||
| 		c->stream_id); |  | ||||||
| 
 | 
 | ||||||
| 	if (!dev->warm) { | 	if (!dev->active) { | ||||||
| 		ret = -EAGAIN; | 		ret = -EAGAIN; | ||||||
| 		goto err; | 		goto err; | ||||||
| 	} | 	} | ||||||
|  | @ -51,30 +47,50 @@ static int mn88473_set_frontend(struct dvb_frontend *fe) | ||||||
| 	switch (c->delivery_system) { | 	switch (c->delivery_system) { | ||||||
| 	case SYS_DVBT: | 	case SYS_DVBT: | ||||||
| 		delivery_system_val = 0x02; | 		delivery_system_val = 0x02; | ||||||
|  | 		reg_bank2_2d_val = 0x23; | ||||||
|  | 		reg_bank0_d2_val = 0x2a; | ||||||
| 		break; | 		break; | ||||||
| 	case SYS_DVBT2: | 	case SYS_DVBT2: | ||||||
| 		delivery_system_val = 0x03; | 		delivery_system_val = 0x03; | ||||||
|  | 		reg_bank2_2d_val = 0x3b; | ||||||
|  | 		reg_bank0_d2_val = 0x29; | ||||||
| 		break; | 		break; | ||||||
| 	case SYS_DVBC_ANNEX_A: | 	case SYS_DVBC_ANNEX_A: | ||||||
| 		delivery_system_val = 0x04; | 		delivery_system_val = 0x04; | ||||||
|  | 		reg_bank2_2d_val = 0x3b; | ||||||
|  | 		reg_bank0_d2_val = 0x29; | ||||||
| 		break; | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		ret = -EINVAL; | 		ret = -EINVAL; | ||||||
| 		goto err; | 		goto err; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (c->bandwidth_hz <= 6000000) { | 	switch (c->delivery_system) { | ||||||
| 		memcpy(bw_val, "\xe9\x55\x55\x1c\x29\x1c\x29", 7); | 	case SYS_DVBT: | ||||||
| 	} else if (c->bandwidth_hz <= 7000000) { | 	case SYS_DVBT2: | ||||||
| 		memcpy(bw_val, "\xc8\x00\x00\x17\x0a\x17\x0a", 7); | 		switch (c->bandwidth_hz) { | ||||||
| 	} else if (c->bandwidth_hz <= 8000000) { | 		case 6000000: | ||||||
| 		memcpy(bw_val, "\xaf\x00\x00\x11\xec\x11\xec", 7); | 			conf_val_ptr = "\xe9\x55\x55\x1c\x29\x1c\x29"; | ||||||
| 	} else { | 			break; | ||||||
| 		ret = -EINVAL; | 		case 7000000: | ||||||
| 		goto err; | 			conf_val_ptr = "\xc8\x00\x00\x17\x0a\x17\x0a"; | ||||||
|  | 			break; | ||||||
|  | 		case 8000000: | ||||||
|  | 			conf_val_ptr = "\xaf\x00\x00\x11\xec\x11\xec"; | ||||||
|  | 			break; | ||||||
|  | 		default: | ||||||
|  | 			ret = -EINVAL; | ||||||
|  | 			goto err; | ||||||
|  | 		} | ||||||
|  | 		break; | ||||||
|  | 	case SYS_DVBC_ANNEX_A: | ||||||
|  | 		conf_val_ptr = "\x10\xab\x0d\xae\x1d\x9d"; | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		break; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* program tuner */ | 	/* Program tuner */ | ||||||
| 	if (fe->ops.tuner_ops.set_params) { | 	if (fe->ops.tuner_ops.set_params) { | ||||||
| 		ret = fe->ops.tuner_ops.set_params(fe); | 		ret = fe->ops.tuner_ops.set_params(fe); | ||||||
| 		if (ret) | 		if (ret) | ||||||
|  | @ -86,27 +102,45 @@ static int mn88473_set_frontend(struct dvb_frontend *fe) | ||||||
| 		if (ret) | 		if (ret) | ||||||
| 			goto err; | 			goto err; | ||||||
| 
 | 
 | ||||||
| 		dev_dbg(&client->dev, "get_if_frequency=%d\n", if_frequency); | 		dev_dbg(&client->dev, "get_if_frequency=%u\n", if_frequency); | ||||||
| 	} else { | 	} else { | ||||||
| 		if_frequency = 0; | 		ret = -EINVAL; | ||||||
|  | 		goto err; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* Calculate IF registers ( (1<<24)*IF / Xtal ) */ | 	/* Calculate IF registers */ | ||||||
| 	tmp =  div_u64(if_frequency * (u64)(1<<24) + (dev->xtal / 2), | 	uitmp = DIV_ROUND_CLOSEST_ULL((u64) if_frequency * 0x1000000, dev->clk); | ||||||
| 				   dev->xtal); | 	if_val[0] = (uitmp >> 16) & 0xff; | ||||||
| 	if_val[0] = ((tmp >> 16) & 0xff); | 	if_val[1] = (uitmp >>  8) & 0xff; | ||||||
| 	if_val[1] = ((tmp >>  8) & 0xff); | 	if_val[2] = (uitmp >>  0) & 0xff; | ||||||
| 	if_val[2] = ((tmp >>  0) & 0xff); |  | ||||||
| 
 | 
 | ||||||
| 	ret = regmap_write(dev->regmap[2], 0x05, 0x00); | 	ret = regmap_write(dev->regmap[2], 0x05, 0x00); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[2], 0xfb, 0x13); | 	ret = regmap_write(dev->regmap[2], 0xfb, 0x13); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[2], 0xef, 0x13); | 	ret = regmap_write(dev->regmap[2], 0xef, 0x13); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[2], 0xf9, 0x13); | 	ret = regmap_write(dev->regmap[2], 0xf9, 0x13); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[2], 0x00, 0x18); | 	ret = regmap_write(dev->regmap[2], 0x00, 0x18); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[2], 0x01, 0x01); | 	ret = regmap_write(dev->regmap[2], 0x01, 0x01); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[2], 0x02, 0x21); | 	ret = regmap_write(dev->regmap[2], 0x02, 0x21); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val); | 	ret = regmap_write(dev->regmap[2], 0x03, delivery_system_val); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[2], 0x0b, 0x00); | 	ret = regmap_write(dev->regmap[2], 0x0b, 0x00); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 
 | 
 | ||||||
| 	for (i = 0; i < sizeof(if_val); i++) { | 	for (i = 0; i < sizeof(if_val); i++) { | ||||||
| 		ret = regmap_write(dev->regmap[2], 0x10 + i, if_val[i]); | 		ret = regmap_write(dev->regmap[2], 0x10 + i, if_val[i]); | ||||||
|  | @ -114,52 +148,85 @@ static int mn88473_set_frontend(struct dvb_frontend *fe) | ||||||
| 			goto err; | 			goto err; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for (i = 0; i < sizeof(bw_val); i++) { | 	switch (c->delivery_system) { | ||||||
| 		ret = regmap_write(dev->regmap[2], 0x13 + i, bw_val[i]); | 	case SYS_DVBT: | ||||||
|  | 	case SYS_DVBT2: | ||||||
|  | 		for (i = 0; i < 7; i++) { | ||||||
|  | 			ret = regmap_write(dev->regmap[2], 0x13 + i, | ||||||
|  | 					   conf_val_ptr[i]); | ||||||
|  | 			if (ret) | ||||||
|  | 				goto err; | ||||||
|  | 		} | ||||||
|  | 		break; | ||||||
|  | 	case SYS_DVBC_ANNEX_A: | ||||||
|  | 		ret = regmap_bulk_write(dev->regmap[1], 0x10, conf_val_ptr, 6); | ||||||
| 		if (ret) | 		if (ret) | ||||||
| 			goto err; | 			goto err; | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		break; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	ret = regmap_write(dev->regmap[2], 0x2d, 0x3b); | 	ret = regmap_write(dev->regmap[2], 0x2d, reg_bank2_2d_val); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[2], 0x2e, 0x00); | 	ret = regmap_write(dev->regmap[2], 0x2e, 0x00); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[2], 0x56, 0x0d); | 	ret = regmap_write(dev->regmap[2], 0x56, 0x0d); | ||||||
| 	ret = regmap_write(dev->regmap[0], 0x01, 0xba); | 	if (ret) | ||||||
| 	ret = regmap_write(dev->regmap[0], 0x02, 0x13); | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[0], 0x03, 0x80); | 	ret = regmap_bulk_write(dev->regmap[0], 0x01, | ||||||
| 	ret = regmap_write(dev->regmap[0], 0x04, 0xba); | 				"\xba\x13\x80\xba\x91\xdd\xe7\x28", 8); | ||||||
| 	ret = regmap_write(dev->regmap[0], 0x05, 0x91); | 	if (ret) | ||||||
| 	ret = regmap_write(dev->regmap[0], 0x07, 0xe7); | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[0], 0x08, 0x28); |  | ||||||
| 	ret = regmap_write(dev->regmap[0], 0x0a, 0x1a); | 	ret = regmap_write(dev->regmap[0], 0x0a, 0x1a); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[0], 0x13, 0x1f); | 	ret = regmap_write(dev->regmap[0], 0x13, 0x1f); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[0], 0x19, 0x03); | 	ret = regmap_write(dev->regmap[0], 0x19, 0x03); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[0], 0x1d, 0xb0); | 	ret = regmap_write(dev->regmap[0], 0x1d, 0xb0); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[0], 0x2a, 0x72); | 	ret = regmap_write(dev->regmap[0], 0x2a, 0x72); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[0], 0x2d, 0x00); | 	ret = regmap_write(dev->regmap[0], 0x2d, 0x00); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[0], 0x3c, 0x00); | 	ret = regmap_write(dev->regmap[0], 0x3c, 0x00); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[0], 0x3f, 0xf8); | 	ret = regmap_write(dev->regmap[0], 0x3f, 0xf8); | ||||||
| 	ret = regmap_write(dev->regmap[0], 0x40, 0xf4); | 	if (ret) | ||||||
| 	ret = regmap_write(dev->regmap[0], 0x41, 0x08); | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[0], 0xd2, 0x29); | 	ret = regmap_bulk_write(dev->regmap[0], 0x40, "\xf4\x08", 2); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
|  | 	ret = regmap_write(dev->regmap[0], 0xd2, reg_bank0_d2_val); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[0], 0xd4, 0x55); | 	ret = regmap_write(dev->regmap[0], 0xd4, 0x55); | ||||||
| 	ret = regmap_write(dev->regmap[1], 0x10, 0x10); | 	if (ret) | ||||||
| 	ret = regmap_write(dev->regmap[1], 0x11, 0xab); | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[1], 0x12, 0x0d); |  | ||||||
| 	ret = regmap_write(dev->regmap[1], 0x13, 0xae); |  | ||||||
| 	ret = regmap_write(dev->regmap[1], 0x14, 0x1d); |  | ||||||
| 	ret = regmap_write(dev->regmap[1], 0x15, 0x9d); |  | ||||||
| 	ret = regmap_write(dev->regmap[1], 0xbe, 0x08); | 	ret = regmap_write(dev->regmap[1], 0xbe, 0x08); | ||||||
| 	ret = regmap_write(dev->regmap[2], 0x09, 0x08); | 	if (ret) | ||||||
| 	ret = regmap_write(dev->regmap[2], 0x08, 0x1d); | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[0], 0xb2, 0x37); | 	ret = regmap_write(dev->regmap[0], 0xb2, 0x37); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 	ret = regmap_write(dev->regmap[0], 0xd7, 0x04); | 	ret = regmap_write(dev->regmap[0], 0xd7, 0x04); | ||||||
| 	ret = regmap_write(dev->regmap[2], 0x32, 0x80); |  | ||||||
| 	ret = regmap_write(dev->regmap[2], 0x36, 0x00); |  | ||||||
| 	ret = regmap_write(dev->regmap[2], 0xf8, 0x9f); |  | ||||||
| 	if (ret) | 	if (ret) | ||||||
| 		goto err; | 		goto err; | ||||||
| 
 | 
 | ||||||
| 	dev->delivery_system = c->delivery_system; | 	/* Reset FSM */ | ||||||
|  | 	ret = regmap_write(dev->regmap[2], 0xf8, 0x9f); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| err: | err: | ||||||
|  | @ -173,51 +240,61 @@ static int mn88473_read_status(struct dvb_frontend *fe, enum fe_status *status) | ||||||
| 	struct mn88473_dev *dev = i2c_get_clientdata(client); | 	struct mn88473_dev *dev = i2c_get_clientdata(client); | ||||||
| 	struct dtv_frontend_properties *c = &fe->dtv_property_cache; | 	struct dtv_frontend_properties *c = &fe->dtv_property_cache; | ||||||
| 	int ret; | 	int ret; | ||||||
| 	unsigned int utmp; | 	unsigned int uitmp; | ||||||
| 	int lock = 0; |  | ||||||
| 
 | 
 | ||||||
| 	*status = 0; | 	if (!dev->active) { | ||||||
| 
 |  | ||||||
| 	if (!dev->warm) { |  | ||||||
| 		ret = -EAGAIN; | 		ret = -EAGAIN; | ||||||
| 		goto err; | 		goto err; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	*status = 0; | ||||||
|  | 
 | ||||||
| 	switch (c->delivery_system) { | 	switch (c->delivery_system) { | ||||||
| 	case SYS_DVBT: | 	case SYS_DVBT: | ||||||
| 		ret = regmap_read(dev->regmap[0], 0x62, &utmp); | 		ret = regmap_read(dev->regmap[0], 0x62, &uitmp); | ||||||
| 		if (ret) | 		if (ret) | ||||||
| 			goto err; | 			goto err; | ||||||
| 		if (!(utmp & 0xA0)) { | 
 | ||||||
| 			if ((utmp & 0xF) >= 0x03) | 		if (!(uitmp & 0xa0)) { | ||||||
| 				*status |= FE_HAS_SIGNAL; | 			if ((uitmp & 0x0f) >= 0x09) | ||||||
| 			if ((utmp & 0xF) >= 0x09) | 				*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | | ||||||
| 				lock = 1; | 					  FE_HAS_VITERBI | FE_HAS_SYNC | | ||||||
|  | 					  FE_HAS_LOCK; | ||||||
|  | 			else if ((uitmp & 0x0f) >= 0x03) | ||||||
|  | 				*status = FE_HAS_SIGNAL | FE_HAS_CARRIER; | ||||||
| 		} | 		} | ||||||
| 		break; | 		break; | ||||||
| 	case SYS_DVBT2: | 	case SYS_DVBT2: | ||||||
| 		ret = regmap_read(dev->regmap[2], 0x8B, &utmp); | 		ret = regmap_read(dev->regmap[2], 0x8b, &uitmp); | ||||||
| 		if (ret) | 		if (ret) | ||||||
| 			goto err; | 			goto err; | ||||||
| 		if (!(utmp & 0x40)) { | 
 | ||||||
| 			if ((utmp & 0xF) >= 0x07) | 		if (!(uitmp & 0x40)) { | ||||||
| 				*status |= FE_HAS_SIGNAL; | 			if ((uitmp & 0x0f) >= 0x0d) | ||||||
| 			if ((utmp & 0xF) >= 0x0a) | 				*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | | ||||||
| 				*status |= FE_HAS_CARRIER; | 					  FE_HAS_VITERBI | FE_HAS_SYNC | | ||||||
| 			if ((utmp & 0xF) >= 0x0d) | 					  FE_HAS_LOCK; | ||||||
| 				*status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; | 			else if ((uitmp & 0x0f) >= 0x0a) | ||||||
|  | 				*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | | ||||||
|  | 					  FE_HAS_VITERBI; | ||||||
|  | 			else if ((uitmp & 0x0f) >= 0x07) | ||||||
|  | 				*status = FE_HAS_SIGNAL | FE_HAS_CARRIER; | ||||||
| 		} | 		} | ||||||
| 		break; | 		break; | ||||||
| 	case SYS_DVBC_ANNEX_A: | 	case SYS_DVBC_ANNEX_A: | ||||||
| 		ret = regmap_read(dev->regmap[1], 0x85, &utmp); | 		ret = regmap_read(dev->regmap[1], 0x85, &uitmp); | ||||||
| 		if (ret) | 		if (ret) | ||||||
| 			goto err; | 			goto err; | ||||||
| 		if (!(utmp & 0x40)) { | 
 | ||||||
| 			ret = regmap_read(dev->regmap[1], 0x89, &utmp); | 		if (!(uitmp & 0x40)) { | ||||||
|  | 			ret = regmap_read(dev->regmap[1], 0x89, &uitmp); | ||||||
| 			if (ret) | 			if (ret) | ||||||
| 				goto err; | 				goto err; | ||||||
| 			if (utmp & 0x01) | 
 | ||||||
| 				lock = 1; | 			if (uitmp & 0x01) | ||||||
|  | 				*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | | ||||||
|  | 					  FE_HAS_VITERBI | FE_HAS_SYNC | | ||||||
|  | 					  FE_HAS_LOCK; | ||||||
| 		} | 		} | ||||||
| 		break; | 		break; | ||||||
| 	default: | 	default: | ||||||
|  | @ -225,10 +302,6 @@ static int mn88473_read_status(struct dvb_frontend *fe, enum fe_status *status) | ||||||
| 		goto err; | 		goto err; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (lock) |  | ||||||
| 		*status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | |  | ||||||
| 				FE_HAS_SYNC | FE_HAS_LOCK; |  | ||||||
| 
 |  | ||||||
| 	return 0; | 	return 0; | ||||||
| err: | err: | ||||||
| 	dev_dbg(&client->dev, "failed=%d\n", ret); | 	dev_dbg(&client->dev, "failed=%d\n", ret); | ||||||
|  | @ -239,85 +312,76 @@ static int mn88473_init(struct dvb_frontend *fe) | ||||||
| { | { | ||||||
| 	struct i2c_client *client = fe->demodulator_priv; | 	struct i2c_client *client = fe->demodulator_priv; | ||||||
| 	struct mn88473_dev *dev = i2c_get_clientdata(client); | 	struct mn88473_dev *dev = i2c_get_clientdata(client); | ||||||
| 	int ret, len, remaining; | 	int ret, len, remain; | ||||||
| 	const struct firmware *fw = NULL; | 	unsigned int uitmp; | ||||||
| 	u8 *fw_file = MN88473_FIRMWARE; | 	const struct firmware *fw; | ||||||
| 	unsigned int tmp; | 	const char *name = MN88473_FIRMWARE; | ||||||
| 
 | 
 | ||||||
| 	dev_dbg(&client->dev, "\n"); | 	dev_dbg(&client->dev, "\n"); | ||||||
| 
 | 
 | ||||||
| 	/* set cold state by default */ | 	/* Check if firmware is already running */ | ||||||
| 	dev->warm = false; | 	ret = regmap_read(dev->regmap[0], 0xf5, &uitmp); | ||||||
| 
 |  | ||||||
| 	/* check if firmware is already running */ |  | ||||||
| 	ret = regmap_read(dev->regmap[0], 0xf5, &tmp); |  | ||||||
| 	if (ret) | 	if (ret) | ||||||
| 		goto err; | 		goto err; | ||||||
| 
 | 
 | ||||||
| 	if (!(tmp & 0x1)) { | 	if (!(uitmp & 0x01)) | ||||||
| 		dev_info(&client->dev, "firmware already running\n"); | 		goto warm; | ||||||
| 		dev->warm = true; |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	/* request the firmware, this will block and timeout */ | 	/* Request the firmware, this will block and timeout */ | ||||||
| 	ret = request_firmware(&fw, fw_file, &client->dev); | 	ret = request_firmware(&fw, name, &client->dev); | ||||||
| 	if (ret) { | 	if (ret) { | ||||||
| 		dev_err(&client->dev, "firmare file '%s' not found\n", fw_file); | 		dev_err(&client->dev, "firmare file '%s' not found\n", name); | ||||||
| 		goto err_request_firmware; | 		goto err; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	dev_info(&client->dev, "downloading firmware from file '%s'\n", | 	dev_info(&client->dev, "downloading firmware from file '%s'\n", name); | ||||||
| 		 fw_file); |  | ||||||
| 
 | 
 | ||||||
| 	ret = regmap_write(dev->regmap[0], 0xf5, 0x03); | 	ret = regmap_write(dev->regmap[0], 0xf5, 0x03); | ||||||
| 	if (ret) | 	if (ret) | ||||||
| 		goto err; | 		goto err_release_firmware; | ||||||
| 
 |  | ||||||
| 	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; |  | ||||||
| 
 | 
 | ||||||
|  | 	for (remain = fw->size; remain > 0; remain -= (dev->i2c_wr_max - 1)) { | ||||||
|  | 		len = min(dev->i2c_wr_max - 1, remain); | ||||||
| 		ret = regmap_bulk_write(dev->regmap[0], 0xf6, | 		ret = regmap_bulk_write(dev->regmap[0], 0xf6, | ||||||
| 					&fw->data[fw->size - remaining], len); | 					&fw->data[fw->size - remain], len); | ||||||
| 		if (ret) { | 		if (ret) { | ||||||
| 			dev_err(&client->dev, "firmware download failed=%d\n", | 			dev_err(&client->dev, "firmware download failed %d\n", | ||||||
| 				ret); | 				ret); | ||||||
| 			goto err; | 			goto err_release_firmware; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* parity check of firmware */ | 	release_firmware(fw); | ||||||
| 	ret = regmap_read(dev->regmap[0], 0xf8, &tmp); | 
 | ||||||
| 	if (ret) { | 	/* Parity check of firmware */ | ||||||
| 		dev_err(&client->dev, | 	ret = regmap_read(dev->regmap[0], 0xf8, &uitmp); | ||||||
| 				"parity reg read failed=%d\n", ret); | 	if (ret) | ||||||
|  | 		goto err; | ||||||
|  | 
 | ||||||
|  | 	if (uitmp & 0x10) { | ||||||
|  | 		dev_err(&client->dev, "firmware parity check failed\n"); | ||||||
|  | 		ret = -EINVAL; | ||||||
| 		goto err; | 		goto err; | ||||||
| 	} | 	} | ||||||
| 	if (tmp & 0x10) { |  | ||||||
| 		dev_err(&client->dev, |  | ||||||
| 				"firmware parity check failed=0x%x\n", tmp); |  | ||||||
| 		goto err; |  | ||||||
| 	} |  | ||||||
| 	dev_err(&client->dev, "firmware parity check succeeded=0x%x\n", tmp); |  | ||||||
| 
 | 
 | ||||||
| 	ret = regmap_write(dev->regmap[0], 0xf5, 0x00); | 	ret = regmap_write(dev->regmap[0], 0xf5, 0x00); | ||||||
| 	if (ret) | 	if (ret) | ||||||
| 		goto err; | 		goto err; | ||||||
|  | warm: | ||||||
|  | 	/* TS config */ | ||||||
|  | 	ret = regmap_write(dev->regmap[2], 0x09, 0x08); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
|  | 	ret = regmap_write(dev->regmap[2], 0x08, 0x1d); | ||||||
|  | 	if (ret) | ||||||
|  | 		goto err; | ||||||
| 
 | 
 | ||||||
| 	release_firmware(fw); | 	dev->active = true; | ||||||
| 	fw = NULL; |  | ||||||
| 
 |  | ||||||
| 	/* warm state */ |  | ||||||
| 	dev->warm = true; |  | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | err_release_firmware: | ||||||
| err: |  | ||||||
| 	release_firmware(fw); | 	release_firmware(fw); | ||||||
| err_request_firmware: | err: | ||||||
| 	dev_dbg(&client->dev, "failed=%d\n", ret); | 	dev_dbg(&client->dev, "failed=%d\n", ret); | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
|  | @ -330,20 +394,20 @@ static int mn88473_sleep(struct dvb_frontend *fe) | ||||||
| 
 | 
 | ||||||
| 	dev_dbg(&client->dev, "\n"); | 	dev_dbg(&client->dev, "\n"); | ||||||
| 
 | 
 | ||||||
|  | 	dev->active = false; | ||||||
|  | 
 | ||||||
| 	ret = regmap_write(dev->regmap[2], 0x05, 0x3e); | 	ret = regmap_write(dev->regmap[2], 0x05, 0x3e); | ||||||
| 	if (ret) | 	if (ret) | ||||||
| 		goto err; | 		goto err; | ||||||
| 
 | 
 | ||||||
| 	dev->delivery_system = SYS_UNDEFINED; |  | ||||||
| 
 |  | ||||||
| 	return 0; | 	return 0; | ||||||
| err: | err: | ||||||
| 	dev_dbg(&client->dev, "failed=%d\n", ret); | 	dev_dbg(&client->dev, "failed=%d\n", ret); | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct dvb_frontend_ops mn88473_ops = { | static const struct dvb_frontend_ops mn88473_ops = { | ||||||
| 	.delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_AC}, | 	.delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A}, | ||||||
| 	.info = { | 	.info = { | ||||||
| 		.name = "Panasonic MN88473", | 		.name = "Panasonic MN88473", | ||||||
| 		.symbol_rate_min = 1000000, | 		.symbol_rate_min = 1000000, | ||||||
|  | @ -365,8 +429,7 @@ static struct dvb_frontend_ops mn88473_ops = { | ||||||
| 			FE_CAN_GUARD_INTERVAL_AUTO     | | 			FE_CAN_GUARD_INTERVAL_AUTO     | | ||||||
| 			FE_CAN_HIERARCHY_AUTO          | | 			FE_CAN_HIERARCHY_AUTO          | | ||||||
| 			FE_CAN_MUTE_TS                 | | 			FE_CAN_MUTE_TS                 | | ||||||
| 			FE_CAN_2G_MODULATION           | | 			FE_CAN_2G_MODULATION | ||||||
| 			FE_CAN_MULTISTREAM |  | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	.get_tune_settings = mn88473_get_tune_settings, | 	.get_tune_settings = mn88473_get_tune_settings, | ||||||
|  | @ -385,7 +448,7 @@ static int mn88473_probe(struct i2c_client *client, | ||||||
| 	struct mn88473_config *config = client->dev.platform_data; | 	struct mn88473_config *config = client->dev.platform_data; | ||||||
| 	struct mn88473_dev *dev; | 	struct mn88473_dev *dev; | ||||||
| 	int ret; | 	int ret; | ||||||
| 	unsigned int utmp; | 	unsigned int uitmp; | ||||||
| 	static const struct regmap_config regmap_config = { | 	static const struct regmap_config regmap_config = { | ||||||
| 		.reg_bits = 8, | 		.reg_bits = 8, | ||||||
| 		.val_bits = 8, | 		.val_bits = 8, | ||||||
|  | @ -393,7 +456,7 @@ static int mn88473_probe(struct i2c_client *client, | ||||||
| 
 | 
 | ||||||
| 	dev_dbg(&client->dev, "\n"); | 	dev_dbg(&client->dev, "\n"); | ||||||
| 
 | 
 | ||||||
| 	/* Caller really need to provide pointer for frontend we create. */ | 	/* Caller really need to provide pointer for frontend we create */ | ||||||
| 	if (config->fe == NULL) { | 	if (config->fe == NULL) { | ||||||
| 		dev_err(&client->dev, "frontend pointer not defined\n"); | 		dev_err(&client->dev, "frontend pointer not defined\n"); | ||||||
| 		ret = -EINVAL; | 		ret = -EINVAL; | ||||||
|  | @ -406,11 +469,15 @@ static int mn88473_probe(struct i2c_client *client, | ||||||
| 		goto err; | 		goto err; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	dev->i2c_wr_max = config->i2c_wr_max; | 	if (config->i2c_wr_max) | ||||||
| 	if (!config->xtal) | 		dev->i2c_wr_max = config->i2c_wr_max; | ||||||
| 		dev->xtal = 25000000; |  | ||||||
| 	else | 	else | ||||||
| 		dev->xtal = config->xtal; | 		dev->i2c_wr_max = ~0; | ||||||
|  | 
 | ||||||
|  | 	if (config->xtal) | ||||||
|  | 		dev->clk = config->xtal; | ||||||
|  | 	else | ||||||
|  | 		dev->clk = 25000000; | ||||||
| 	dev->client[0] = client; | 	dev->client[0] = client; | ||||||
| 	dev->regmap[0] = regmap_init_i2c(dev->client[0], ®map_config); | 	dev->regmap[0] = regmap_init_i2c(dev->client[0], ®map_config); | ||||||
| 	if (IS_ERR(dev->regmap[0])) { | 	if (IS_ERR(dev->regmap[0])) { | ||||||
|  | @ -418,15 +485,25 @@ static int mn88473_probe(struct i2c_client *client, | ||||||
| 		goto err_kfree; | 		goto err_kfree; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* check demod answers to I2C */ | 	/* Check demod answers with correct chip id */ | ||||||
| 	ret = regmap_read(dev->regmap[0], 0x00, &utmp); | 	ret = regmap_read(dev->regmap[0], 0xff, &uitmp); | ||||||
| 	if (ret) | 	if (ret) | ||||||
| 		goto err_regmap_0_regmap_exit; | 		goto err_regmap_0_regmap_exit; | ||||||
| 
 | 
 | ||||||
|  | 	dev_dbg(&client->dev, "chip id=%02x\n", uitmp); | ||||||
|  | 
 | ||||||
|  | 	if (uitmp != 0x03) { | ||||||
|  | 		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, | 	 * 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); | 	dev->client[1] = i2c_new_dummy(client->adapter, 0x1a); | ||||||
| 	if (dev->client[1] == NULL) { | 	if (dev->client[1] == NULL) { | ||||||
|  | @ -456,13 +533,19 @@ static int mn88473_probe(struct i2c_client *client, | ||||||
| 	} | 	} | ||||||
| 	i2c_set_clientdata(dev->client[2], dev); | 	i2c_set_clientdata(dev->client[2], dev); | ||||||
| 
 | 
 | ||||||
| 	/* create dvb_frontend */ | 	/* Sleep because chip is active by default */ | ||||||
| 	memcpy(&dev->fe.ops, &mn88473_ops, sizeof(struct dvb_frontend_ops)); | 	ret = regmap_write(dev->regmap[2], 0x05, 0x3e); | ||||||
| 	dev->fe.demodulator_priv = client; | 	if (ret) | ||||||
| 	*config->fe = &dev->fe; | 		goto err_client_2_i2c_unregister_device; | ||||||
|  | 
 | ||||||
|  | 	/* Create dvb frontend */ | ||||||
|  | 	memcpy(&dev->frontend.ops, &mn88473_ops, sizeof(dev->frontend.ops)); | ||||||
|  | 	dev->frontend.demodulator_priv = client; | ||||||
|  | 	*config->fe = &dev->frontend; | ||||||
| 	i2c_set_clientdata(client, dev); | 	i2c_set_clientdata(client, dev); | ||||||
| 
 | 
 | ||||||
| 	dev_info(&dev->client[0]->dev, "Panasonic MN88473 successfully attached\n"); | 	dev_info(&client->dev, "Panasonic MN88473 successfully identified\n"); | ||||||
|  | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | 
 | ||||||
| err_client_2_i2c_unregister_device: | err_client_2_i2c_unregister_device: | ||||||
|  | @ -507,7 +590,8 @@ MODULE_DEVICE_TABLE(i2c, mn88473_id_table); | ||||||
| 
 | 
 | ||||||
| static struct i2c_driver mn88473_driver = { | static struct i2c_driver mn88473_driver = { | ||||||
| 	.driver = { | 	.driver = { | ||||||
| 		.name	= "mn88473", | 		.name	             = "mn88473", | ||||||
|  | 		.suppress_bind_attrs = true, | ||||||
| 	}, | 	}, | ||||||
| 	.probe		= mn88473_probe, | 	.probe		= mn88473_probe, | ||||||
| 	.remove		= mn88473_remove, | 	.remove		= mn88473_remove, | ||||||
|  |  | ||||||
|  | @ -22,10 +22,16 @@ | ||||||
| struct mn88473_config { | struct mn88473_config { | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Max num of bytes given I2C adapter could write at once. | 	 * Max num of bytes given I2C adapter could write at once. | ||||||
| 	 * Default: none | 	 * Default: unlimited | ||||||
| 	 */ | 	 */ | ||||||
| 	u16 i2c_wr_max; | 	u16 i2c_wr_max; | ||||||
| 
 | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Xtal frequency Hz. | ||||||
|  | 	 * Default: 25000000 | ||||||
|  | 	 */ | ||||||
|  | 	u32 xtal; | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| 	/* Everything after that is returned by the driver. */ | 	/* Everything after that is returned by the driver. */ | ||||||
| 
 | 
 | ||||||
|  | @ -33,12 +39,6 @@ struct mn88473_config { | ||||||
| 	 * DVB frontend. | 	 * DVB frontend. | ||||||
| 	 */ | 	 */ | ||||||
| 	struct dvb_frontend **fe; | 	struct dvb_frontend **fe; | ||||||
| 
 |  | ||||||
| 	/*
 |  | ||||||
| 	 * Xtal frequency. |  | ||||||
| 	 * Hz |  | ||||||
| 	 */ |  | ||||||
| 	u32 xtal; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -27,11 +27,10 @@ | ||||||
| struct mn88473_dev { | struct mn88473_dev { | ||||||
| 	struct i2c_client *client[3]; | 	struct i2c_client *client[3]; | ||||||
| 	struct regmap *regmap[3]; | 	struct regmap *regmap[3]; | ||||||
| 	struct dvb_frontend fe; | 	struct dvb_frontend frontend; | ||||||
| 	u16 i2c_wr_max; | 	u16 i2c_wr_max; | ||||||
| 	enum fe_delivery_system delivery_system; | 	bool active; | ||||||
| 	bool warm; /* FW running */ | 	u32 clk; | ||||||
| 	u32 xtal; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Antti Palosaari
						Antti Palosaari