162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * i2c tv tuner chip device driver 462306a36Sopenharmony_ci * controls microtune tuners, mt2032 + mt2050 at the moment. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This "mt20xx" module was split apart from the original "tuner" module. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/i2c.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/videodev2.h> 1262306a36Sopenharmony_ci#include "tuner-i2c.h" 1362306a36Sopenharmony_ci#include "mt20xx.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic int debug; 1662306a36Sopenharmony_cimodule_param(debug, int, 0644); 1762306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "enable verbose debug messages"); 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic unsigned int optimize_vco = 1; 2262306a36Sopenharmony_cimodule_param(optimize_vco, int, 0644); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic unsigned int tv_antenna = 1; 2562306a36Sopenharmony_cimodule_param(tv_antenna, int, 0644); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic unsigned int radio_antenna; 2862306a36Sopenharmony_cimodule_param(radio_antenna, int, 0644); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define MT2032 0x04 3362306a36Sopenharmony_ci#define MT2030 0x06 3462306a36Sopenharmony_ci#define MT2040 0x07 3562306a36Sopenharmony_ci#define MT2050 0x42 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic char *microtune_part[] = { 3862306a36Sopenharmony_ci [ MT2030 ] = "MT2030", 3962306a36Sopenharmony_ci [ MT2032 ] = "MT2032", 4062306a36Sopenharmony_ci [ MT2040 ] = "MT2040", 4162306a36Sopenharmony_ci [ MT2050 ] = "MT2050", 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct microtune_priv { 4562306a36Sopenharmony_ci struct tuner_i2c_props i2c_props; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci unsigned int xogc; 4862306a36Sopenharmony_ci //unsigned int radio_if2; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci u32 frequency; 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void microtune_release(struct dvb_frontend *fe) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci kfree(fe->tuner_priv); 5662306a36Sopenharmony_ci fe->tuner_priv = NULL; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic int microtune_get_frequency(struct dvb_frontend *fe, u32 *frequency) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct microtune_priv *priv = fe->tuner_priv; 6262306a36Sopenharmony_ci *frequency = priv->frequency; 6362306a36Sopenharmony_ci return 0; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci// IsSpurInBand()? 6762306a36Sopenharmony_cistatic int mt2032_spurcheck(struct dvb_frontend *fe, 6862306a36Sopenharmony_ci int f1, int f2, int spectrum_from,int spectrum_to) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct microtune_priv *priv = fe->tuner_priv; 7162306a36Sopenharmony_ci int n1=1,n2,f; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci f1=f1/1000; //scale to kHz to avoid 32bit overflows 7462306a36Sopenharmony_ci f2=f2/1000; 7562306a36Sopenharmony_ci spectrum_from/=1000; 7662306a36Sopenharmony_ci spectrum_to/=1000; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci tuner_dbg("spurcheck f1=%d f2=%d from=%d to=%d\n", 7962306a36Sopenharmony_ci f1,f2,spectrum_from,spectrum_to); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci do { 8262306a36Sopenharmony_ci n2=-n1; 8362306a36Sopenharmony_ci f=n1*(f1-f2); 8462306a36Sopenharmony_ci do { 8562306a36Sopenharmony_ci n2--; 8662306a36Sopenharmony_ci f=f-f2; 8762306a36Sopenharmony_ci tuner_dbg("spurtest n1=%d n2=%d ftest=%d\n",n1,n2,f); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if( (f>spectrum_from) && (f<spectrum_to)) 9062306a36Sopenharmony_ci tuner_dbg("mt2032 spurcheck triggered: %d\n",n1); 9162306a36Sopenharmony_ci } while ( (f>(f2-spectrum_to)) || (n2>-5)); 9262306a36Sopenharmony_ci n1++; 9362306a36Sopenharmony_ci } while (n1<5); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return 1; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int mt2032_compute_freq(struct dvb_frontend *fe, 9962306a36Sopenharmony_ci unsigned int rfin, 10062306a36Sopenharmony_ci unsigned int if1, unsigned int if2, 10162306a36Sopenharmony_ci unsigned int spectrum_from, 10262306a36Sopenharmony_ci unsigned int spectrum_to, 10362306a36Sopenharmony_ci unsigned char *buf, 10462306a36Sopenharmony_ci int *ret_sel, 10562306a36Sopenharmony_ci unsigned int xogc) //all in Hz 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct microtune_priv *priv = fe->tuner_priv; 10862306a36Sopenharmony_ci unsigned int fref,lo1,lo1n,lo1a,s,sel,lo1freq, desired_lo1, 10962306a36Sopenharmony_ci desired_lo2,lo2,lo2n,lo2a,lo2num,lo2freq; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci fref= 5250 *1000; //5.25MHz 11262306a36Sopenharmony_ci desired_lo1=rfin+if1; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci lo1=(2*(desired_lo1/1000)+(fref/1000)) / (2*fref/1000); 11562306a36Sopenharmony_ci lo1n=lo1/8; 11662306a36Sopenharmony_ci lo1a=lo1-(lo1n*8); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci s=rfin/1000/1000+1090; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if(optimize_vco) { 12162306a36Sopenharmony_ci if(s>1890) sel=0; 12262306a36Sopenharmony_ci else if(s>1720) sel=1; 12362306a36Sopenharmony_ci else if(s>1530) sel=2; 12462306a36Sopenharmony_ci else if(s>1370) sel=3; 12562306a36Sopenharmony_ci else sel=4; // >1090 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci else { 12862306a36Sopenharmony_ci if(s>1790) sel=0; // <1958 12962306a36Sopenharmony_ci else if(s>1617) sel=1; 13062306a36Sopenharmony_ci else if(s>1449) sel=2; 13162306a36Sopenharmony_ci else if(s>1291) sel=3; 13262306a36Sopenharmony_ci else sel=4; // >1090 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci *ret_sel=sel; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci lo1freq=(lo1a+8*lo1n)*fref; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci tuner_dbg("mt2032: rfin=%d lo1=%d lo1n=%d lo1a=%d sel=%d, lo1freq=%d\n", 13962306a36Sopenharmony_ci rfin,lo1,lo1n,lo1a,sel,lo1freq); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci desired_lo2=lo1freq-rfin-if2; 14262306a36Sopenharmony_ci lo2=(desired_lo2)/fref; 14362306a36Sopenharmony_ci lo2n=lo2/8; 14462306a36Sopenharmony_ci lo2a=lo2-(lo2n*8); 14562306a36Sopenharmony_ci lo2num=((desired_lo2/1000)%(fref/1000))* 3780/(fref/1000); //scale to fit in 32bit arith 14662306a36Sopenharmony_ci lo2freq=(lo2a+8*lo2n)*fref + lo2num*(fref/1000)/3780*1000; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci tuner_dbg("mt2032: rfin=%d lo2=%d lo2n=%d lo2a=%d num=%d lo2freq=%d\n", 14962306a36Sopenharmony_ci rfin,lo2,lo2n,lo2a,lo2num,lo2freq); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (lo1a > 7 || lo1n < 17 || lo1n > 48 || lo2a > 7 || lo2n < 17 || 15262306a36Sopenharmony_ci lo2n > 30) { 15362306a36Sopenharmony_ci tuner_info("mt2032: frequency parameters out of range: %d %d %d %d\n", 15462306a36Sopenharmony_ci lo1a, lo1n, lo2a,lo2n); 15562306a36Sopenharmony_ci return(-1); 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci mt2032_spurcheck(fe, lo1freq, desired_lo2, spectrum_from, spectrum_to); 15962306a36Sopenharmony_ci // should recalculate lo1 (one step up/down) 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci // set up MT2032 register map for transfer over i2c 16262306a36Sopenharmony_ci buf[0]=lo1n-1; 16362306a36Sopenharmony_ci buf[1]=lo1a | (sel<<4); 16462306a36Sopenharmony_ci buf[2]=0x86; // LOGC 16562306a36Sopenharmony_ci buf[3]=0x0f; //reserved 16662306a36Sopenharmony_ci buf[4]=0x1f; 16762306a36Sopenharmony_ci buf[5]=(lo2n-1) | (lo2a<<5); 16862306a36Sopenharmony_ci if(rfin >400*1000*1000) 16962306a36Sopenharmony_ci buf[6]=0xe4; 17062306a36Sopenharmony_ci else 17162306a36Sopenharmony_ci buf[6]=0xf4; // set PKEN per rev 1.2 17262306a36Sopenharmony_ci buf[7]=8+xogc; 17362306a36Sopenharmony_ci buf[8]=0xc3; //reserved 17462306a36Sopenharmony_ci buf[9]=0x4e; //reserved 17562306a36Sopenharmony_ci buf[10]=0xec; //reserved 17662306a36Sopenharmony_ci buf[11]=(lo2num&0xff); 17762306a36Sopenharmony_ci buf[12]=(lo2num>>8) |0x80; // Lo2RST 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int mt2032_check_lo_lock(struct dvb_frontend *fe) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct microtune_priv *priv = fe->tuner_priv; 18562306a36Sopenharmony_ci int try,lock=0; 18662306a36Sopenharmony_ci unsigned char buf[2]; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci for(try=0;try<10;try++) { 18962306a36Sopenharmony_ci buf[0]=0x0e; 19062306a36Sopenharmony_ci tuner_i2c_xfer_send(&priv->i2c_props,buf,1); 19162306a36Sopenharmony_ci tuner_i2c_xfer_recv(&priv->i2c_props,buf,1); 19262306a36Sopenharmony_ci tuner_dbg("mt2032 Reg.E=0x%02x\n",buf[0]); 19362306a36Sopenharmony_ci lock=buf[0] &0x06; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (lock==6) 19662306a36Sopenharmony_ci break; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci tuner_dbg("mt2032: pll wait 1ms for lock (0x%2x)\n",buf[0]); 19962306a36Sopenharmony_ci udelay(1000); 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci return lock; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int mt2032_optimize_vco(struct dvb_frontend *fe,int sel,int lock) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct microtune_priv *priv = fe->tuner_priv; 20762306a36Sopenharmony_ci unsigned char buf[2]; 20862306a36Sopenharmony_ci int tad1; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci buf[0]=0x0f; 21162306a36Sopenharmony_ci tuner_i2c_xfer_send(&priv->i2c_props,buf,1); 21262306a36Sopenharmony_ci tuner_i2c_xfer_recv(&priv->i2c_props,buf,1); 21362306a36Sopenharmony_ci tuner_dbg("mt2032 Reg.F=0x%02x\n",buf[0]); 21462306a36Sopenharmony_ci tad1=buf[0]&0x07; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if(tad1 ==0) return lock; 21762306a36Sopenharmony_ci if(tad1 ==1) return lock; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if(tad1==2) { 22062306a36Sopenharmony_ci if(sel==0) 22162306a36Sopenharmony_ci return lock; 22262306a36Sopenharmony_ci else sel--; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci else { 22562306a36Sopenharmony_ci if(sel<4) 22662306a36Sopenharmony_ci sel++; 22762306a36Sopenharmony_ci else 22862306a36Sopenharmony_ci return lock; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci tuner_dbg("mt2032 optimize_vco: sel=%d\n",sel); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci buf[0]=0x0f; 23462306a36Sopenharmony_ci buf[1]=sel; 23562306a36Sopenharmony_ci tuner_i2c_xfer_send(&priv->i2c_props,buf,2); 23662306a36Sopenharmony_ci lock=mt2032_check_lo_lock(fe); 23762306a36Sopenharmony_ci return lock; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic void mt2032_set_if_freq(struct dvb_frontend *fe, unsigned int rfin, 24262306a36Sopenharmony_ci unsigned int if1, unsigned int if2, 24362306a36Sopenharmony_ci unsigned int from, unsigned int to) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci unsigned char buf[21]; 24662306a36Sopenharmony_ci int lint_try,ret,sel,lock=0; 24762306a36Sopenharmony_ci struct microtune_priv *priv = fe->tuner_priv; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci tuner_dbg("mt2032_set_if_freq rfin=%d if1=%d if2=%d from=%d to=%d\n", 25062306a36Sopenharmony_ci rfin,if1,if2,from,to); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci buf[0]=0; 25362306a36Sopenharmony_ci ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,1); 25462306a36Sopenharmony_ci tuner_i2c_xfer_recv(&priv->i2c_props,buf,21); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci buf[0]=0; 25762306a36Sopenharmony_ci ret=mt2032_compute_freq(fe,rfin,if1,if2,from,to,&buf[1],&sel,priv->xogc); 25862306a36Sopenharmony_ci if (ret<0) 25962306a36Sopenharmony_ci return; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci // send only the relevant registers per Rev. 1.2 26262306a36Sopenharmony_ci buf[0]=0; 26362306a36Sopenharmony_ci ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,4); 26462306a36Sopenharmony_ci buf[5]=5; 26562306a36Sopenharmony_ci ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+5,4); 26662306a36Sopenharmony_ci buf[11]=11; 26762306a36Sopenharmony_ci ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+11,3); 26862306a36Sopenharmony_ci if(ret!=3) 26962306a36Sopenharmony_ci tuner_warn("i2c i/o error: rc == %d (should be 3)\n",ret); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci // wait for PLLs to lock (per manual), retry LINT if not. 27262306a36Sopenharmony_ci for(lint_try=0; lint_try<2; lint_try++) { 27362306a36Sopenharmony_ci lock=mt2032_check_lo_lock(fe); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if(optimize_vco) 27662306a36Sopenharmony_ci lock=mt2032_optimize_vco(fe,sel,lock); 27762306a36Sopenharmony_ci if(lock==6) break; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci tuner_dbg("mt2032: re-init PLLs by LINT\n"); 28062306a36Sopenharmony_ci buf[0]=7; 28162306a36Sopenharmony_ci buf[1]=0x80 +8+priv->xogc; // set LINT to re-init PLLs 28262306a36Sopenharmony_ci tuner_i2c_xfer_send(&priv->i2c_props,buf,2); 28362306a36Sopenharmony_ci mdelay(10); 28462306a36Sopenharmony_ci buf[1]=8+priv->xogc; 28562306a36Sopenharmony_ci tuner_i2c_xfer_send(&priv->i2c_props,buf,2); 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (lock!=6) 28962306a36Sopenharmony_ci tuner_warn("MT2032 Fatal Error: PLLs didn't lock.\n"); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci buf[0]=2; 29262306a36Sopenharmony_ci buf[1]=0x20; // LOGC for optimal phase noise 29362306a36Sopenharmony_ci ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2); 29462306a36Sopenharmony_ci if (ret!=2) 29562306a36Sopenharmony_ci tuner_warn("i2c i/o error: rc == %d (should be 2)\n",ret); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int mt2032_set_tv_freq(struct dvb_frontend *fe, 30062306a36Sopenharmony_ci struct analog_parameters *params) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci int if2,from,to; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci // signal bandwidth and picture carrier 30562306a36Sopenharmony_ci if (params->std & V4L2_STD_525_60) { 30662306a36Sopenharmony_ci // NTSC 30762306a36Sopenharmony_ci from = 40750*1000; 30862306a36Sopenharmony_ci to = 46750*1000; 30962306a36Sopenharmony_ci if2 = 45750*1000; 31062306a36Sopenharmony_ci } else { 31162306a36Sopenharmony_ci // PAL 31262306a36Sopenharmony_ci from = 32900*1000; 31362306a36Sopenharmony_ci to = 39900*1000; 31462306a36Sopenharmony_ci if2 = 38900*1000; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci mt2032_set_if_freq(fe, params->frequency*62500, 31862306a36Sopenharmony_ci 1090*1000*1000, if2, from, to); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic int mt2032_set_radio_freq(struct dvb_frontend *fe, 32462306a36Sopenharmony_ci struct analog_parameters *params) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct microtune_priv *priv = fe->tuner_priv; 32762306a36Sopenharmony_ci int if2; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (params->std & V4L2_STD_525_60) { 33062306a36Sopenharmony_ci tuner_dbg("pinnacle ntsc\n"); 33162306a36Sopenharmony_ci if2 = 41300 * 1000; 33262306a36Sopenharmony_ci } else { 33362306a36Sopenharmony_ci tuner_dbg("pinnacle pal\n"); 33462306a36Sopenharmony_ci if2 = 33300 * 1000; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci // per Manual for FM tuning: first if center freq. 1085 MHz 33862306a36Sopenharmony_ci mt2032_set_if_freq(fe, params->frequency * 125 / 2, 33962306a36Sopenharmony_ci 1085*1000*1000,if2,if2,if2); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return 0; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic int mt2032_set_params(struct dvb_frontend *fe, 34562306a36Sopenharmony_ci struct analog_parameters *params) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci struct microtune_priv *priv = fe->tuner_priv; 34862306a36Sopenharmony_ci int ret = -EINVAL; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci switch (params->mode) { 35162306a36Sopenharmony_ci case V4L2_TUNER_RADIO: 35262306a36Sopenharmony_ci ret = mt2032_set_radio_freq(fe, params); 35362306a36Sopenharmony_ci priv->frequency = params->frequency * 125 / 2; 35462306a36Sopenharmony_ci break; 35562306a36Sopenharmony_ci case V4L2_TUNER_ANALOG_TV: 35662306a36Sopenharmony_ci case V4L2_TUNER_DIGITAL_TV: 35762306a36Sopenharmony_ci ret = mt2032_set_tv_freq(fe, params); 35862306a36Sopenharmony_ci priv->frequency = params->frequency * 62500; 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci return ret; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic const struct dvb_tuner_ops mt2032_tuner_ops = { 36662306a36Sopenharmony_ci .set_analog_params = mt2032_set_params, 36762306a36Sopenharmony_ci .release = microtune_release, 36862306a36Sopenharmony_ci .get_frequency = microtune_get_frequency, 36962306a36Sopenharmony_ci}; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci// Initialization as described in "MT203x Programming Procedures", Rev 1.2, Feb.2001 37262306a36Sopenharmony_cistatic int mt2032_init(struct dvb_frontend *fe) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct microtune_priv *priv = fe->tuner_priv; 37562306a36Sopenharmony_ci unsigned char buf[21]; 37662306a36Sopenharmony_ci int ret,xogc,xok=0; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci // Initialize Registers per spec. 37962306a36Sopenharmony_ci buf[1]=2; // Index to register 2 38062306a36Sopenharmony_ci buf[2]=0xff; 38162306a36Sopenharmony_ci buf[3]=0x0f; 38262306a36Sopenharmony_ci buf[4]=0x1f; 38362306a36Sopenharmony_ci ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+1,4); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci buf[5]=6; // Index register 6 38662306a36Sopenharmony_ci buf[6]=0xe4; 38762306a36Sopenharmony_ci buf[7]=0x8f; 38862306a36Sopenharmony_ci buf[8]=0xc3; 38962306a36Sopenharmony_ci buf[9]=0x4e; 39062306a36Sopenharmony_ci buf[10]=0xec; 39162306a36Sopenharmony_ci ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+5,6); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci buf[12]=13; // Index register 13 39462306a36Sopenharmony_ci buf[13]=0x32; 39562306a36Sopenharmony_ci ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+12,2); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci // Adjust XOGC (register 7), wait for XOK 39862306a36Sopenharmony_ci xogc=7; 39962306a36Sopenharmony_ci do { 40062306a36Sopenharmony_ci tuner_dbg("mt2032: xogc = 0x%02x\n",xogc&0x07); 40162306a36Sopenharmony_ci mdelay(10); 40262306a36Sopenharmony_ci buf[0]=0x0e; 40362306a36Sopenharmony_ci tuner_i2c_xfer_send(&priv->i2c_props,buf,1); 40462306a36Sopenharmony_ci tuner_i2c_xfer_recv(&priv->i2c_props,buf,1); 40562306a36Sopenharmony_ci xok=buf[0]&0x01; 40662306a36Sopenharmony_ci tuner_dbg("mt2032: xok = 0x%02x\n",xok); 40762306a36Sopenharmony_ci if (xok == 1) break; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci xogc--; 41062306a36Sopenharmony_ci tuner_dbg("mt2032: xogc = 0x%02x\n",xogc&0x07); 41162306a36Sopenharmony_ci if (xogc == 3) { 41262306a36Sopenharmony_ci xogc=4; // min. 4 per spec 41362306a36Sopenharmony_ci break; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci buf[0]=0x07; 41662306a36Sopenharmony_ci buf[1]=0x88 + xogc; 41762306a36Sopenharmony_ci ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2); 41862306a36Sopenharmony_ci if (ret!=2) 41962306a36Sopenharmony_ci tuner_warn("i2c i/o error: rc == %d (should be 2)\n",ret); 42062306a36Sopenharmony_ci } while (xok != 1 ); 42162306a36Sopenharmony_ci priv->xogc=xogc; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci memcpy(&fe->ops.tuner_ops, &mt2032_tuner_ops, sizeof(struct dvb_tuner_ops)); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return(1); 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic void mt2050_set_antenna(struct dvb_frontend *fe, unsigned char antenna) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct microtune_priv *priv = fe->tuner_priv; 43162306a36Sopenharmony_ci unsigned char buf[2]; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci buf[0] = 6; 43462306a36Sopenharmony_ci buf[1] = antenna ? 0x11 : 0x10; 43562306a36Sopenharmony_ci tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); 43662306a36Sopenharmony_ci tuner_dbg("mt2050: enabled antenna connector %d\n", antenna); 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic void mt2050_set_if_freq(struct dvb_frontend *fe,unsigned int freq, unsigned int if2) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci struct microtune_priv *priv = fe->tuner_priv; 44262306a36Sopenharmony_ci unsigned int if1=1218*1000*1000; 44362306a36Sopenharmony_ci unsigned int f_lo1,f_lo2,lo1,lo2,f_lo1_modulo,f_lo2_modulo,num1,num2,div1a,div1b,div2a,div2b; 44462306a36Sopenharmony_ci int ret; 44562306a36Sopenharmony_ci unsigned char buf[6]; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci tuner_dbg("mt2050_set_if_freq freq=%d if1=%d if2=%d\n", 44862306a36Sopenharmony_ci freq,if1,if2); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci f_lo1=freq+if1; 45162306a36Sopenharmony_ci f_lo1=(f_lo1/1000000)*1000000; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci f_lo2=f_lo1-freq-if2; 45462306a36Sopenharmony_ci f_lo2=(f_lo2/50000)*50000; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci lo1=f_lo1/4000000; 45762306a36Sopenharmony_ci lo2=f_lo2/4000000; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci f_lo1_modulo= f_lo1-(lo1*4000000); 46062306a36Sopenharmony_ci f_lo2_modulo= f_lo2-(lo2*4000000); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci num1=4*f_lo1_modulo/4000000; 46362306a36Sopenharmony_ci num2=4096*(f_lo2_modulo/1000)/4000; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci // todo spurchecks 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci div1a=(lo1/12)-1; 46862306a36Sopenharmony_ci div1b=lo1-(div1a+1)*12; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci div2a=(lo2/8)-1; 47162306a36Sopenharmony_ci div2b=lo2-(div2a+1)*8; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (debug > 1) { 47462306a36Sopenharmony_ci tuner_dbg("lo1 lo2 = %d %d\n", lo1, lo2); 47562306a36Sopenharmony_ci tuner_dbg("num1 num2 div1a div1b div2a div2b= %x %x %x %x %x %x\n", 47662306a36Sopenharmony_ci num1,num2,div1a,div1b,div2a,div2b); 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci buf[0]=1; 48062306a36Sopenharmony_ci buf[1]= 4*div1b + num1; 48162306a36Sopenharmony_ci if(freq<275*1000*1000) buf[1] = buf[1]|0x80; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci buf[2]=div1a; 48462306a36Sopenharmony_ci buf[3]=32*div2b + num2/256; 48562306a36Sopenharmony_ci buf[4]=num2-(num2/256)*256; 48662306a36Sopenharmony_ci buf[5]=div2a; 48762306a36Sopenharmony_ci if(num2!=0) buf[5]=buf[5]|0x40; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (debug > 1) 49062306a36Sopenharmony_ci tuner_dbg("bufs is: %*ph\n", 6, buf); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,6); 49362306a36Sopenharmony_ci if (ret!=6) 49462306a36Sopenharmony_ci tuner_warn("i2c i/o error: rc == %d (should be 6)\n",ret); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic int mt2050_set_tv_freq(struct dvb_frontend *fe, 49862306a36Sopenharmony_ci struct analog_parameters *params) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci unsigned int if2; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (params->std & V4L2_STD_525_60) { 50362306a36Sopenharmony_ci // NTSC 50462306a36Sopenharmony_ci if2 = 45750*1000; 50562306a36Sopenharmony_ci } else { 50662306a36Sopenharmony_ci // PAL 50762306a36Sopenharmony_ci if2 = 38900*1000; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci if (V4L2_TUNER_DIGITAL_TV == params->mode) { 51062306a36Sopenharmony_ci // DVB (pinnacle 300i) 51162306a36Sopenharmony_ci if2 = 36150*1000; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci mt2050_set_if_freq(fe, params->frequency*62500, if2); 51462306a36Sopenharmony_ci mt2050_set_antenna(fe, tv_antenna); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci return 0; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic int mt2050_set_radio_freq(struct dvb_frontend *fe, 52062306a36Sopenharmony_ci struct analog_parameters *params) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct microtune_priv *priv = fe->tuner_priv; 52362306a36Sopenharmony_ci int if2; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (params->std & V4L2_STD_525_60) { 52662306a36Sopenharmony_ci tuner_dbg("pinnacle ntsc\n"); 52762306a36Sopenharmony_ci if2 = 41300 * 1000; 52862306a36Sopenharmony_ci } else { 52962306a36Sopenharmony_ci tuner_dbg("pinnacle pal\n"); 53062306a36Sopenharmony_ci if2 = 33300 * 1000; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci mt2050_set_if_freq(fe, params->frequency * 125 / 2, if2); 53462306a36Sopenharmony_ci mt2050_set_antenna(fe, radio_antenna); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci return 0; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic int mt2050_set_params(struct dvb_frontend *fe, 54062306a36Sopenharmony_ci struct analog_parameters *params) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci struct microtune_priv *priv = fe->tuner_priv; 54362306a36Sopenharmony_ci int ret = -EINVAL; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci switch (params->mode) { 54662306a36Sopenharmony_ci case V4L2_TUNER_RADIO: 54762306a36Sopenharmony_ci ret = mt2050_set_radio_freq(fe, params); 54862306a36Sopenharmony_ci priv->frequency = params->frequency * 125 / 2; 54962306a36Sopenharmony_ci break; 55062306a36Sopenharmony_ci case V4L2_TUNER_ANALOG_TV: 55162306a36Sopenharmony_ci case V4L2_TUNER_DIGITAL_TV: 55262306a36Sopenharmony_ci ret = mt2050_set_tv_freq(fe, params); 55362306a36Sopenharmony_ci priv->frequency = params->frequency * 62500; 55462306a36Sopenharmony_ci break; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci return ret; 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic const struct dvb_tuner_ops mt2050_tuner_ops = { 56162306a36Sopenharmony_ci .set_analog_params = mt2050_set_params, 56262306a36Sopenharmony_ci .release = microtune_release, 56362306a36Sopenharmony_ci .get_frequency = microtune_get_frequency, 56462306a36Sopenharmony_ci}; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic int mt2050_init(struct dvb_frontend *fe) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct microtune_priv *priv = fe->tuner_priv; 56962306a36Sopenharmony_ci unsigned char buf[2]; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci buf[0] = 6; 57262306a36Sopenharmony_ci buf[1] = 0x10; 57362306a36Sopenharmony_ci tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); /* power */ 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci buf[0] = 0x0f; 57662306a36Sopenharmony_ci buf[1] = 0x0f; 57762306a36Sopenharmony_ci tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); /* m1lo */ 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci buf[0] = 0x0d; 58062306a36Sopenharmony_ci tuner_i2c_xfer_send(&priv->i2c_props, buf, 1); 58162306a36Sopenharmony_ci tuner_i2c_xfer_recv(&priv->i2c_props, buf, 1); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci tuner_dbg("mt2050: sro is %x\n", buf[0]); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci memcpy(&fe->ops.tuner_ops, &mt2050_tuner_ops, sizeof(struct dvb_tuner_ops)); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistruct dvb_frontend *microtune_attach(struct dvb_frontend *fe, 59162306a36Sopenharmony_ci struct i2c_adapter* i2c_adap, 59262306a36Sopenharmony_ci u8 i2c_addr) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci struct microtune_priv *priv = NULL; 59562306a36Sopenharmony_ci char *name; 59662306a36Sopenharmony_ci unsigned char buf[21]; 59762306a36Sopenharmony_ci int company_code; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci priv = kzalloc(sizeof(struct microtune_priv), GFP_KERNEL); 60062306a36Sopenharmony_ci if (priv == NULL) 60162306a36Sopenharmony_ci return NULL; 60262306a36Sopenharmony_ci fe->tuner_priv = priv; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci priv->i2c_props.addr = i2c_addr; 60562306a36Sopenharmony_ci priv->i2c_props.adap = i2c_adap; 60662306a36Sopenharmony_ci priv->i2c_props.name = "mt20xx"; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci //priv->radio_if2 = 10700 * 1000; /* 10.7MHz - FM radio */ 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci memset(buf,0,sizeof(buf)); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci name = "unknown"; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci tuner_i2c_xfer_send(&priv->i2c_props,buf,1); 61562306a36Sopenharmony_ci tuner_i2c_xfer_recv(&priv->i2c_props,buf,21); 61662306a36Sopenharmony_ci if (debug) 61762306a36Sopenharmony_ci tuner_dbg("MT20xx hexdump: %*ph\n", 21, buf); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci company_code = buf[0x11] << 8 | buf[0x12]; 62062306a36Sopenharmony_ci tuner_info("microtune: companycode=%04x part=%02x rev=%02x\n", 62162306a36Sopenharmony_ci company_code,buf[0x13],buf[0x14]); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (buf[0x13] < ARRAY_SIZE(microtune_part) && 62562306a36Sopenharmony_ci NULL != microtune_part[buf[0x13]]) 62662306a36Sopenharmony_ci name = microtune_part[buf[0x13]]; 62762306a36Sopenharmony_ci switch (buf[0x13]) { 62862306a36Sopenharmony_ci case MT2032: 62962306a36Sopenharmony_ci mt2032_init(fe); 63062306a36Sopenharmony_ci break; 63162306a36Sopenharmony_ci case MT2050: 63262306a36Sopenharmony_ci mt2050_init(fe); 63362306a36Sopenharmony_ci break; 63462306a36Sopenharmony_ci default: 63562306a36Sopenharmony_ci tuner_info("microtune %s found, not (yet?) supported, sorry :-/\n", 63662306a36Sopenharmony_ci name); 63762306a36Sopenharmony_ci return NULL; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci strscpy(fe->ops.tuner_ops.info.name, name, 64162306a36Sopenharmony_ci sizeof(fe->ops.tuner_ops.info.name)); 64262306a36Sopenharmony_ci tuner_info("microtune %s found, OK\n",name); 64362306a36Sopenharmony_ci return fe; 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(microtune_attach); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ciMODULE_DESCRIPTION("Microtune tuner driver"); 64962306a36Sopenharmony_ciMODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); 65062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 651