162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ITE IT913X silicon tuner driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com) 662306a36Sopenharmony_ci * IT9137 Copyright (C) ITE Tech Inc. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "it913x.h" 1062306a36Sopenharmony_ci#include <linux/platform_device.h> 1162306a36Sopenharmony_ci#include <linux/regmap.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistruct it913x_dev { 1462306a36Sopenharmony_ci struct platform_device *pdev; 1562306a36Sopenharmony_ci struct regmap *regmap; 1662306a36Sopenharmony_ci struct dvb_frontend *fe; 1762306a36Sopenharmony_ci u8 chip_ver:2; 1862306a36Sopenharmony_ci u8 role:2; 1962306a36Sopenharmony_ci u16 xtal; 2062306a36Sopenharmony_ci u8 fdiv; 2162306a36Sopenharmony_ci u8 clk_mode; 2262306a36Sopenharmony_ci u32 fn_min; 2362306a36Sopenharmony_ci bool active; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic int it913x_init(struct dvb_frontend *fe) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct it913x_dev *dev = fe->tuner_priv; 2962306a36Sopenharmony_ci struct platform_device *pdev = dev->pdev; 3062306a36Sopenharmony_ci int ret; 3162306a36Sopenharmony_ci unsigned int utmp; 3262306a36Sopenharmony_ci u8 iqik_m_cal, nv_val, buf[2]; 3362306a36Sopenharmony_ci static const u8 nv[] = {48, 32, 24, 16, 12, 8, 6, 4, 2}; 3462306a36Sopenharmony_ci unsigned long timeout; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci dev_dbg(&pdev->dev, "role %u\n", dev->role); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci ret = regmap_write(dev->regmap, 0x80ec4c, 0x68); 3962306a36Sopenharmony_ci if (ret) 4062306a36Sopenharmony_ci goto err; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci usleep_range(10000, 100000); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci ret = regmap_read(dev->regmap, 0x80ec86, &utmp); 4562306a36Sopenharmony_ci if (ret) 4662306a36Sopenharmony_ci goto err; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci switch (utmp) { 4962306a36Sopenharmony_ci case 0: 5062306a36Sopenharmony_ci /* 12.000 MHz */ 5162306a36Sopenharmony_ci dev->clk_mode = utmp; 5262306a36Sopenharmony_ci dev->xtal = 2000; 5362306a36Sopenharmony_ci dev->fdiv = 3; 5462306a36Sopenharmony_ci iqik_m_cal = 16; 5562306a36Sopenharmony_ci break; 5662306a36Sopenharmony_ci case 1: 5762306a36Sopenharmony_ci /* 20.480 MHz */ 5862306a36Sopenharmony_ci dev->clk_mode = utmp; 5962306a36Sopenharmony_ci dev->xtal = 640; 6062306a36Sopenharmony_ci dev->fdiv = 1; 6162306a36Sopenharmony_ci iqik_m_cal = 6; 6262306a36Sopenharmony_ci break; 6362306a36Sopenharmony_ci default: 6462306a36Sopenharmony_ci dev_err(&pdev->dev, "unknown clock identifier %d\n", utmp); 6562306a36Sopenharmony_ci ret = -EINVAL; 6662306a36Sopenharmony_ci goto err; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci ret = regmap_read(dev->regmap, 0x80ed03, &utmp); 7062306a36Sopenharmony_ci if (ret) 7162306a36Sopenharmony_ci goto err; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci else if (utmp < ARRAY_SIZE(nv)) 7462306a36Sopenharmony_ci nv_val = nv[utmp]; 7562306a36Sopenharmony_ci else 7662306a36Sopenharmony_ci nv_val = 2; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci #define TIMEOUT 50 7962306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(TIMEOUT); 8062306a36Sopenharmony_ci while (!time_after(jiffies, timeout)) { 8162306a36Sopenharmony_ci ret = regmap_bulk_read(dev->regmap, 0x80ed23, buf, 2); 8262306a36Sopenharmony_ci if (ret) 8362306a36Sopenharmony_ci goto err; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci utmp = (buf[1] << 8) | (buf[0] << 0); 8662306a36Sopenharmony_ci if (utmp) 8762306a36Sopenharmony_ci break; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci dev_dbg(&pdev->dev, "r_fbc_m_bdry took %u ms, val %u\n", 9162306a36Sopenharmony_ci jiffies_to_msecs(jiffies) - 9262306a36Sopenharmony_ci (jiffies_to_msecs(timeout) - TIMEOUT), utmp); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci dev->fn_min = dev->xtal * utmp; 9562306a36Sopenharmony_ci dev->fn_min /= (dev->fdiv * nv_val); 9662306a36Sopenharmony_ci dev->fn_min *= 1000; 9762306a36Sopenharmony_ci dev_dbg(&pdev->dev, "fn_min %u\n", dev->fn_min); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* 10062306a36Sopenharmony_ci * Chip version BX never sets that flag so we just wait 50ms in that 10162306a36Sopenharmony_ci * case. It is possible poll BX similarly than AX and then timeout in 10262306a36Sopenharmony_ci * order to get 50ms delay, but that causes about 120 extra I2C 10362306a36Sopenharmony_ci * messages. As for now, we just wait and reduce IO. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci if (dev->chip_ver == 1) { 10662306a36Sopenharmony_ci #define TIMEOUT 50 10762306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(TIMEOUT); 10862306a36Sopenharmony_ci while (!time_after(jiffies, timeout)) { 10962306a36Sopenharmony_ci ret = regmap_read(dev->regmap, 0x80ec82, &utmp); 11062306a36Sopenharmony_ci if (ret) 11162306a36Sopenharmony_ci goto err; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (utmp) 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci dev_dbg(&pdev->dev, "p_tsm_init_mode took %u ms, val %u\n", 11862306a36Sopenharmony_ci jiffies_to_msecs(jiffies) - 11962306a36Sopenharmony_ci (jiffies_to_msecs(timeout) - TIMEOUT), utmp); 12062306a36Sopenharmony_ci } else { 12162306a36Sopenharmony_ci msleep(50); 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci ret = regmap_write(dev->regmap, 0x80ed81, iqik_m_cal); 12562306a36Sopenharmony_ci if (ret) 12662306a36Sopenharmony_ci goto err; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci ret = regmap_write(dev->regmap, 0x80ec57, 0x00); 12962306a36Sopenharmony_ci if (ret) 13062306a36Sopenharmony_ci goto err; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci ret = regmap_write(dev->regmap, 0x80ec58, 0x00); 13362306a36Sopenharmony_ci if (ret) 13462306a36Sopenharmony_ci goto err; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci ret = regmap_write(dev->regmap, 0x80ec40, 0x01); 13762306a36Sopenharmony_ci if (ret) 13862306a36Sopenharmony_ci goto err; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci dev->active = true; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return 0; 14362306a36Sopenharmony_cierr: 14462306a36Sopenharmony_ci dev_dbg(&pdev->dev, "failed %d\n", ret); 14562306a36Sopenharmony_ci return ret; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic int it913x_sleep(struct dvb_frontend *fe) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct it913x_dev *dev = fe->tuner_priv; 15162306a36Sopenharmony_ci struct platform_device *pdev = dev->pdev; 15262306a36Sopenharmony_ci int ret, len; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci dev_dbg(&pdev->dev, "role %u\n", dev->role); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci dev->active = false; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci ret = regmap_bulk_write(dev->regmap, 0x80ec40, "\x00", 1); 15962306a36Sopenharmony_ci if (ret) 16062306a36Sopenharmony_ci goto err; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* 16362306a36Sopenharmony_ci * Writing '0x00' to master tuner register '0x80ec08' causes slave tuner 16462306a36Sopenharmony_ci * communication lost. Due to that, we cannot put master full sleep. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci if (dev->role == IT913X_ROLE_DUAL_MASTER) 16762306a36Sopenharmony_ci len = 4; 16862306a36Sopenharmony_ci else 16962306a36Sopenharmony_ci len = 15; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "role %u, len %d\n", dev->role, len); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci ret = regmap_bulk_write(dev->regmap, 0x80ec02, 17462306a36Sopenharmony_ci "\x3f\x1f\x3f\x3e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 17562306a36Sopenharmony_ci len); 17662306a36Sopenharmony_ci if (ret) 17762306a36Sopenharmony_ci goto err; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci ret = regmap_bulk_write(dev->regmap, 0x80ec12, "\x00\x00\x00\x00", 4); 18062306a36Sopenharmony_ci if (ret) 18162306a36Sopenharmony_ci goto err; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci ret = regmap_bulk_write(dev->regmap, 0x80ec17, 18462306a36Sopenharmony_ci "\x00\x00\x00\x00\x00\x00\x00\x00\x00", 9); 18562306a36Sopenharmony_ci if (ret) 18662306a36Sopenharmony_ci goto err; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci ret = regmap_bulk_write(dev->regmap, 0x80ec22, 18962306a36Sopenharmony_ci "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 10); 19062306a36Sopenharmony_ci if (ret) 19162306a36Sopenharmony_ci goto err; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci ret = regmap_bulk_write(dev->regmap, 0x80ec20, "\x00", 1); 19462306a36Sopenharmony_ci if (ret) 19562306a36Sopenharmony_ci goto err; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci ret = regmap_bulk_write(dev->regmap, 0x80ec3f, "\x01", 1); 19862306a36Sopenharmony_ci if (ret) 19962306a36Sopenharmony_ci goto err; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_cierr: 20362306a36Sopenharmony_ci dev_dbg(&pdev->dev, "failed %d\n", ret); 20462306a36Sopenharmony_ci return ret; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int it913x_set_params(struct dvb_frontend *fe) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct it913x_dev *dev = fe->tuner_priv; 21062306a36Sopenharmony_ci struct platform_device *pdev = dev->pdev; 21162306a36Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 21262306a36Sopenharmony_ci int ret; 21362306a36Sopenharmony_ci unsigned int utmp; 21462306a36Sopenharmony_ci u32 pre_lo_freq, t_cal_freq; 21562306a36Sopenharmony_ci u16 iqik_m_cal, n_div; 21662306a36Sopenharmony_ci u8 u8tmp, n, l_band, lna_band; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci dev_dbg(&pdev->dev, "role=%u, frequency %u, bandwidth_hz %u\n", 21962306a36Sopenharmony_ci dev->role, c->frequency, c->bandwidth_hz); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (!dev->active) { 22262306a36Sopenharmony_ci ret = -EINVAL; 22362306a36Sopenharmony_ci goto err; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (c->frequency <= 74000000) { 22762306a36Sopenharmony_ci n_div = 48; 22862306a36Sopenharmony_ci n = 0; 22962306a36Sopenharmony_ci } else if (c->frequency <= 111000000) { 23062306a36Sopenharmony_ci n_div = 32; 23162306a36Sopenharmony_ci n = 1; 23262306a36Sopenharmony_ci } else if (c->frequency <= 148000000) { 23362306a36Sopenharmony_ci n_div = 24; 23462306a36Sopenharmony_ci n = 2; 23562306a36Sopenharmony_ci } else if (c->frequency <= 222000000) { 23662306a36Sopenharmony_ci n_div = 16; 23762306a36Sopenharmony_ci n = 3; 23862306a36Sopenharmony_ci } else if (c->frequency <= 296000000) { 23962306a36Sopenharmony_ci n_div = 12; 24062306a36Sopenharmony_ci n = 4; 24162306a36Sopenharmony_ci } else if (c->frequency <= 445000000) { 24262306a36Sopenharmony_ci n_div = 8; 24362306a36Sopenharmony_ci n = 5; 24462306a36Sopenharmony_ci } else if (c->frequency <= dev->fn_min) { 24562306a36Sopenharmony_ci n_div = 6; 24662306a36Sopenharmony_ci n = 6; 24762306a36Sopenharmony_ci } else if (c->frequency <= 950000000) { 24862306a36Sopenharmony_ci n_div = 4; 24962306a36Sopenharmony_ci n = 7; 25062306a36Sopenharmony_ci } else { 25162306a36Sopenharmony_ci n_div = 2; 25262306a36Sopenharmony_ci n = 0; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci ret = regmap_read(dev->regmap, 0x80ed81, &utmp); 25662306a36Sopenharmony_ci if (ret) 25762306a36Sopenharmony_ci goto err; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci iqik_m_cal = utmp * n_div; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (utmp < 0x20) { 26262306a36Sopenharmony_ci if (dev->clk_mode == 0) 26362306a36Sopenharmony_ci iqik_m_cal = (iqik_m_cal * 9) >> 5; 26462306a36Sopenharmony_ci else 26562306a36Sopenharmony_ci iqik_m_cal >>= 1; 26662306a36Sopenharmony_ci } else { 26762306a36Sopenharmony_ci iqik_m_cal = 0x40 - iqik_m_cal; 26862306a36Sopenharmony_ci if (dev->clk_mode == 0) 26962306a36Sopenharmony_ci iqik_m_cal = ~((iqik_m_cal * 9) >> 5); 27062306a36Sopenharmony_ci else 27162306a36Sopenharmony_ci iqik_m_cal = ~(iqik_m_cal >> 1); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci t_cal_freq = (c->frequency / 1000) * n_div * dev->fdiv; 27562306a36Sopenharmony_ci pre_lo_freq = t_cal_freq / dev->xtal; 27662306a36Sopenharmony_ci utmp = pre_lo_freq * dev->xtal; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if ((t_cal_freq - utmp) >= (dev->xtal >> 1)) 27962306a36Sopenharmony_ci pre_lo_freq++; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci pre_lo_freq += (u32) n << 13; 28262306a36Sopenharmony_ci /* Frequency OMEGA_IQIK_M_CAL_MID*/ 28362306a36Sopenharmony_ci t_cal_freq = pre_lo_freq + (u32)iqik_m_cal; 28462306a36Sopenharmony_ci dev_dbg(&pdev->dev, "t_cal_freq %u, pre_lo_freq %u\n", 28562306a36Sopenharmony_ci t_cal_freq, pre_lo_freq); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (c->frequency <= 440000000) { 28862306a36Sopenharmony_ci l_band = 0; 28962306a36Sopenharmony_ci lna_band = 0; 29062306a36Sopenharmony_ci } else if (c->frequency <= 484000000) { 29162306a36Sopenharmony_ci l_band = 1; 29262306a36Sopenharmony_ci lna_band = 1; 29362306a36Sopenharmony_ci } else if (c->frequency <= 533000000) { 29462306a36Sopenharmony_ci l_band = 1; 29562306a36Sopenharmony_ci lna_band = 2; 29662306a36Sopenharmony_ci } else if (c->frequency <= 587000000) { 29762306a36Sopenharmony_ci l_band = 1; 29862306a36Sopenharmony_ci lna_band = 3; 29962306a36Sopenharmony_ci } else if (c->frequency <= 645000000) { 30062306a36Sopenharmony_ci l_band = 1; 30162306a36Sopenharmony_ci lna_band = 4; 30262306a36Sopenharmony_ci } else if (c->frequency <= 710000000) { 30362306a36Sopenharmony_ci l_band = 1; 30462306a36Sopenharmony_ci lna_band = 5; 30562306a36Sopenharmony_ci } else if (c->frequency <= 782000000) { 30662306a36Sopenharmony_ci l_band = 1; 30762306a36Sopenharmony_ci lna_band = 6; 30862306a36Sopenharmony_ci } else if (c->frequency <= 860000000) { 30962306a36Sopenharmony_ci l_band = 1; 31062306a36Sopenharmony_ci lna_band = 7; 31162306a36Sopenharmony_ci } else if (c->frequency <= 1492000000) { 31262306a36Sopenharmony_ci l_band = 1; 31362306a36Sopenharmony_ci lna_band = 0; 31462306a36Sopenharmony_ci } else if (c->frequency <= 1685000000) { 31562306a36Sopenharmony_ci l_band = 1; 31662306a36Sopenharmony_ci lna_band = 1; 31762306a36Sopenharmony_ci } else { 31862306a36Sopenharmony_ci ret = -EINVAL; 31962306a36Sopenharmony_ci goto err; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* XXX: latest windows driver does not set that at all */ 32362306a36Sopenharmony_ci ret = regmap_write(dev->regmap, 0x80ee06, lna_band); 32462306a36Sopenharmony_ci if (ret) 32562306a36Sopenharmony_ci goto err; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (c->bandwidth_hz <= 5000000) 32862306a36Sopenharmony_ci u8tmp = 0; 32962306a36Sopenharmony_ci else if (c->bandwidth_hz <= 6000000) 33062306a36Sopenharmony_ci u8tmp = 2; 33162306a36Sopenharmony_ci else if (c->bandwidth_hz <= 7000000) 33262306a36Sopenharmony_ci u8tmp = 4; 33362306a36Sopenharmony_ci else 33462306a36Sopenharmony_ci u8tmp = 6; /* 8000000 */ 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci ret = regmap_write(dev->regmap, 0x80ec56, u8tmp); 33762306a36Sopenharmony_ci if (ret) 33862306a36Sopenharmony_ci goto err; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* XXX: latest windows driver sets different value (a8 != 68) */ 34162306a36Sopenharmony_ci ret = regmap_write(dev->regmap, 0x80ec4c, 0xa0 | (l_band << 3)); 34262306a36Sopenharmony_ci if (ret) 34362306a36Sopenharmony_ci goto err; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci ret = regmap_write(dev->regmap, 0x80ec4d, (t_cal_freq >> 0) & 0xff); 34662306a36Sopenharmony_ci if (ret) 34762306a36Sopenharmony_ci goto err; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci ret = regmap_write(dev->regmap, 0x80ec4e, (t_cal_freq >> 8) & 0xff); 35062306a36Sopenharmony_ci if (ret) 35162306a36Sopenharmony_ci goto err; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci ret = regmap_write(dev->regmap, 0x80011e, (pre_lo_freq >> 0) & 0xff); 35462306a36Sopenharmony_ci if (ret) 35562306a36Sopenharmony_ci goto err; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci ret = regmap_write(dev->regmap, 0x80011f, (pre_lo_freq >> 8) & 0xff); 35862306a36Sopenharmony_ci if (ret) 35962306a36Sopenharmony_ci goto err; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci return 0; 36262306a36Sopenharmony_cierr: 36362306a36Sopenharmony_ci dev_dbg(&pdev->dev, "failed %d\n", ret); 36462306a36Sopenharmony_ci return ret; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic const struct dvb_tuner_ops it913x_tuner_ops = { 36862306a36Sopenharmony_ci .info = { 36962306a36Sopenharmony_ci .name = "ITE IT913X", 37062306a36Sopenharmony_ci .frequency_min_hz = 174 * MHz, 37162306a36Sopenharmony_ci .frequency_max_hz = 862 * MHz, 37262306a36Sopenharmony_ci }, 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci .init = it913x_init, 37562306a36Sopenharmony_ci .sleep = it913x_sleep, 37662306a36Sopenharmony_ci .set_params = it913x_set_params, 37762306a36Sopenharmony_ci}; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic int it913x_probe(struct platform_device *pdev) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct it913x_platform_data *pdata = pdev->dev.platform_data; 38262306a36Sopenharmony_ci struct dvb_frontend *fe = pdata->fe; 38362306a36Sopenharmony_ci struct it913x_dev *dev; 38462306a36Sopenharmony_ci const struct platform_device_id *id = platform_get_device_id(pdev); 38562306a36Sopenharmony_ci int ret; 38662306a36Sopenharmony_ci char *chip_ver_str; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci dev = kzalloc(sizeof(struct it913x_dev), GFP_KERNEL); 38962306a36Sopenharmony_ci if (dev == NULL) { 39062306a36Sopenharmony_ci ret = -ENOMEM; 39162306a36Sopenharmony_ci dev_err(&pdev->dev, "kzalloc() failed\n"); 39262306a36Sopenharmony_ci goto err; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci dev->pdev = pdev; 39662306a36Sopenharmony_ci dev->regmap = pdata->regmap; 39762306a36Sopenharmony_ci dev->fe = pdata->fe; 39862306a36Sopenharmony_ci dev->chip_ver = id->driver_data; 39962306a36Sopenharmony_ci dev->role = pdata->role; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci fe->tuner_priv = dev; 40262306a36Sopenharmony_ci memcpy(&fe->ops.tuner_ops, &it913x_tuner_ops, 40362306a36Sopenharmony_ci sizeof(struct dvb_tuner_ops)); 40462306a36Sopenharmony_ci platform_set_drvdata(pdev, dev); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (dev->chip_ver == 1) 40762306a36Sopenharmony_ci chip_ver_str = "AX"; 40862306a36Sopenharmony_ci else if (dev->chip_ver == 2) 40962306a36Sopenharmony_ci chip_ver_str = "BX"; 41062306a36Sopenharmony_ci else 41162306a36Sopenharmony_ci chip_ver_str = "??"; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci dev_info(&pdev->dev, "ITE IT913X %s successfully attached\n", 41462306a36Sopenharmony_ci chip_ver_str); 41562306a36Sopenharmony_ci dev_dbg(&pdev->dev, "chip_ver %u, role %u\n", dev->chip_ver, dev->role); 41662306a36Sopenharmony_ci return 0; 41762306a36Sopenharmony_cierr: 41862306a36Sopenharmony_ci dev_dbg(&pdev->dev, "failed %d\n", ret); 41962306a36Sopenharmony_ci return ret; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic void it913x_remove(struct platform_device *pdev) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct it913x_dev *dev = platform_get_drvdata(pdev); 42562306a36Sopenharmony_ci struct dvb_frontend *fe = dev->fe; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci dev_dbg(&pdev->dev, "\n"); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); 43062306a36Sopenharmony_ci fe->tuner_priv = NULL; 43162306a36Sopenharmony_ci kfree(dev); 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic const struct platform_device_id it913x_id_table[] = { 43562306a36Sopenharmony_ci {"it9133ax-tuner", 1}, 43662306a36Sopenharmony_ci {"it9133bx-tuner", 2}, 43762306a36Sopenharmony_ci {}, 43862306a36Sopenharmony_ci}; 43962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, it913x_id_table); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic struct platform_driver it913x_driver = { 44262306a36Sopenharmony_ci .driver = { 44362306a36Sopenharmony_ci .name = "it913x", 44462306a36Sopenharmony_ci .suppress_bind_attrs = true, 44562306a36Sopenharmony_ci }, 44662306a36Sopenharmony_ci .probe = it913x_probe, 44762306a36Sopenharmony_ci .remove_new = it913x_remove, 44862306a36Sopenharmony_ci .id_table = it913x_id_table, 44962306a36Sopenharmony_ci}; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cimodule_platform_driver(it913x_driver); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ciMODULE_DESCRIPTION("ITE IT913X silicon tuner driver"); 45462306a36Sopenharmony_ciMODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); 45562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 456