162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// For Philips TEA5767 FM Chip used on some TV Cards like Prolink Pixelview 362306a36Sopenharmony_ci// I2C address is always 0xC0. 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (c) 2005 Mauro Carvalho Chehab <mchehab@kernel.org> 662306a36Sopenharmony_ci// 762306a36Sopenharmony_ci// tea5767 autodetection thanks to Torsten Seeboth and Atsushi Nakagawa 862306a36Sopenharmony_ci// from their contributions on DScaler. 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/i2c.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/videodev2.h> 1662306a36Sopenharmony_ci#include "tuner-i2c.h" 1762306a36Sopenharmony_ci#include "tea5767.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic int debug; 2062306a36Sopenharmony_cimodule_param(debug, int, 0644); 2162306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "enable verbose debug messages"); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/*****************************************************************************/ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct tea5767_priv { 2662306a36Sopenharmony_ci struct tuner_i2c_props i2c_props; 2762306a36Sopenharmony_ci u32 frequency; 2862306a36Sopenharmony_ci struct tea5767_ctrl ctrl; 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/*****************************************************************************/ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/****************************** 3462306a36Sopenharmony_ci * Write mode register values * 3562306a36Sopenharmony_ci ******************************/ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* First register */ 3862306a36Sopenharmony_ci#define TEA5767_MUTE 0x80 /* Mutes output */ 3962306a36Sopenharmony_ci#define TEA5767_SEARCH 0x40 /* Activates station search */ 4062306a36Sopenharmony_ci/* Bits 0-5 for divider MSB */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* Second register */ 4362306a36Sopenharmony_ci/* Bits 0-7 for divider LSB */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* Third register */ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Station search from botton to up */ 4862306a36Sopenharmony_ci#define TEA5767_SEARCH_UP 0x80 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* Searches with ADC output = 10 */ 5162306a36Sopenharmony_ci#define TEA5767_SRCH_HIGH_LVL 0x60 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* Searches with ADC output = 10 */ 5462306a36Sopenharmony_ci#define TEA5767_SRCH_MID_LVL 0x40 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* Searches with ADC output = 5 */ 5762306a36Sopenharmony_ci#define TEA5767_SRCH_LOW_LVL 0x20 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* if on, div=4*(Frf+Fif)/Fref otherwise, div=4*(Frf-Fif)/Freq) */ 6062306a36Sopenharmony_ci#define TEA5767_HIGH_LO_INJECT 0x10 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* Disable stereo */ 6362306a36Sopenharmony_ci#define TEA5767_MONO 0x08 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* Disable right channel and turns to mono */ 6662306a36Sopenharmony_ci#define TEA5767_MUTE_RIGHT 0x04 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* Disable left channel and turns to mono */ 6962306a36Sopenharmony_ci#define TEA5767_MUTE_LEFT 0x02 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define TEA5767_PORT1_HIGH 0x01 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* Fourth register */ 7462306a36Sopenharmony_ci#define TEA5767_PORT2_HIGH 0x80 7562306a36Sopenharmony_ci/* Chips stops working. Only I2C bus remains on */ 7662306a36Sopenharmony_ci#define TEA5767_STDBY 0x40 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* Japan freq (76-108 MHz. If disabled, 87.5-108 MHz */ 7962306a36Sopenharmony_ci#define TEA5767_JAPAN_BAND 0x20 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* Unselected means 32.768 KHz freq as reference. Otherwise Xtal at 13 MHz */ 8262306a36Sopenharmony_ci#define TEA5767_XTAL_32768 0x10 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* Cuts weak signals */ 8562306a36Sopenharmony_ci#define TEA5767_SOFT_MUTE 0x08 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* Activates high cut control */ 8862306a36Sopenharmony_ci#define TEA5767_HIGH_CUT_CTRL 0x04 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* Activates stereo noise control */ 9162306a36Sopenharmony_ci#define TEA5767_ST_NOISE_CTL 0x02 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* If activate PORT 1 indicates SEARCH or else it is used as PORT1 */ 9462306a36Sopenharmony_ci#define TEA5767_SRCH_IND 0x01 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* Fifth register */ 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* By activating, it will use Xtal at 13 MHz as reference for divider */ 9962306a36Sopenharmony_ci#define TEA5767_PLLREF_ENABLE 0x80 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* By activating, deemphasis=50, or else, deemphasis of 50us */ 10262306a36Sopenharmony_ci#define TEA5767_DEEMPH_75 0X40 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/***************************** 10562306a36Sopenharmony_ci * Read mode register values * 10662306a36Sopenharmony_ci *****************************/ 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* First register */ 10962306a36Sopenharmony_ci#define TEA5767_READY_FLAG_MASK 0x80 11062306a36Sopenharmony_ci#define TEA5767_BAND_LIMIT_MASK 0X40 11162306a36Sopenharmony_ci/* Bits 0-5 for divider MSB after search or preset */ 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* Second register */ 11462306a36Sopenharmony_ci/* Bits 0-7 for divider LSB after search or preset */ 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* Third register */ 11762306a36Sopenharmony_ci#define TEA5767_STEREO_MASK 0x80 11862306a36Sopenharmony_ci#define TEA5767_IF_CNTR_MASK 0x7f 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/* Fourth register */ 12162306a36Sopenharmony_ci#define TEA5767_ADC_LEVEL_MASK 0xf0 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* should be 0 */ 12462306a36Sopenharmony_ci#define TEA5767_CHIP_ID_MASK 0x0f 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* Fifth register */ 12762306a36Sopenharmony_ci/* Reserved for future extensions */ 12862306a36Sopenharmony_ci#define TEA5767_RESERVED_MASK 0xff 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/*****************************************************************************/ 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic void tea5767_status_dump(struct tea5767_priv *priv, 13362306a36Sopenharmony_ci unsigned char *buffer) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci unsigned int div, frq; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (TEA5767_READY_FLAG_MASK & buffer[0]) 13862306a36Sopenharmony_ci tuner_info("Ready Flag ON\n"); 13962306a36Sopenharmony_ci else 14062306a36Sopenharmony_ci tuner_info("Ready Flag OFF\n"); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (TEA5767_BAND_LIMIT_MASK & buffer[0]) 14362306a36Sopenharmony_ci tuner_info("Tuner at band limit\n"); 14462306a36Sopenharmony_ci else 14562306a36Sopenharmony_ci tuner_info("Tuner not at band limit\n"); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci div = ((buffer[0] & 0x3f) << 8) | buffer[1]; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci switch (priv->ctrl.xtal_freq) { 15062306a36Sopenharmony_ci case TEA5767_HIGH_LO_13MHz: 15162306a36Sopenharmony_ci frq = (div * 50000 - 700000 - 225000) / 4; /* Freq in KHz */ 15262306a36Sopenharmony_ci break; 15362306a36Sopenharmony_ci case TEA5767_LOW_LO_13MHz: 15462306a36Sopenharmony_ci frq = (div * 50000 + 700000 + 225000) / 4; /* Freq in KHz */ 15562306a36Sopenharmony_ci break; 15662306a36Sopenharmony_ci case TEA5767_LOW_LO_32768: 15762306a36Sopenharmony_ci frq = (div * 32768 + 700000 + 225000) / 4; /* Freq in KHz */ 15862306a36Sopenharmony_ci break; 15962306a36Sopenharmony_ci case TEA5767_HIGH_LO_32768: 16062306a36Sopenharmony_ci default: 16162306a36Sopenharmony_ci frq = (div * 32768 - 700000 - 225000) / 4; /* Freq in KHz */ 16262306a36Sopenharmony_ci break; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci buffer[0] = (div >> 8) & 0x3f; 16562306a36Sopenharmony_ci buffer[1] = div & 0xff; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci tuner_info("Frequency %d.%03d KHz (divider = 0x%04x)\n", 16862306a36Sopenharmony_ci frq / 1000, frq % 1000, div); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (TEA5767_STEREO_MASK & buffer[2]) 17162306a36Sopenharmony_ci tuner_info("Stereo\n"); 17262306a36Sopenharmony_ci else 17362306a36Sopenharmony_ci tuner_info("Mono\n"); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci tuner_info("IF Counter = %d\n", buffer[2] & TEA5767_IF_CNTR_MASK); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci tuner_info("ADC Level = %d\n", 17862306a36Sopenharmony_ci (buffer[3] & TEA5767_ADC_LEVEL_MASK) >> 4); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci tuner_info("Chip ID = %d\n", (buffer[3] & TEA5767_CHIP_ID_MASK)); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci tuner_info("Reserved = 0x%02x\n", 18362306a36Sopenharmony_ci (buffer[4] & TEA5767_RESERVED_MASK)); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/* Freq should be specifyed at 62.5 Hz */ 18762306a36Sopenharmony_cistatic int set_radio_freq(struct dvb_frontend *fe, 18862306a36Sopenharmony_ci struct analog_parameters *params) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct tea5767_priv *priv = fe->tuner_priv; 19162306a36Sopenharmony_ci unsigned int frq = params->frequency; 19262306a36Sopenharmony_ci unsigned char buffer[5]; 19362306a36Sopenharmony_ci unsigned div; 19462306a36Sopenharmony_ci int rc; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci tuner_dbg("radio freq = %d.%03d MHz\n", frq/16000,(frq/16)%1000); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci buffer[2] = 0; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (priv->ctrl.port1) 20162306a36Sopenharmony_ci buffer[2] |= TEA5767_PORT1_HIGH; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (params->audmode == V4L2_TUNER_MODE_MONO) { 20462306a36Sopenharmony_ci tuner_dbg("TEA5767 set to mono\n"); 20562306a36Sopenharmony_ci buffer[2] |= TEA5767_MONO; 20662306a36Sopenharmony_ci } else { 20762306a36Sopenharmony_ci tuner_dbg("TEA5767 set to stereo\n"); 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci buffer[3] = 0; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (priv->ctrl.port2) 21462306a36Sopenharmony_ci buffer[3] |= TEA5767_PORT2_HIGH; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (priv->ctrl.high_cut) 21762306a36Sopenharmony_ci buffer[3] |= TEA5767_HIGH_CUT_CTRL; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (priv->ctrl.st_noise) 22062306a36Sopenharmony_ci buffer[3] |= TEA5767_ST_NOISE_CTL; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (priv->ctrl.soft_mute) 22362306a36Sopenharmony_ci buffer[3] |= TEA5767_SOFT_MUTE; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (priv->ctrl.japan_band) 22662306a36Sopenharmony_ci buffer[3] |= TEA5767_JAPAN_BAND; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci buffer[4] = 0; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (priv->ctrl.deemph_75) 23162306a36Sopenharmony_ci buffer[4] |= TEA5767_DEEMPH_75; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (priv->ctrl.pllref) 23462306a36Sopenharmony_ci buffer[4] |= TEA5767_PLLREF_ENABLE; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* Rounds freq to next decimal value - for 62.5 KHz step */ 23862306a36Sopenharmony_ci /* frq = 20*(frq/16)+radio_frq[frq%16]; */ 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci switch (priv->ctrl.xtal_freq) { 24162306a36Sopenharmony_ci case TEA5767_HIGH_LO_13MHz: 24262306a36Sopenharmony_ci tuner_dbg("radio HIGH LO inject xtal @ 13 MHz\n"); 24362306a36Sopenharmony_ci buffer[2] |= TEA5767_HIGH_LO_INJECT; 24462306a36Sopenharmony_ci div = (frq * (4000 / 16) + 700000 + 225000 + 25000) / 50000; 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci case TEA5767_LOW_LO_13MHz: 24762306a36Sopenharmony_ci tuner_dbg("radio LOW LO inject xtal @ 13 MHz\n"); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci div = (frq * (4000 / 16) - 700000 - 225000 + 25000) / 50000; 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci case TEA5767_LOW_LO_32768: 25262306a36Sopenharmony_ci tuner_dbg("radio LOW LO inject xtal @ 32,768 MHz\n"); 25362306a36Sopenharmony_ci buffer[3] |= TEA5767_XTAL_32768; 25462306a36Sopenharmony_ci /* const 700=4000*175 Khz - to adjust freq to right value */ 25562306a36Sopenharmony_ci div = ((frq * (4000 / 16) - 700000 - 225000) + 16384) >> 15; 25662306a36Sopenharmony_ci break; 25762306a36Sopenharmony_ci case TEA5767_HIGH_LO_32768: 25862306a36Sopenharmony_ci default: 25962306a36Sopenharmony_ci tuner_dbg("radio HIGH LO inject xtal @ 32,768 MHz\n"); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci buffer[2] |= TEA5767_HIGH_LO_INJECT; 26262306a36Sopenharmony_ci buffer[3] |= TEA5767_XTAL_32768; 26362306a36Sopenharmony_ci div = ((frq * (4000 / 16) + 700000 + 225000) + 16384) >> 15; 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci buffer[0] = (div >> 8) & 0x3f; 26762306a36Sopenharmony_ci buffer[1] = div & 0xff; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5))) 27062306a36Sopenharmony_ci tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (debug) { 27362306a36Sopenharmony_ci if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) 27462306a36Sopenharmony_ci tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); 27562306a36Sopenharmony_ci else 27662306a36Sopenharmony_ci tea5767_status_dump(priv, buffer); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci priv->frequency = frq * 125 / 2; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci return 0; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic int tea5767_read_status(struct dvb_frontend *fe, char *buffer) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct tea5767_priv *priv = fe->tuner_priv; 28762306a36Sopenharmony_ci int rc; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci memset(buffer, 0, 5); 29062306a36Sopenharmony_ci if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) { 29162306a36Sopenharmony_ci tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); 29262306a36Sopenharmony_ci return -EREMOTEIO; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci return 0; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic inline int tea5767_signal(struct dvb_frontend *fe, const char *buffer) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci struct tea5767_priv *priv = fe->tuner_priv; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci int signal = ((buffer[3] & TEA5767_ADC_LEVEL_MASK) << 8); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci tuner_dbg("Signal strength: %d\n", signal); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return signal; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic inline int tea5767_stereo(struct dvb_frontend *fe, const char *buffer) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct tea5767_priv *priv = fe->tuner_priv; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci int stereo = buffer[2] & TEA5767_STEREO_MASK; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci tuner_dbg("Radio ST GET = %02x\n", stereo); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return (stereo ? V4L2_TUNER_SUB_STEREO : 0); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic int tea5767_get_status(struct dvb_frontend *fe, u32 *status) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci unsigned char buffer[5]; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci *status = 0; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (0 == tea5767_read_status(fe, buffer)) { 32762306a36Sopenharmony_ci if (tea5767_signal(fe, buffer)) 32862306a36Sopenharmony_ci *status = TUNER_STATUS_LOCKED; 32962306a36Sopenharmony_ci if (tea5767_stereo(fe, buffer)) 33062306a36Sopenharmony_ci *status |= TUNER_STATUS_STEREO; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci return 0; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic int tea5767_get_rf_strength(struct dvb_frontend *fe, u16 *strength) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci unsigned char buffer[5]; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci *strength = 0; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (0 == tea5767_read_status(fe, buffer)) 34362306a36Sopenharmony_ci *strength = tea5767_signal(fe, buffer); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci return 0; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic int tea5767_standby(struct dvb_frontend *fe) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci unsigned char buffer[5]; 35162306a36Sopenharmony_ci struct tea5767_priv *priv = fe->tuner_priv; 35262306a36Sopenharmony_ci unsigned div, rc; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci div = (87500 * 4 + 700 + 225 + 25) / 50; /* Set frequency to 87.5 MHz */ 35562306a36Sopenharmony_ci buffer[0] = (div >> 8) & 0x3f; 35662306a36Sopenharmony_ci buffer[1] = div & 0xff; 35762306a36Sopenharmony_ci buffer[2] = TEA5767_PORT1_HIGH; 35862306a36Sopenharmony_ci buffer[3] = TEA5767_PORT2_HIGH | TEA5767_HIGH_CUT_CTRL | 35962306a36Sopenharmony_ci TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND | TEA5767_STDBY; 36062306a36Sopenharmony_ci buffer[4] = 0; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5))) 36362306a36Sopenharmony_ci tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ciint tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct tuner_i2c_props i2c = { .adap = i2c_adap, .addr = i2c_addr }; 37162306a36Sopenharmony_ci unsigned char buffer[7] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci int rc; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if ((rc = tuner_i2c_xfer_recv(&i2c, buffer, 7))< 5) { 37662306a36Sopenharmony_ci pr_warn("It is not a TEA5767. Received %i bytes.\n", rc); 37762306a36Sopenharmony_ci return -EINVAL; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* If all bytes are the same then it's a TV tuner and not a tea5767 */ 38162306a36Sopenharmony_ci if (buffer[0] == buffer[1] && buffer[0] == buffer[2] && 38262306a36Sopenharmony_ci buffer[0] == buffer[3] && buffer[0] == buffer[4]) { 38362306a36Sopenharmony_ci pr_warn("All bytes are equal. It is not a TEA5767\n"); 38462306a36Sopenharmony_ci return -EINVAL; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* Status bytes: 38862306a36Sopenharmony_ci * Byte 4: bit 3:1 : CI (Chip Identification) == 0 38962306a36Sopenharmony_ci * bit 0 : internally set to 0 39062306a36Sopenharmony_ci * Byte 5: bit 7:0 : == 0 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_ci if (((buffer[3] & 0x0f) != 0x00) || (buffer[4] != 0x00)) { 39362306a36Sopenharmony_ci pr_warn("Chip ID is not zero. It is not a TEA5767\n"); 39462306a36Sopenharmony_ci return -EINVAL; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci return 0; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic void tea5767_release(struct dvb_frontend *fe) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci kfree(fe->tuner_priv); 40462306a36Sopenharmony_ci fe->tuner_priv = NULL; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic int tea5767_get_frequency(struct dvb_frontend *fe, u32 *frequency) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct tea5767_priv *priv = fe->tuner_priv; 41062306a36Sopenharmony_ci *frequency = priv->frequency; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci return 0; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic int tea5767_set_config (struct dvb_frontend *fe, void *priv_cfg) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct tea5767_priv *priv = fe->tuner_priv; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci memcpy(&priv->ctrl, priv_cfg, sizeof(priv->ctrl)); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return 0; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic const struct dvb_tuner_ops tea5767_tuner_ops = { 42562306a36Sopenharmony_ci .info = { 42662306a36Sopenharmony_ci .name = "tea5767", // Philips TEA5767HN FM Radio 42762306a36Sopenharmony_ci }, 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci .set_analog_params = set_radio_freq, 43062306a36Sopenharmony_ci .set_config = tea5767_set_config, 43162306a36Sopenharmony_ci .sleep = tea5767_standby, 43262306a36Sopenharmony_ci .release = tea5767_release, 43362306a36Sopenharmony_ci .get_frequency = tea5767_get_frequency, 43462306a36Sopenharmony_ci .get_status = tea5767_get_status, 43562306a36Sopenharmony_ci .get_rf_strength = tea5767_get_rf_strength, 43662306a36Sopenharmony_ci}; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistruct dvb_frontend *tea5767_attach(struct dvb_frontend *fe, 43962306a36Sopenharmony_ci struct i2c_adapter* i2c_adap, 44062306a36Sopenharmony_ci u8 i2c_addr) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci struct tea5767_priv *priv = NULL; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci priv = kzalloc(sizeof(struct tea5767_priv), GFP_KERNEL); 44562306a36Sopenharmony_ci if (priv == NULL) 44662306a36Sopenharmony_ci return NULL; 44762306a36Sopenharmony_ci fe->tuner_priv = priv; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci priv->i2c_props.addr = i2c_addr; 45062306a36Sopenharmony_ci priv->i2c_props.adap = i2c_adap; 45162306a36Sopenharmony_ci priv->i2c_props.name = "tea5767"; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci priv->ctrl.xtal_freq = TEA5767_HIGH_LO_32768; 45462306a36Sopenharmony_ci priv->ctrl.port1 = 1; 45562306a36Sopenharmony_ci priv->ctrl.port2 = 1; 45662306a36Sopenharmony_ci priv->ctrl.high_cut = 1; 45762306a36Sopenharmony_ci priv->ctrl.st_noise = 1; 45862306a36Sopenharmony_ci priv->ctrl.japan_band = 1; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci memcpy(&fe->ops.tuner_ops, &tea5767_tuner_ops, 46162306a36Sopenharmony_ci sizeof(struct dvb_tuner_ops)); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci tuner_info("type set to %s\n", "Philips TEA5767HN FM Radio"); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci return fe; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tea5767_attach); 46962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tea5767_autodetection); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ciMODULE_DESCRIPTION("Philips TEA5767 FM tuner driver"); 47262306a36Sopenharmony_ciMODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@kernel.org>"); 47362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 474