18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// For Philips TEA5767 FM Chip used on some TV Cards like Prolink Pixelview 38c2ecf20Sopenharmony_ci// I2C address is always 0xC0. 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (c) 2005 Mauro Carvalho Chehab <mchehab@kernel.org> 68c2ecf20Sopenharmony_ci// 78c2ecf20Sopenharmony_ci// tea5767 autodetection thanks to Torsten Seeboth and Atsushi Nakagawa 88c2ecf20Sopenharmony_ci// from their contributions on DScaler. 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/i2c.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 168c2ecf20Sopenharmony_ci#include "tuner-i2c.h" 178c2ecf20Sopenharmony_ci#include "tea5767.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic int debug; 208c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 218c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "enable verbose debug messages"); 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/*****************************************************************************/ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct tea5767_priv { 268c2ecf20Sopenharmony_ci struct tuner_i2c_props i2c_props; 278c2ecf20Sopenharmony_ci u32 frequency; 288c2ecf20Sopenharmony_ci struct tea5767_ctrl ctrl; 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/*****************************************************************************/ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/****************************** 348c2ecf20Sopenharmony_ci * Write mode register values * 358c2ecf20Sopenharmony_ci ******************************/ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* First register */ 388c2ecf20Sopenharmony_ci#define TEA5767_MUTE 0x80 /* Mutes output */ 398c2ecf20Sopenharmony_ci#define TEA5767_SEARCH 0x40 /* Activates station search */ 408c2ecf20Sopenharmony_ci/* Bits 0-5 for divider MSB */ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* Second register */ 438c2ecf20Sopenharmony_ci/* Bits 0-7 for divider LSB */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* Third register */ 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* Station search from botton to up */ 488c2ecf20Sopenharmony_ci#define TEA5767_SEARCH_UP 0x80 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* Searches with ADC output = 10 */ 518c2ecf20Sopenharmony_ci#define TEA5767_SRCH_HIGH_LVL 0x60 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* Searches with ADC output = 10 */ 548c2ecf20Sopenharmony_ci#define TEA5767_SRCH_MID_LVL 0x40 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* Searches with ADC output = 5 */ 578c2ecf20Sopenharmony_ci#define TEA5767_SRCH_LOW_LVL 0x20 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* if on, div=4*(Frf+Fif)/Fref otherwise, div=4*(Frf-Fif)/Freq) */ 608c2ecf20Sopenharmony_ci#define TEA5767_HIGH_LO_INJECT 0x10 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* Disable stereo */ 638c2ecf20Sopenharmony_ci#define TEA5767_MONO 0x08 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* Disable right channel and turns to mono */ 668c2ecf20Sopenharmony_ci#define TEA5767_MUTE_RIGHT 0x04 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* Disable left channel and turns to mono */ 698c2ecf20Sopenharmony_ci#define TEA5767_MUTE_LEFT 0x02 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define TEA5767_PORT1_HIGH 0x01 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* Fourth register */ 748c2ecf20Sopenharmony_ci#define TEA5767_PORT2_HIGH 0x80 758c2ecf20Sopenharmony_ci/* Chips stops working. Only I2C bus remains on */ 768c2ecf20Sopenharmony_ci#define TEA5767_STDBY 0x40 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* Japan freq (76-108 MHz. If disabled, 87.5-108 MHz */ 798c2ecf20Sopenharmony_ci#define TEA5767_JAPAN_BAND 0x20 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* Unselected means 32.768 KHz freq as reference. Otherwise Xtal at 13 MHz */ 828c2ecf20Sopenharmony_ci#define TEA5767_XTAL_32768 0x10 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* Cuts weak signals */ 858c2ecf20Sopenharmony_ci#define TEA5767_SOFT_MUTE 0x08 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* Activates high cut control */ 888c2ecf20Sopenharmony_ci#define TEA5767_HIGH_CUT_CTRL 0x04 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* Activates stereo noise control */ 918c2ecf20Sopenharmony_ci#define TEA5767_ST_NOISE_CTL 0x02 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* If activate PORT 1 indicates SEARCH or else it is used as PORT1 */ 948c2ecf20Sopenharmony_ci#define TEA5767_SRCH_IND 0x01 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/* Fifth register */ 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* By activating, it will use Xtal at 13 MHz as reference for divider */ 998c2ecf20Sopenharmony_ci#define TEA5767_PLLREF_ENABLE 0x80 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* By activating, deemphasis=50, or else, deemphasis of 50us */ 1028c2ecf20Sopenharmony_ci#define TEA5767_DEEMPH_75 0X40 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/***************************** 1058c2ecf20Sopenharmony_ci * Read mode register values * 1068c2ecf20Sopenharmony_ci *****************************/ 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* First register */ 1098c2ecf20Sopenharmony_ci#define TEA5767_READY_FLAG_MASK 0x80 1108c2ecf20Sopenharmony_ci#define TEA5767_BAND_LIMIT_MASK 0X40 1118c2ecf20Sopenharmony_ci/* Bits 0-5 for divider MSB after search or preset */ 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* Second register */ 1148c2ecf20Sopenharmony_ci/* Bits 0-7 for divider LSB after search or preset */ 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* Third register */ 1178c2ecf20Sopenharmony_ci#define TEA5767_STEREO_MASK 0x80 1188c2ecf20Sopenharmony_ci#define TEA5767_IF_CNTR_MASK 0x7f 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/* Fourth register */ 1218c2ecf20Sopenharmony_ci#define TEA5767_ADC_LEVEL_MASK 0xf0 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* should be 0 */ 1248c2ecf20Sopenharmony_ci#define TEA5767_CHIP_ID_MASK 0x0f 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/* Fifth register */ 1278c2ecf20Sopenharmony_ci/* Reserved for future extensions */ 1288c2ecf20Sopenharmony_ci#define TEA5767_RESERVED_MASK 0xff 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/*****************************************************************************/ 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void tea5767_status_dump(struct tea5767_priv *priv, 1338c2ecf20Sopenharmony_ci unsigned char *buffer) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci unsigned int div, frq; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (TEA5767_READY_FLAG_MASK & buffer[0]) 1388c2ecf20Sopenharmony_ci tuner_info("Ready Flag ON\n"); 1398c2ecf20Sopenharmony_ci else 1408c2ecf20Sopenharmony_ci tuner_info("Ready Flag OFF\n"); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (TEA5767_BAND_LIMIT_MASK & buffer[0]) 1438c2ecf20Sopenharmony_ci tuner_info("Tuner at band limit\n"); 1448c2ecf20Sopenharmony_ci else 1458c2ecf20Sopenharmony_ci tuner_info("Tuner not at band limit\n"); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci div = ((buffer[0] & 0x3f) << 8) | buffer[1]; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci switch (priv->ctrl.xtal_freq) { 1508c2ecf20Sopenharmony_ci case TEA5767_HIGH_LO_13MHz: 1518c2ecf20Sopenharmony_ci frq = (div * 50000 - 700000 - 225000) / 4; /* Freq in KHz */ 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci case TEA5767_LOW_LO_13MHz: 1548c2ecf20Sopenharmony_ci frq = (div * 50000 + 700000 + 225000) / 4; /* Freq in KHz */ 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci case TEA5767_LOW_LO_32768: 1578c2ecf20Sopenharmony_ci frq = (div * 32768 + 700000 + 225000) / 4; /* Freq in KHz */ 1588c2ecf20Sopenharmony_ci break; 1598c2ecf20Sopenharmony_ci case TEA5767_HIGH_LO_32768: 1608c2ecf20Sopenharmony_ci default: 1618c2ecf20Sopenharmony_ci frq = (div * 32768 - 700000 - 225000) / 4; /* Freq in KHz */ 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci buffer[0] = (div >> 8) & 0x3f; 1658c2ecf20Sopenharmony_ci buffer[1] = div & 0xff; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci tuner_info("Frequency %d.%03d KHz (divider = 0x%04x)\n", 1688c2ecf20Sopenharmony_ci frq / 1000, frq % 1000, div); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (TEA5767_STEREO_MASK & buffer[2]) 1718c2ecf20Sopenharmony_ci tuner_info("Stereo\n"); 1728c2ecf20Sopenharmony_ci else 1738c2ecf20Sopenharmony_ci tuner_info("Mono\n"); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci tuner_info("IF Counter = %d\n", buffer[2] & TEA5767_IF_CNTR_MASK); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci tuner_info("ADC Level = %d\n", 1788c2ecf20Sopenharmony_ci (buffer[3] & TEA5767_ADC_LEVEL_MASK) >> 4); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci tuner_info("Chip ID = %d\n", (buffer[3] & TEA5767_CHIP_ID_MASK)); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci tuner_info("Reserved = 0x%02x\n", 1838c2ecf20Sopenharmony_ci (buffer[4] & TEA5767_RESERVED_MASK)); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/* Freq should be specifyed at 62.5 Hz */ 1878c2ecf20Sopenharmony_cistatic int set_radio_freq(struct dvb_frontend *fe, 1888c2ecf20Sopenharmony_ci struct analog_parameters *params) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct tea5767_priv *priv = fe->tuner_priv; 1918c2ecf20Sopenharmony_ci unsigned int frq = params->frequency; 1928c2ecf20Sopenharmony_ci unsigned char buffer[5]; 1938c2ecf20Sopenharmony_ci unsigned div; 1948c2ecf20Sopenharmony_ci int rc; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci tuner_dbg("radio freq = %d.%03d MHz\n", frq/16000,(frq/16)%1000); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci buffer[2] = 0; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (priv->ctrl.port1) 2018c2ecf20Sopenharmony_ci buffer[2] |= TEA5767_PORT1_HIGH; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (params->audmode == V4L2_TUNER_MODE_MONO) { 2048c2ecf20Sopenharmony_ci tuner_dbg("TEA5767 set to mono\n"); 2058c2ecf20Sopenharmony_ci buffer[2] |= TEA5767_MONO; 2068c2ecf20Sopenharmony_ci } else { 2078c2ecf20Sopenharmony_ci tuner_dbg("TEA5767 set to stereo\n"); 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci buffer[3] = 0; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (priv->ctrl.port2) 2148c2ecf20Sopenharmony_ci buffer[3] |= TEA5767_PORT2_HIGH; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (priv->ctrl.high_cut) 2178c2ecf20Sopenharmony_ci buffer[3] |= TEA5767_HIGH_CUT_CTRL; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (priv->ctrl.st_noise) 2208c2ecf20Sopenharmony_ci buffer[3] |= TEA5767_ST_NOISE_CTL; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (priv->ctrl.soft_mute) 2238c2ecf20Sopenharmony_ci buffer[3] |= TEA5767_SOFT_MUTE; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (priv->ctrl.japan_band) 2268c2ecf20Sopenharmony_ci buffer[3] |= TEA5767_JAPAN_BAND; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci buffer[4] = 0; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (priv->ctrl.deemph_75) 2318c2ecf20Sopenharmony_ci buffer[4] |= TEA5767_DEEMPH_75; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (priv->ctrl.pllref) 2348c2ecf20Sopenharmony_ci buffer[4] |= TEA5767_PLLREF_ENABLE; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* Rounds freq to next decimal value - for 62.5 KHz step */ 2388c2ecf20Sopenharmony_ci /* frq = 20*(frq/16)+radio_frq[frq%16]; */ 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci switch (priv->ctrl.xtal_freq) { 2418c2ecf20Sopenharmony_ci case TEA5767_HIGH_LO_13MHz: 2428c2ecf20Sopenharmony_ci tuner_dbg("radio HIGH LO inject xtal @ 13 MHz\n"); 2438c2ecf20Sopenharmony_ci buffer[2] |= TEA5767_HIGH_LO_INJECT; 2448c2ecf20Sopenharmony_ci div = (frq * (4000 / 16) + 700000 + 225000 + 25000) / 50000; 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci case TEA5767_LOW_LO_13MHz: 2478c2ecf20Sopenharmony_ci tuner_dbg("radio LOW LO inject xtal @ 13 MHz\n"); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci div = (frq * (4000 / 16) - 700000 - 225000 + 25000) / 50000; 2508c2ecf20Sopenharmony_ci break; 2518c2ecf20Sopenharmony_ci case TEA5767_LOW_LO_32768: 2528c2ecf20Sopenharmony_ci tuner_dbg("radio LOW LO inject xtal @ 32,768 MHz\n"); 2538c2ecf20Sopenharmony_ci buffer[3] |= TEA5767_XTAL_32768; 2548c2ecf20Sopenharmony_ci /* const 700=4000*175 Khz - to adjust freq to right value */ 2558c2ecf20Sopenharmony_ci div = ((frq * (4000 / 16) - 700000 - 225000) + 16384) >> 15; 2568c2ecf20Sopenharmony_ci break; 2578c2ecf20Sopenharmony_ci case TEA5767_HIGH_LO_32768: 2588c2ecf20Sopenharmony_ci default: 2598c2ecf20Sopenharmony_ci tuner_dbg("radio HIGH LO inject xtal @ 32,768 MHz\n"); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci buffer[2] |= TEA5767_HIGH_LO_INJECT; 2628c2ecf20Sopenharmony_ci buffer[3] |= TEA5767_XTAL_32768; 2638c2ecf20Sopenharmony_ci div = ((frq * (4000 / 16) + 700000 + 225000) + 16384) >> 15; 2648c2ecf20Sopenharmony_ci break; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci buffer[0] = (div >> 8) & 0x3f; 2678c2ecf20Sopenharmony_ci buffer[1] = div & 0xff; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5))) 2708c2ecf20Sopenharmony_ci tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (debug) { 2738c2ecf20Sopenharmony_ci if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) 2748c2ecf20Sopenharmony_ci tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); 2758c2ecf20Sopenharmony_ci else 2768c2ecf20Sopenharmony_ci tea5767_status_dump(priv, buffer); 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci priv->frequency = frq * 125 / 2; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci return 0; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic int tea5767_read_status(struct dvb_frontend *fe, char *buffer) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct tea5767_priv *priv = fe->tuner_priv; 2878c2ecf20Sopenharmony_ci int rc; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci memset(buffer, 0, 5); 2908c2ecf20Sopenharmony_ci if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) { 2918c2ecf20Sopenharmony_ci tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); 2928c2ecf20Sopenharmony_ci return -EREMOTEIO; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic inline int tea5767_signal(struct dvb_frontend *fe, const char *buffer) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct tea5767_priv *priv = fe->tuner_priv; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci int signal = ((buffer[3] & TEA5767_ADC_LEVEL_MASK) << 8); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci tuner_dbg("Signal strength: %d\n", signal); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return signal; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic inline int tea5767_stereo(struct dvb_frontend *fe, const char *buffer) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci struct tea5767_priv *priv = fe->tuner_priv; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci int stereo = buffer[2] & TEA5767_STEREO_MASK; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci tuner_dbg("Radio ST GET = %02x\n", stereo); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return (stereo ? V4L2_TUNER_SUB_STEREO : 0); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int tea5767_get_status(struct dvb_frontend *fe, u32 *status) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci unsigned char buffer[5]; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci *status = 0; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (0 == tea5767_read_status(fe, buffer)) { 3278c2ecf20Sopenharmony_ci if (tea5767_signal(fe, buffer)) 3288c2ecf20Sopenharmony_ci *status = TUNER_STATUS_LOCKED; 3298c2ecf20Sopenharmony_ci if (tea5767_stereo(fe, buffer)) 3308c2ecf20Sopenharmony_ci *status |= TUNER_STATUS_STEREO; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci return 0; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic int tea5767_get_rf_strength(struct dvb_frontend *fe, u16 *strength) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci unsigned char buffer[5]; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci *strength = 0; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (0 == tea5767_read_status(fe, buffer)) 3438c2ecf20Sopenharmony_ci *strength = tea5767_signal(fe, buffer); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic int tea5767_standby(struct dvb_frontend *fe) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci unsigned char buffer[5]; 3518c2ecf20Sopenharmony_ci struct tea5767_priv *priv = fe->tuner_priv; 3528c2ecf20Sopenharmony_ci unsigned div, rc; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci div = (87500 * 4 + 700 + 225 + 25) / 50; /* Set frequency to 87.5 MHz */ 3558c2ecf20Sopenharmony_ci buffer[0] = (div >> 8) & 0x3f; 3568c2ecf20Sopenharmony_ci buffer[1] = div & 0xff; 3578c2ecf20Sopenharmony_ci buffer[2] = TEA5767_PORT1_HIGH; 3588c2ecf20Sopenharmony_ci buffer[3] = TEA5767_PORT2_HIGH | TEA5767_HIGH_CUT_CTRL | 3598c2ecf20Sopenharmony_ci TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND | TEA5767_STDBY; 3608c2ecf20Sopenharmony_ci buffer[4] = 0; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5))) 3638c2ecf20Sopenharmony_ci tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci return 0; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ciint tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct tuner_i2c_props i2c = { .adap = i2c_adap, .addr = i2c_addr }; 3718c2ecf20Sopenharmony_ci unsigned char buffer[7] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci int rc; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if ((rc = tuner_i2c_xfer_recv(&i2c, buffer, 7))< 5) { 3768c2ecf20Sopenharmony_ci pr_warn("It is not a TEA5767. Received %i bytes.\n", rc); 3778c2ecf20Sopenharmony_ci return -EINVAL; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* If all bytes are the same then it's a TV tuner and not a tea5767 */ 3818c2ecf20Sopenharmony_ci if (buffer[0] == buffer[1] && buffer[0] == buffer[2] && 3828c2ecf20Sopenharmony_ci buffer[0] == buffer[3] && buffer[0] == buffer[4]) { 3838c2ecf20Sopenharmony_ci pr_warn("All bytes are equal. It is not a TEA5767\n"); 3848c2ecf20Sopenharmony_ci return -EINVAL; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* Status bytes: 3888c2ecf20Sopenharmony_ci * Byte 4: bit 3:1 : CI (Chip Identification) == 0 3898c2ecf20Sopenharmony_ci * bit 0 : internally set to 0 3908c2ecf20Sopenharmony_ci * Byte 5: bit 7:0 : == 0 3918c2ecf20Sopenharmony_ci */ 3928c2ecf20Sopenharmony_ci if (((buffer[3] & 0x0f) != 0x00) || (buffer[4] != 0x00)) { 3938c2ecf20Sopenharmony_ci pr_warn("Chip ID is not zero. It is not a TEA5767\n"); 3948c2ecf20Sopenharmony_ci return -EINVAL; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic void tea5767_release(struct dvb_frontend *fe) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci kfree(fe->tuner_priv); 4048c2ecf20Sopenharmony_ci fe->tuner_priv = NULL; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int tea5767_get_frequency(struct dvb_frontend *fe, u32 *frequency) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct tea5767_priv *priv = fe->tuner_priv; 4108c2ecf20Sopenharmony_ci *frequency = priv->frequency; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci return 0; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic int tea5767_set_config (struct dvb_frontend *fe, void *priv_cfg) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci struct tea5767_priv *priv = fe->tuner_priv; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci memcpy(&priv->ctrl, priv_cfg, sizeof(priv->ctrl)); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci return 0; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic const struct dvb_tuner_ops tea5767_tuner_ops = { 4258c2ecf20Sopenharmony_ci .info = { 4268c2ecf20Sopenharmony_ci .name = "tea5767", // Philips TEA5767HN FM Radio 4278c2ecf20Sopenharmony_ci }, 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci .set_analog_params = set_radio_freq, 4308c2ecf20Sopenharmony_ci .set_config = tea5767_set_config, 4318c2ecf20Sopenharmony_ci .sleep = tea5767_standby, 4328c2ecf20Sopenharmony_ci .release = tea5767_release, 4338c2ecf20Sopenharmony_ci .get_frequency = tea5767_get_frequency, 4348c2ecf20Sopenharmony_ci .get_status = tea5767_get_status, 4358c2ecf20Sopenharmony_ci .get_rf_strength = tea5767_get_rf_strength, 4368c2ecf20Sopenharmony_ci}; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistruct dvb_frontend *tea5767_attach(struct dvb_frontend *fe, 4398c2ecf20Sopenharmony_ci struct i2c_adapter* i2c_adap, 4408c2ecf20Sopenharmony_ci u8 i2c_addr) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci struct tea5767_priv *priv = NULL; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(struct tea5767_priv), GFP_KERNEL); 4458c2ecf20Sopenharmony_ci if (priv == NULL) 4468c2ecf20Sopenharmony_ci return NULL; 4478c2ecf20Sopenharmony_ci fe->tuner_priv = priv; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci priv->i2c_props.addr = i2c_addr; 4508c2ecf20Sopenharmony_ci priv->i2c_props.adap = i2c_adap; 4518c2ecf20Sopenharmony_ci priv->i2c_props.name = "tea5767"; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci priv->ctrl.xtal_freq = TEA5767_HIGH_LO_32768; 4548c2ecf20Sopenharmony_ci priv->ctrl.port1 = 1; 4558c2ecf20Sopenharmony_ci priv->ctrl.port2 = 1; 4568c2ecf20Sopenharmony_ci priv->ctrl.high_cut = 1; 4578c2ecf20Sopenharmony_ci priv->ctrl.st_noise = 1; 4588c2ecf20Sopenharmony_ci priv->ctrl.japan_band = 1; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci memcpy(&fe->ops.tuner_ops, &tea5767_tuner_ops, 4618c2ecf20Sopenharmony_ci sizeof(struct dvb_tuner_ops)); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci tuner_info("type set to %s\n", "Philips TEA5767HN FM Radio"); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci return fe; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tea5767_attach); 4698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tea5767_autodetection); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Philips TEA5767 FM tuner driver"); 4728c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@kernel.org>"); 4738c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 474