tda8290: Make all read operations atomic
A message that is not in the archive (sent off-list or not yet received): [email protected]
PATCH 1/8 tda8290: Make all read operations atomic
Read operations should be preceeded by a write operation. However, nothing prevents that an I2C operation could happen between the two transactions.
To avoid that problem, use an unique I2C transfer for both parts of the I2C transaction.
drivers/media/common/tuners/tda8290.c +66 -53
@@ -95,8 +95,7 @@ static int tda8295_i2c_bridge(struct dvb_frontend *fe, int close)msleep (20 ); }else { msg = disable; -tuner_i2c_xfer_send (&priv->i2c_props, msg,1 ); -tuner_i2c_xfer_recv (&priv->i2c_props, &msg[1 ],1 ); +tuner_i2c_xfer_send_recv (&priv->i2c_props, msg,1 , &msg[1 ],1 ); buf[2 ] = msg[1 ]; buf[2 ] &= ~0x04 ;
@@ -239,13 +238,15 @@ static void tda8290_set_params(struct dvb_frontend *fe, fe->ops.tuner_ops.set_analog_params (fe, params);for (i =0 ; i <3 ; i++) { -tuner_i2c_xfer_send (&priv->i2c_props, &addr_pll_stat,1 ); -tuner_i2c_xfer_recv (&priv->i2c_props, &pll_stat,1 ); +tuner_i2c_xfer_send_recv (&priv->i2c_props, + &addr_pll_stat,1 , &pll_stat,1 );if (pll_stat &0x80 ) { -tuner_i2c_xfer_send (&priv->i2c_props, &addr_adc_sat,1 ); -tuner_i2c_xfer_recv (&priv->i2c_props, &adc_sat,1 ); -tuner_i2c_xfer_send (&priv->i2c_props, &addr_agc_stat,1 ); -tuner_i2c_xfer_recv (&priv->i2c_props, &agc_stat,1 ); +tuner_i2c_xfer_send_recv (&priv->i2c_props, + &addr_adc_sat,1 , + &adc_sat,1 ); +tuner_i2c_xfer_send_recv (&priv->i2c_props, + &addr_agc_stat,1 , + &agc_stat,1 );tuner_dbg ("tda8290 is locked, AGC: %d\n" , agc_stat);break ; }else {
@@ -259,20 +260,22 @@ static void tda8290_set_params(struct dvb_frontend *fe, agc_stat, adc_sat, pll_stat & 0x80);tuner_i2c_xfer_send (&priv->i2c_props, gainset_2,2 );msleep (100 ); -tuner_i2c_xfer_send (&priv->i2c_props, &addr_agc_stat,1 ); -tuner_i2c_xfer_recv (&priv->i2c_props, &agc_stat,1 ); -tuner_i2c_xfer_send (&priv->i2c_props, &addr_pll_stat,1 ); -tuner_i2c_xfer_recv (&priv->i2c_props, &pll_stat,1 ); +tuner_i2c_xfer_send_recv (&priv->i2c_props, + &addr_agc_stat,1 , &agc_stat,1 ); +tuner_i2c_xfer_send_recv (&priv->i2c_props, + &addr_pll_stat,1 , &pll_stat,1 );if ((agc_stat >115 ) || !(pll_stat &0x80 )) {tuner_dbg ("adjust gain, step 2. Agc: %d, lock: %d\n" , agc_stat, pll_stat &0x80 );if (priv->cfg.agcf) priv->cfg.agcf (fe);msleep (100 ); -tuner_i2c_xfer_send (&priv->i2c_props, &addr_agc_stat,1 ); -tuner_i2c_xfer_recv (&priv->i2c_props, &agc_stat,1 ); -tuner_i2c_xfer_send (&priv->i2c_props, &addr_pll_stat,1 ); -tuner_i2c_xfer_recv (&priv->i2c_props, &pll_stat,1 ); +tuner_i2c_xfer_send_recv (&priv->i2c_props, + &addr_agc_stat,1 , + &agc_stat,1 ); +tuner_i2c_xfer_send_recv (&priv->i2c_props, + &addr_pll_stat,1 , + &pll_stat,1 );if ((agc_stat >115 ) || !(pll_stat &0x80 )) {tuner_dbg ("adjust gain, step 3. Agc: %d\n" , agc_stat);tuner_i2c_xfer_send (&priv->i2c_props, adc_head_12,2 );
@@ -284,10 +287,12 @@ static void tda8290_set_params(struct dvb_frontend *fe,/* l/ l' deadlock? */ if (priv->tda8290_easy_mode &0x60 ) { -tuner_i2c_xfer_send (&priv->i2c_props, &addr_adc_sat,1 ); -tuner_i2c_xfer_recv (&priv->i2c_props, &adc_sat,1 ); -tuner_i2c_xfer_send (&priv->i2c_props, &addr_pll_stat,1 ); -tuner_i2c_xfer_recv (&priv->i2c_props, &pll_stat,1 ); +tuner_i2c_xfer_send_recv (&priv->i2c_props, + &addr_adc_sat,1 , + &adc_sat,1 ); +tuner_i2c_xfer_send_recv (&priv->i2c_props, + &addr_pll_stat,1 , + &pll_stat,1 );if ((adc_sat >20 ) || !(pll_stat &0x80 )) {tuner_dbg ("trying to resolve SECAM L deadlock\n" );tuner_i2c_xfer_send (&priv->i2c_props, agc_rst_on,2 );
@@ -307,8 +312,7 @@ static void tda8295_power(struct dvb_frontend *fe, int enable)struct tda8290_priv *priv = fe->analog_demod_priv;unsigned char buf[] = {0x30 ,0x00 };/* clb_stdbt */ -tuner_i2c_xfer_send (&priv->i2c_props, &buf[0 ],1 ); -tuner_i2c_xfer_recv (&priv->i2c_props, &buf[1 ],1 ); +tuner_i2c_xfer_send_recv (&priv->i2c_props, &buf[0 ],1 , &buf[1 ],1 );if (enable) buf[1 ] =0x01 ;
@@ -323,8 +327,7 @@ static void tda8295_set_easy_mode(struct dvb_frontend *fe, int enable)struct tda8290_priv *priv = fe->analog_demod_priv;unsigned char buf[] = {0x01 ,0x00 }; -tuner_i2c_xfer_send (&priv->i2c_props, &buf[0 ],1 ); -tuner_i2c_xfer_recv (&priv->i2c_props, &buf[1 ],1 ); +tuner_i2c_xfer_send_recv (&priv->i2c_props, &buf[0 ],1 , &buf[1 ],1 );if (enable) buf[1 ] =0x01 ;/* rising edge sets regs 0x02 - 0x23 */
@@ -353,8 +356,7 @@ static void tda8295_agc1_out(struct dvb_frontend *fe, int enable)struct tda8290_priv *priv = fe->analog_demod_priv;unsigned char buf[] = {0x02 ,0x00 };/* DIV_FUNC */ -tuner_i2c_xfer_send (&priv->i2c_props, &buf[0 ],1 ); -tuner_i2c_xfer_recv (&priv->i2c_props, &buf[1 ],1 ); +tuner_i2c_xfer_send_recv (&priv->i2c_props, &buf[0 ],1 , &buf[1 ],1 );if (enable) buf[1 ] &= ~0x40 ;
@@ -370,10 +372,10 @@ static void tda8295_agc2_out(struct dvb_frontend *fe, int enable)unsigned char set_gpio_cf[] = {0x44 ,0x00 };unsigned char set_gpio_val[] = {0x46 ,0x00 }; -tuner_i2c_xfer_send (&priv->i2c_props, &set_gpio_cf[0 ],1 ); -tuner_i2c_xfer_recv (&priv->i2c_props, &set_gpio_cf[1 ],1 ); -tuner_i2c_xfer_send (&priv->i2c_props, &set_gpio_val[0 ],1 ); -tuner_i2c_xfer_recv (&priv->i2c_props, &set_gpio_val[1 ],1 ); +tuner_i2c_xfer_send_recv (&priv->i2c_props, + &set_gpio_cf[0 ],1 , &set_gpio_cf[1 ],1 ); +tuner_i2c_xfer_send_recv (&priv->i2c_props, + &set_gpio_val[0 ],1 , &set_gpio_val[1 ],1 ); set_gpio_cf[1 ] &=0xf0 ;/* clear GPIO_0 bits 3-0 */
@@ -392,8 +394,7 @@ static int tda8295_has_signal(struct dvb_frontend *fe)unsigned char hvpll_stat =0x26 ;unsigned char ret; -tuner_i2c_xfer_send (&priv->i2c_props, &hvpll_stat,1 ); -tuner_i2c_xfer_recv (&priv->i2c_props, &ret,1 ); +tuner_i2c_xfer_send_recv (&priv->i2c_props, &hvpll_stat,1 , &ret,1 );return (ret &0x01 ) ?65535 :0 ; }
@@ -413,8 +414,8 @@ static void tda8295_set_params(struct dvb_frontend *fe,tda8295_power (fe,1 );tda8295_agc1_out (fe,1 ); -tuner_i2c_xfer_send (&priv->i2c_props, &blanking_mode[0 ],1 ); -tuner_i2c_xfer_recv (&priv->i2c_props, &blanking_mode[1 ],1 ); +tuner_i2c_xfer_send_recv (&priv->i2c_props, + &blanking_mode[0 ],1 , &blanking_mode[1 ],1 );tda8295_set_video_std (fe);
@@ -447,8 +448,8 @@ static int tda8290_has_signal(struct dvb_frontend *fe)unsigned char i2c_get_afc[1 ] = {0x1B };unsigned char afc =0 ; -tuner_i2c_xfer_send (&priv->i2c_props, i2c_get_afc,ARRAY_SIZE (i2c_get_afc)); -tuner_i2c_xfer_recv (&priv->i2c_props, &afc,1 ); +tuner_i2c_xfer_send_recv (&priv->i2c_props, + i2c_get_afc,ARRAY_SIZE (i2c_get_afc), &afc,1 );return (afc &0x80 )?65535 :0 ; }
@@ -654,20 +655,26 @@ static int tda829x_find_tuner(struct dvb_frontend *fe)static int tda8290_probe (struct tuner_i2c_props *i2c_props) {#define TDA8290_ID 0x89 -unsigned char tda8290_id[] = {0x1f ,0x00 }; +u8 reg =0x1f , id; +struct i2c_msg msg_read[] = { + { .addr =0x4b , .flags =0 , .len =1 , .buf = ® }, + { .addr =0x4b , .flags =I2C_M_RD , .len =1 , .buf = &id }, + };/* detect tda8290 */ -tuner_i2c_xfer_send (i2c_props, &tda8290_id[0 ],1 ); -tuner_i2c_xfer_recv (i2c_props, &tda8290_id[1 ],1 ); +if (i2c_transfer (i2c_props->adap, msg_read,2 ) !=2 ) { +printk (KERN_WARNING "%s: tda8290 couldn't read register 0x%02x\n" , + __func__, reg); +return -ENODEV ; + } -if (tda8290_id[1 ] ==TDA8290_ID ) { +if (id ==TDA8290_ID ) {if (debug)printk (KERN_DEBUG "%s: tda8290 detected @ %d-%04x\n" , __func__,i2c_adapter_id (i2c_props->adap), i2c_props->addr);return 0 ; } -return -ENODEV ; }
@@ -675,16 +682,23 @@ static int tda8295_probe(struct tuner_i2c_props *i2c_props) {#define TDA8295_ID 0x8a#define TDA8295C2_ID 0x8b -unsigned char tda8295_id[] = {0x2f ,0x00 }; +u8 reg =0x2f , id; +struct i2c_msg msg_read[] = { + { .addr =0x4b , .flags =0 , .len =1 , .buf = ® }, + { .addr =0x4b , .flags =I2C_M_RD , .len =1 , .buf = &id }, + }; -/* detect tda8295 */ -tuner_i2c_xfer_send (i2c_props, &tda8295_id[0 ],1 ); -tuner_i2c_xfer_recv (i2c_props, &tda8295_id[1 ],1 ); +/* detect tda8290 */ +if (i2c_transfer (i2c_props->adap, msg_read,2 ) !=2 ) { +printk (KERN_WARNING "%s: tda8290 couldn't read register 0x%02x\n" , + __func__, reg); +return -ENODEV ; + } -if ((tda8295_id[1 ] &0xfe ) ==TDA8295_ID ) { +if ((id &0xfe ) ==TDA8295_ID ) {if (debug)printk (KERN_DEBUG "%s: %s detected @ %d-%04x\n" , - __func__, (tda8295_id[1 ] ==TDA8295_ID ) ? + __func__, (id ==TDA8295_ID ) ?"tda8295c1" :"tda8295c2" ,i2c_adapter_id (i2c_props->adap), i2c_props->addr);
@@ -809,8 +823,8 @@ int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr)int i;/* rule out tda9887, which would return the same byte repeatedly */ -tuner_i2c_xfer_send (&i2c_props, soft_reset,1 ); -tuner_i2c_xfer_recv (&i2c_props, buf,PROBE_BUFFER_SIZE ); +tuner_i2c_xfer_send_recv (&i2c_props, + soft_reset,1 , buf,PROBE_BUFFER_SIZE );for (i =1 ; i <PROBE_BUFFER_SIZE ; i++) {if (buf[i] != buf[0 ])break ;
@@ -827,13 +841,12 @@ int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr)/* fall back to old probing method */ tuner_i2c_xfer_send (&i2c_props, easy_mode_b,2 );tuner_i2c_xfer_send (&i2c_props, soft_reset,2 ); -tuner_i2c_xfer_send (&i2c_props, &addr_dto_lsb,1 ); -tuner_i2c_xfer_recv (&i2c_props, &data,1 ); +tuner_i2c_xfer_send_recv (&i2c_props, &addr_dto_lsb,1 , &data,1 );if (data ==0 ) {tuner_i2c_xfer_send (&i2c_props, easy_mode_g,2 );tuner_i2c_xfer_send (&i2c_props, soft_reset,2 ); -tuner_i2c_xfer_send (&i2c_props, &addr_dto_lsb,1 ); -tuner_i2c_xfer_recv (&i2c_props, &data,1 ); +tuner_i2c_xfer_send_recv (&i2c_props, + &addr_dto_lsb,1 , &data,1 );if (data ==0x7b ) {return 0 ; }
Signature · 1 line
1.7.1
PATCH 2/8 tda8290: Fix a bug if no tuner is detected
In reply to a message not in the archiveIf tda8290 is detected, but no tuner is found, the driver will do bad things:
tuner 2-0060: chip found @ 0xc0 (saa7133[0])
tda829x 2-0060: could not clearly identify tuner address, defaulting to 60 tda829x 2-0060: tuner access failed!
BUG: unable to handle kernel NULL pointer dereference at 0000000000000020 IP: [<ffffffffa048c267>] set_audio+0x47/0x170 [tda8290] PGD 1187b0067 PUD 11771e067 PMD 0 Oops: 0002 [#1] SMP last sysfs file: /sys/module/i2c_core/initstate CPU 0 Modules linked in: tda8290(U) tea5767(U) tuner(U) ir_lirc_codec(U) lirc_dev(U) ir_sony_decoder(U) ir_jvc_decoder(U) ir_rc6_decoder(U) ir_rc5_decoder(U) saa7134(+)(U) v4l2_common(U) ir_nec_decoder(U) videodev(U) v4l2_compat_ioctl32(U) rc_core(U) videobuf_dma_sg(U) videobuf_core(U) tveeprom(U) ebtable_nat ebtables xt_CHECKSUM iptable_mangle ipt_MASQUERADE iptable_nat nf_nat nf_conntrack_ipv4 nf_defrag_ipv4 xt_state nf_conntrack ipt_REJECT bridge stp llc autofs4 sunrpc cpufreq_ondemand acpi_cpufreq freq_table xt_physdev iptable_filter ip_tables ip6t_REJECT ip6table_filter ip6_tables ipv6 dm_mirror dm_region_hash dm_log parport kvm_intel kvm uinput floppy tpm_infineon wmi sg serio_raw iTCO_wdt iTCO_vendor_support tg3 snd_hda_codec_realtek snd_hda_intel snd_hda_codec snd_hwdep snd_seq snd_seq_device snd_pcm snd_timer snd soundcore snd_page_alloc i7core_edac edac_core nouveau Modules linked in: tda8290(U) tea5767(U) tuner(U) ir_lirc_codec(U) lirc_dev(U) ir_sony_decoder(U) ir_jvc_decoder(U) ir_rc6_decoder(U) ir_rc5_decoder(U) saa7134(+)(U) v4l2_common(U) ir_nec_decoder(U) videodev(U) v4l2_compat_ioctl32(U) rc_core(U) videobuf_dma_sg(U) videobuf_core(U) tveeprom(U) ebtable_nat ebtables xt_CHECKSUM iptable_mangle ipt_MASQUERADE iptable_nat nf_nat nf_conntrack_ipv4 nf_defrag_ipv4 xt_state nf_conntrack ipt_REJECT bridge stp llc autofs4 sunrpc cpufreq_ondemand acpi_cpufreq freq_table xt_physdev iptable_filter ip_tables ip6t_REJECT ip6table_filter ip6_tables ipv6 dm_mirror dm_region_hash dm_log parport kvm_intel kvm uinput floppy tpm_infineon wmi sg serio_raw iTCO_wdt iTCO_vendor_support tg3 snd_hda_codec_realtek snd_hda_intel snd_hda_codec snd_hwdep snd_seq snd_seq_device snd_pcm snd_timer snd soundcore snd_page_alloc i7core_edac edac_core nouveau ttm drm_kms_helper drm i2c_algo_bit video output i2c_core ext3 jbd mbcache firewire_ohci firewire_core crc! _itu_t sr_mod cdrom sd_mod crc_t10dif ahci dm_mod [last unloaded: microcode] Pid: 9497, comm: modprobe Not tainted 2.6.32-72.el6.x86_64 #1 HP Z400 Workstation RIP: 0010:[<ffffffffa048c267>] [<ffffffffa048c267>] set_audio+0x47/0x170 [tda8290] RSP: 0018:ffff88010ba01b28 EFLAGS: 00010206 RAX: 00000000000000ff RBX: ffff880119522800 RCX: 0000000000000002 RDX: 0000000000003be0 RSI: ffff88010ba01bb8 RDI: 0000000000000000 RBP: ffff88010ba01b28 R08: 0000000000000002 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000 R13: ffff88010ba01bb8 R14: 0000000000001900 R15: 0000000000001900 FS: 00007f4b96b3d700(0000) GS:ffff880028200000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000020 CR3: 000000011866c000 CR4: 00000000000026f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process modprobe (pid: 9497, threadinfo ffff88010ba00000, task ffff880100708a70) Stack: ffff88010ba01b98 ffffffffa048c95b ffff88010ba01b78 0000000000000060 <0> 0000000000000000 0000000e00000000 000000000000001d ffffffffa03ec838 <0> ffff88010abac240 ffff880119522800 ffff880119522800 ffff880119522bc0 Call Trace: [<ffffffffa048c95b>] tda8295_set_params+0x3b/0x210 [tda8290] [<ffffffffa03ec838>] ? v4l2_i2c_new_subdev_cfg+0x88/0xc0 [v4l2_common] [<ffffffffa0484418>] set_freq+0x128/0x2f0 [tuner] [<ffffffffa0486464>] tuner_s_std+0xc4/0x740 [tuner] [<ffffffffa04b9ae6>] saa7134_set_tvnorm_hw+0x2d6/0x3d0 [saa7134] [<ffffffffa04ba455>] set_tvnorm+0xd5/0x100 [saa7134] [<ffffffffa04bc9fd>] saa7134_video_init2+0x1d/0x50 [saa7134] [<ffffffffa04bf57e>] saa7134_initdev+0x6e1/0xb1d [saa7134] [<ffffffff8125afea>] ? kobject_get+0x1a/0x30 [<ffffffff812765f7>] local_pci_probe+0x17/0x20 [<ffffffff812777e1>] pci_device_probe+0x101/0x120 [<ffffffff8132ec72>] ? driver_sysfs_add+0x62/0x90 [<ffffffff8132ee10>] driver_probe_device+0xa0/0x2a0 [<ffffffff8132f0bb>] __driver_attach+0xab/0xb0 [<ffffffff8132f010>] ? __driver_attach+0x0/0xb0 [<ffffffff8132e074>] bus_for_each_dev+0x64/0x90 [<ffffffff8132ebae>] driver_attach+0x1e/0x20 [<ffffffff8132e4b0>] bus_add_driver+0x200/0x300 [<ffffffff8132f3e6>] driver_register+0x76/0x140 [<ffffffff814c7c43>] ? printk+0x41/0x46 [<ffffffff81277a46>] __pci_register_driver+0x56/0xd0 [<ffffffffa04de000>] ? saa7134_init+0x0/0x4f [saa7134] [<ffffffffa04de04d>] saa7134_init+0x4d/0x4f [saa7134] [<ffffffff8100a04c>] do_one_initcall+0x3c/0x1d0 [<ffffffff810af5ef>] sys_init_module+0xdf/0x250 [<ffffffff81013172>] system_call_fastpath+0x16/0x1b Code: 20 01 49 c7 c0 c9 ec 48 a0 83 7e 04 01 74 2d 8b 0d 3f 2f 00 00 85 c9 0f 85 d7 00 00 00 c9 c3 0f 1f 44 00 00 a9 03 00 01 00 74 61 <c6> 47 20 02 83 7e 04 01 49 c7 c0 cc ec 48 a0 75 d3 0f b6 47 22 RIP [<ffffffffa048c267>] set_audio+0x47/0x170 [tda8290] RSP <ffff88010ba01b28>
CR2: 0000000000000020
This happens because some I2C callbacks actually depend on having the driver entirely initialized. To avoid this OOPS, just clean the I2C callbacks, as if no device were detected.
drivers/media/common/tuners/tda8290.c +4 -1
@@ -755,8 +755,11 @@ struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe, }if ((!(cfg) || (TDA829X_PROBE_TUNER == cfg->probe_tuner)) && - (tda829x_find_tuner (fe) <0 )) + (tda829x_find_tuner (fe) <0 )) { +memset (&fe->ops.analog_ops,0 ,sizeof (struct analog_demod_ops )); + goto fail; + }switch (priv->ver) {case TDA8290 :
Signature · 1 line
1.7.1
PATCH 3/8 tda8290: Turn tda829x on before touching at the I2C gate
In reply to a message not in the archiveOn Kworld SBTVD, tda8295-c1 starts in power off mode. It needs to be powered, otherwise, the I2C gate control command won't work.
drivers/media/common/tuners/tda8290.c +6 -5
@@ -754,11 +754,10 @@ struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe, sizeof(struct analog_demod_ops)); } -if ((!(cfg) || (TDA829X_PROBE_TUNER == cfg->probe_tuner)) && - (tda829x_find_tuner (fe) <0 )) { -memset (&fe->ops.analog_ops,0 ,sizeof (struct analog_demod_ops )); - - goto fail; +if (!(cfg) || (TDA829X_PROBE_TUNER == cfg->probe_tuner)) { +tda8295_power (fe,1 ); +if (tda829x_find_tuner (fe) <0 ) + goto fail; }switch (priv->ver) {
@@ -803,6 +802,8 @@ struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe,return fe; fail: +memset (&fe->ops.analog_ops,0 ,sizeof (struct analog_demod_ops )); +tda829x_release (fe);return NULL ; }
Signature · 1 line
1.7.1
PATCH 4/8 mb86a20s: Fix i2c read/write error messages
In reply to a message not in the archiveA script replaced err var to rc. Howerver, this script gambled "error" string, changing it to "rcor". Revert that bad change.
drivers/media/dvb/frontends/mb86a20s.c +2 -2
@@ -318,7 +318,7 @@ static int mb86a20s_i2c_writereg(struct mb86a20s_state *state, rc =i2c_transfer (state->i2c, &msg,1 );if (rc !=1 ) { -printk ("%s: writereg rcor(rc == %i, reg == 0x%02x," +printk ("%s: writereg error (rc == %i, reg == 0x%02x," " data == 0x%02x)\n" , __func__, rc, reg, data);return rc; }
@@ -353,7 +353,7 @@ static int mb86a20s_i2c_readreg(struct mb86a20s_state *state, rc =i2c_transfer (state->i2c, msg,2 );if (rc !=2 ) { -rc ("%s: reg=0x%x (rcor=%d)\n" , __func__, reg, rc); +rc ("%s: reg=0x%x (error=%d)\n" , __func__, reg, rc);return rc; }
Signature · 1 line
1.7.1
PATCH 5/8 mb86a20s: Be sure that device is initialized before starting DVB
In reply to a message not in the archiveDue to a hard to track bug between tda829x/tda18271/saa7134, tda829x wants to go to analog mode during DVB initialization, causing some I2C errors.
The analog failure doesn't cause any harm, as the device were already properly initialized in analog mode. However, the failure at the digital mode causes the frontend mb86a20s to not initialize. Fortunately, at least on my tests, it was possible to detect that the device is a mb86a20s before the failure.
What happens is that tda8290 is a very bad boy: during DVB setup, it keeps insisting to call tda18271 analog_set_params, that calls tune_agc code. The tune_agc code calls saa7134 driver, changing the value of GPIO 27, switching from digital to analog mode and disabling the access to mb86a20s, as, on Kworld SBTVD, the same GPIO used to switch the hardware AGC mode seems to be used to enable the I2C switch that allows access to the frontend (mb86a20s).
So, a call to analog_set_params ultimately disables the access to the frontend, and causes a failure at the init frontend logic.
This patch is a workaround for this issue: it simply checks if the frontend init had any failure. If so, it will init the frontend when some DTV application will try to set DVB mode.
Even being a hack for Kworld SBTVD to work, and assumning that we could teach tda8290 to be a good boy, this is actually an improvement at the frontend driver, as it will be more reliable to initialization failures.
drivers/media/dvb/frontends/mb86a20s.c +28 -4
@@ -43,6 +43,8 @@ struct mb86a20s_state {const struct mb86a20s_config *config;struct dvb_frontend frontend; + +bool need_init; };struct regdata {
@@ -382,23 +384,31 @@ static int mb86a20s_initfe(struct dvb_frontend *fe)/* Initialize the frontend */ rc =mb86a20s_writeregdata (state, mb86a20s_init);if (rc <0 ) -return rc; + goto err;if (!state->config->is_serial) { regD5 &= ~1 ; rc =mb86a20s_writereg (state,0x50 ,0xd5 );if (rc <0 ) -return rc; + goto err; rc =mb86a20s_writereg (state,0x51 , regD5);if (rc <0 ) -return rc; + goto err; }if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl (fe,1 ); -return 0 ; +err: +if (rc <0 ) { + state->need_init = true; +printk (KERN_INFO "mb86a20s: Init failed. Will try again later\n" ); + }else { + state->need_init = false; +dprintk ("Initialization succeded.\n" ); + } +return rc; }static int mb86a20s_read_signal_strength (struct dvb_frontend *fe,u16 *strength)
@@ -485,8 +495,22 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe,if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl (fe,1 ); +dprintk ("Calling tuner set parameters\n" ); fe->ops.tuner_ops.set_params (fe, p); +/* +* Make it more reliable: if, for some reason, the initial +* device initialization doesn't happen, initialize it when +* a SBTVD parameters are adjusted. +* +* Unfortunately, due to a hard to track bug at tda829x/tda18271, +* the agc callback logic is not called during DVB attach time, +* causing mb86a20s to not be initialized with Kworld SBTVD. +* So, this hack is needed, in order to make Kworld SBTVD to work. +*/ +if (state->need_init) +mb86a20s_initfe (fe); +if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl (fe,0 ); rc =mb86a20s_writeregdata (state, mb86a20s_reset_reception);
Signature · 1 line
1.7.1
PATCH 6/8 saa7134: Fix analog mode for Kworld SBTVD
In reply to a message not in the archiveThere were some issues at tda8290 that were preventing this device to work. Now that those fixes were fixed, we can enable analog mode.
drivers/media/video/saa7134/saa7134-cards.c +3 -37
@@ -5179,18 +5179,8 @@ struct saa7134_board saa7134_boards[] = { [SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG] = { .name = "Kworld PCI SBTVD/ISDB-T Full-Seg Hybrid", .audio_clock = 0x00187de7, -#if 0 -/* -* FIXME: Analog mode doesn't work, if digital is enabled. The proper -* fix is to use tda8290 driver, but Kworld seems to use an -* unsupported version of tda8295. -*/ - .tuner_type = TUNER_NXP_TDA18271, /* TUNER_PHILIPS_TDA8290 */ - .tuner_addr = 0x60, -#else - .tuner_type = UNSET, + .tuner_type = TUNER_PHILIPS_TDA8290, .tuner_addr = ADDR_UNSET, -#endif .radio_type = UNSET, .radio_addr = ADDR_UNSET, .gpiomask = 0x8e054000,
@@ -5201,6 +5191,7 @@ struct saa7134_board saa7134_boards[] = { .vmux = 1, .amux = TV, .tv = 1, + .gpio = 0x4000,#if 0 /* FIXME */ }, { .name = name_comp1,
@@ -7659,36 +7650,11 @@ int saa7134_board_init2(struct saa7134_dev *dev)break ; }case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG : - { -struct i2c_msg msg = { .addr =0x4b , .flags =0 }; -int i; -static u8 buffer[][2 ] = { - {0x30 ,0x31 }, - {0xff ,0x00 }, - {0x41 ,0x03 }, - {0x41 ,0x1a }, - {0xff ,0x02 }, - {0x34 ,0x00 }, - {0x45 ,0x97 }, - {0x45 ,0xc1 }, - };saa_writel (SAA7134_GPIO_GPMODE0 >>2 ,0x4000 );saa_writel (SAA7134_GPIO_GPSTATUS0 >>2 ,0x4000 ); -/* -* FIXME: identify what device is at addr 0x4b and what means -* this initialization -*/ -for (i =0 ; i <ARRAY_SIZE (buffer); i++) { - msg.buf = &buffer[i][0 ]; - msg.len =ARRAY_SIZE (buffer[0 ]); -if (i2c_transfer (&dev->i2c_adap, &msg,1 ) !=1 ) -printk (KERN_WARNING -"%s: Unable to enable tuner(%i).\n" , - dev->name, i); - } +saa7134_set_gpio (dev,27 ,0 );break ; - } }/* switch() */ /* initialize tuner */
drivers/media/video/saa7134/saa7134-dvb.c +1 -39
@@ -236,7 +236,7 @@ static struct tda18271_std_map mb86a20s_tda18271_std_map = {static struct tda18271_config kworld_tda18271_config = { .std_map = &mb86a20s_tda18271_std_map, - .gate =TDA18271_GATE_DIGITAL , + .gate =TDA18271_GATE_ANALOG , };static const struct mb86a20s_config kworld_mb86a20s_config = {
@@ -623,37 +623,6 @@ static struct tda827x_config tda827x_cfg_2_sw42 = {/* ------------------------------------------------------------------ */ -static int __kworld_sbtvd_i2c_gate_ctrl (struct saa7134_dev *dev,int enable) -{ -unsigned char initmsg[] = {0x45 ,0x97 }; -unsigned char msg_enable[] = {0x45 ,0xc1 }; -unsigned char msg_disable[] = {0x45 ,0x81 }; -struct i2c_msg msg = {.addr =0x4b , .flags =0 , .buf = initmsg, .len =2 }; - -if (i2c_transfer (&dev->i2c_adap, &msg,1 ) !=1 ) { -wprintk ("could not access the I2C gate\n" ); -return -EIO ; - } -if (enable) - msg.buf = msg_enable; -else - msg.buf = msg_disable; -if (i2c_transfer (&dev->i2c_adap, &msg,1 ) !=1 ) { -wprintk ("could not access the I2C gate\n" ); -return -EIO ; - } -msleep (20 ); -return 0 ; -} -static int kworld_sbtvd_i2c_gate_ctrl (struct dvb_frontend *fe,int enable) -{ -struct saa7134_dev *dev = fe->dvb->priv; - -return __kworld_sbtvd_i2c_gate_ctrl (dev, enable); -} - -/* ------------------------------------------------------------------ */ -static struct tda1004x_config tda827x_lifeview_config = { .demod_address =0x08 , .invert =1 ,
@@ -1660,7 +1629,6 @@ static int dvb_init(struct saa7134_dev *dev) }break ;case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG : -__kworld_sbtvd_i2c_gate_ctrl (dev,0 );saa_writel (SAA7134_GPIO_GPMODE0 >>2 ,0x14000 );saa_writel (SAA7134_GPIO_GPSTATUS0 >>2 ,0x14000 );msleep (20 );
@@ -1670,16 +1638,10 @@ static int dvb_init(struct saa7134_dev *dev) fe0->dvb.frontend =dvb_attach (mb86a20s_attach, &kworld_mb86a20s_config, &dev->i2c_adap); -__kworld_sbtvd_i2c_gate_ctrl (dev,1 );if (fe0->dvb.frontend !=NULL ) {dvb_attach (tda18271_attach, fe0->dvb.frontend,0x60 , &dev->i2c_adap, &kworld_tda18271_config); -/* -* Only after success, it can initialize the gate, otherwise -* an OOPS will hit, due to kfree(fe0->dvb.frontend) -*/ - fe0->dvb.frontend->ops.i2c_gate_ctrl = kworld_sbtvd_i2c_gate_ctrl; }break ;default :
Signature · 1 line
1.7.1
PATCH 7/8 saa7134: Fix digital mode on Kworld SBTVD
In reply to a message not in the archiveThis patch fixes digital mode on Kworld SBTVD. Unfortunately, it disables analog mode.
drivers/media/video/saa7134/saa7134-cards.c +4 -1
@@ -5179,7 +5179,11 @@ struct saa7134_board saa7134_boards[] = { [SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG] = { .name = "Kworld PCI SBTVD/ISDB-T Full-Seg Hybrid", .audio_clock = 0x00187de7, +#if 0 .tuner_type = TUNER_PHILIPS_TDA8290, +#else + .tuner_type = UNSET, +#endif .tuner_addr = ADDR_UNSET, .radio_type = UNSET, .radio_addr = ADDR_UNSET,
@@ -5191,7 +5195,6 @@ struct saa7134_board saa7134_boards[] = { .vmux = 1, .amux = TV, .tv = 1, - .gpio = 0x4000,#if 0 /* FIXME */ }, { .name = name_comp1,
drivers/media/video/saa7134/saa7134-dvb.c +37 -1
@@ -236,13 +236,38 @@ static struct tda18271_std_map mb86a20s_tda18271_std_map = {static struct tda18271_config kworld_tda18271_config = { .std_map = &mb86a20s_tda18271_std_map, - .gate =TDA18271_GATE_ANALOG , + .gate =TDA18271_GATE_DIGITAL , };static const struct mb86a20s_config kworld_mb86a20s_config = { .demod_address =0x10 , }; +static int kworld_sbtvd_gate_ctrl (struct dvb_frontend * fe,int enable) +{ +struct saa7134_dev *dev = fe->dvb->priv; + +unsigned char initmsg[] = {0x45 ,0x97 }; +unsigned char msg_enable[] = {0x45 ,0xc1 }; +unsigned char msg_disable[] = {0x45 ,0x81 }; +struct i2c_msg msg = {.addr =0x4b , .flags =0 , .buf = initmsg, .len =2 }; + +if (i2c_transfer (&dev->i2c_adap, &msg,1 ) !=1 ) { +wprintk ("could not access the I2C gate\n" ); +return -EIO ; + } +if (enable) + msg.buf = msg_enable; +else + msg.buf = msg_disable; +if (i2c_transfer (&dev->i2c_adap, &msg,1 ) !=1 ) { +wprintk ("could not access the I2C gate\n" ); +return -EIO ; + } +msleep (20 ); +return 0 ; +} +/* ================================================================== * tda1004x based DVB-T cards, helper functions */
@@ -1639,10 +1664,21 @@ static int dvb_init(struct saa7134_dev *dev) &kworld_mb86a20s_config, &dev->i2c_adap);if (fe0->dvb.frontend !=NULL ) { +#if 0 +dvb_attach (tda829x_attach, fe0->dvb.frontend, + &dev->i2c_adap,0x4b , + &tda829x_no_probe); +#else +dvb_attach (tda829x_attach, fe0->dvb.frontend, + &dev->i2c_adap,0x4b ,NULL ); +#endif dvb_attach (tda18271_attach, fe0->dvb.frontend,0x60 , &dev->i2c_adap, &kworld_tda18271_config); + fe0->dvb.frontend->ops.i2c_gate_ctrl = kworld_sbtvd_gate_ctrl; } + +/* mb86a20s need to use the I2C gateway */ break ;default :wprintk ("Huh? unknown DVB card?\n" );
Signature · 1 line
1.7.1
PATCH 8/8 saa7134: Kworld SBTVD: make both analog and digital to work
In reply to a message not in the archiveThere are some weird bugs at tda8290/tda18271 initialization, as it insits do do analog initialization during DVB frontend attach:
DVB: registering new adapter (saa7133[0])
DVB: registering adapter 0 frontend 0 (Fujitsu mb86A20s)...
mb86a20s: mb86a20s_initfetda18271_write_regs: [2-0060|M] ERROR: idx = 0x5, len = 1, i2c_transfer returned: -5tda18271_init: [2-0060|M] error -5 on line 830tda18271_tune: [2-0060|M] error -5 on line 908tda18271_write_regstda18271_write_regs: [2-0060|M] ERROR: idx = 0x5, len = 1, i2c_transfer returned: -5 tda18271c2_rf_tracking_filters_correction: [2-0060|M] error -5 on line 265 tda18271_write_regstda18271_write_regs: [2-0060|M] ERROR: idx = 0x25, len = 1, i2c_transfer returned: -5 tda18271_channel_configuration: [2-0060|M] error -5 on line 119 tda18271_set_analog_params: [2-0060|M] error -5 on line 1045 tda18271_set_analog_params: [2-0060|M] error -5 on line 1045
tda829x 2-004b: tda8295 not locked, no signal?
tda829x 2-004b: tda8295_i2c_bridge: disable i2c gate
tda829x 2-004b: tda8295 not locked, no signal?
tda829x 2-004b: tda8295_i2c_bridge: disable i2c gatemb86a20s_i2c_writereg: writereg error (rc == -5, reg == 0x29, data == 0x33)
mb86a20s: Init failed. Will try again later
The problem is that mb86a20s is only visible if the analog part is disabled.
However, due to a trick at mb86a20s, it will later initialize properly:
mb86a20s: mb86a20s_initfe: Initialization succeded.
This is hacky and ugly. However, I coldn't find any easy way to fix it. A proper fix would be to have a resource locking schema, used by both V4L and DVB parts that would block access to analog registers while digital registers are in use, but this will probably put tda829x into a dead lock.
drivers/media/common/tuners/tda8290.c +1 -0
@@ -232,6 +232,7 @@ static void tda8290_set_params(struct dvb_frontend *fe,tuner_i2c_xfer_send (&priv->i2c_props, pll_bw_nom,2 ); } +tda8290_i2c_bridge (fe,1 );if (fe->ops.tuner_ops.set_analog_params)
drivers/media/video/saa7134/saa7134-cards.c +10 -6
@@ -5179,11 +5179,7 @@ struct saa7134_board saa7134_boards[] = { [SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG] = { .name = "Kworld PCI SBTVD/ISDB-T Full-Seg Hybrid", .audio_clock = 0x00187de7, -#if 0 .tuner_type = TUNER_PHILIPS_TDA8290, -#else - .tuner_type = UNSET, -#endif .tuner_addr = ADDR_UNSET, .radio_type = UNSET, .radio_addr = ADDR_UNSET,
@@ -6926,10 +6922,17 @@ static inline int saa7134_kworld_sbtvd_toggle_agc(struct saa7134_dev *dev,/* toggle AGC switch through GPIO 27 */ switch (mode) {case TDA18271_ANALOG : -saa7134_set_gpio (dev,27 ,0 ); +saa_writel (SAA7134_GPIO_GPMODE0 >>2 ,0x4000 ); +saa_writel (SAA7134_GPIO_GPSTATUS0 >>2 ,0x4000 ); +msleep (20 );break ;case TDA18271_DIGITAL : -saa7134_set_gpio (dev,27 ,1 ); +saa_writel (SAA7134_GPIO_GPMODE0 >>2 ,0x14000 ); +saa_writel (SAA7134_GPIO_GPSTATUS0 >>2 ,0x14000 ); +msleep (20 ); +saa_writel (SAA7134_GPIO_GPMODE0 >>2 ,0x54000 ); +saa_writel (SAA7134_GPIO_GPSTATUS0 >>2 ,0x54000 ); +msleep (30 );break ;default :return -EINVAL ;
@@ -6987,6 +6990,7 @@ static int saa7134_tda8290_callback(struct saa7134_dev *dev,int saa7134_tuner_callback (void *priv,int component,int command,int arg) {struct saa7134_dev *dev = priv; +if (dev !=NULL ) {switch (dev->tuner_type) {case TUNER_PHILIPS_TDA8290 :
drivers/media/video/saa7134/saa7134-dvb.c +5 -11
@@ -237,6 +237,8 @@ static struct tda18271_std_map mb86a20s_tda18271_std_map = {static struct tda18271_config kworld_tda18271_config = { .std_map = &mb86a20s_tda18271_std_map, .gate =TDA18271_GATE_DIGITAL , + .config =3 ,/* Use tuner callback for AGC */ + };static const struct mb86a20s_config kworld_mb86a20s_config = {
@@ -1654,24 +1656,16 @@ static int dvb_init(struct saa7134_dev *dev) }break ;case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG : -saa_writel (SAA7134_GPIO_GPMODE0 >>2 ,0x14000 ); -saa_writel (SAA7134_GPIO_GPSTATUS0 >>2 ,0x14000 ); -msleep (20 ); -saa_writel (SAA7134_GPIO_GPMODE0 >>2 ,0x54000 ); -saa_writel (SAA7134_GPIO_GPSTATUS0 >>2 ,0x54000 ); -msleep (20 ); +/* Switch to digital mode */ +saa7134_tuner_callback (dev,0 , +TDA18271_CALLBACK_CMD_AGC_ENABLE ,1 ); fe0->dvb.frontend =dvb_attach (mb86a20s_attach, &kworld_mb86a20s_config, &dev->i2c_adap);if (fe0->dvb.frontend !=NULL ) { -#if 0 dvb_attach (tda829x_attach, fe0->dvb.frontend, &dev->i2c_adap,0x4b , &tda829x_no_probe); -#else -dvb_attach (tda829x_attach, fe0->dvb.frontend, - &dev->i2c_adap,0x4b ,NULL ); -#endif dvb_attach (tda18271_attach, fe0->dvb.frontend,0x60 , &dev->i2c_adap, &kworld_tda18271_config);
Signature · 1 line
1.7.1
Re: [PATCH 8/8] [media] saa7134: Kworld SBTVD: make both analog and digital to work
drivers/media/common/tuners/tda8290.c static void tda8290_set_params(struct dvb_frontend *fe, · line 236
Quoted 13 lines · ext Mauro Carvalho Chehab
There are some weird bugs at tda8290/tda18271 initialization, as it insits do do analog initialization during DVB frontend attach:
drivers/media/common/tuners/tda8290.c +1
-0@@ -232,6 +232,7 @@ static void tda8290_set_params(struct dvb_frontend *fe,tuner_i2c_xfer_send (&priv->i2c_props, pll_bw_nom,2 ); } +tda8290_i2c_bridge (fe,1 );
Don't add a second empty line here...
Cheers,
Matti