162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// xc2028 362306a36Sopenharmony_ci// 462306a36Sopenharmony_ci// Copyright (c) 2007-2008 Mauro Carvalho Chehab <mchehab@kernel.org> 562306a36Sopenharmony_ci// 662306a36Sopenharmony_ci// Copyright (c) 2007 Michel Ludwig (michel.ludwig@gmail.com) 762306a36Sopenharmony_ci// - frontend interface 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/i2c.h> 1062306a36Sopenharmony_ci#include <asm/div64.h> 1162306a36Sopenharmony_ci#include <linux/firmware.h> 1262306a36Sopenharmony_ci#include <linux/videodev2.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <media/tuner.h> 1562306a36Sopenharmony_ci#include <linux/mutex.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <asm/unaligned.h> 1862306a36Sopenharmony_ci#include "tuner-i2c.h" 1962306a36Sopenharmony_ci#include "xc2028.h" 2062306a36Sopenharmony_ci#include "xc2028-types.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <linux/dvb/frontend.h> 2362306a36Sopenharmony_ci#include <media/dvb_frontend.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* Max transfer size done by I2C transfer functions */ 2662306a36Sopenharmony_ci#define MAX_XFER_SIZE 80 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* Registers (Write-only) */ 2962306a36Sopenharmony_ci#define XREG_INIT 0x00 3062306a36Sopenharmony_ci#define XREG_RF_FREQ 0x02 3162306a36Sopenharmony_ci#define XREG_POWER_DOWN 0x08 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* Registers (Read-only) */ 3462306a36Sopenharmony_ci#define XREG_FREQ_ERROR 0x01 3562306a36Sopenharmony_ci#define XREG_LOCK 0x02 3662306a36Sopenharmony_ci#define XREG_VERSION 0x04 3762306a36Sopenharmony_ci#define XREG_PRODUCT_ID 0x08 3862306a36Sopenharmony_ci#define XREG_HSYNC_FREQ 0x10 3962306a36Sopenharmony_ci#define XREG_FRAME_LINES 0x20 4062306a36Sopenharmony_ci#define XREG_SNR 0x40 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define XREG_ADC_ENV 0x0100 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic int debug; 4562306a36Sopenharmony_cimodule_param(debug, int, 0644); 4662306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "enable verbose debug messages"); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int no_poweroff; 4962306a36Sopenharmony_cimodule_param(no_poweroff, int, 0644); 5062306a36Sopenharmony_ciMODULE_PARM_DESC(no_poweroff, "0 (default) powers device off when not used.\n" 5162306a36Sopenharmony_ci "1 keep device energized and with tuner ready all the times.\n" 5262306a36Sopenharmony_ci " Faster, but consumes more power and keeps the device hotter\n"); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic char audio_std[8]; 5562306a36Sopenharmony_cimodule_param_string(audio_std, audio_std, sizeof(audio_std), 0); 5662306a36Sopenharmony_ciMODULE_PARM_DESC(audio_std, 5762306a36Sopenharmony_ci "Audio standard. XC3028 audio decoder explicitly needs to know what audio\n" 5862306a36Sopenharmony_ci "standard is needed for some video standards with audio A2 or NICAM.\n" 5962306a36Sopenharmony_ci "The valid values are:\n" 6062306a36Sopenharmony_ci "A2\n" 6162306a36Sopenharmony_ci "A2/A\n" 6262306a36Sopenharmony_ci "A2/B\n" 6362306a36Sopenharmony_ci "NICAM\n" 6462306a36Sopenharmony_ci "NICAM/A\n" 6562306a36Sopenharmony_ci "NICAM/B\n"); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic char firmware_name[30]; 6862306a36Sopenharmony_cimodule_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0); 6962306a36Sopenharmony_ciMODULE_PARM_DESC(firmware_name, 7062306a36Sopenharmony_ci "Firmware file name. Allows overriding the default firmware name\n"); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic LIST_HEAD(hybrid_tuner_instance_list); 7362306a36Sopenharmony_cistatic DEFINE_MUTEX(xc2028_list_mutex); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* struct for storing firmware table */ 7662306a36Sopenharmony_cistruct firmware_description { 7762306a36Sopenharmony_ci unsigned int type; 7862306a36Sopenharmony_ci v4l2_std_id id; 7962306a36Sopenharmony_ci __u16 int_freq; 8062306a36Sopenharmony_ci unsigned char *ptr; 8162306a36Sopenharmony_ci unsigned int size; 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistruct firmware_properties { 8562306a36Sopenharmony_ci unsigned int type; 8662306a36Sopenharmony_ci v4l2_std_id id; 8762306a36Sopenharmony_ci v4l2_std_id std_req; 8862306a36Sopenharmony_ci __u16 int_freq; 8962306a36Sopenharmony_ci unsigned int scode_table; 9062306a36Sopenharmony_ci int scode_nr; 9162306a36Sopenharmony_ci}; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cienum xc2028_state { 9462306a36Sopenharmony_ci XC2028_NO_FIRMWARE = 0, 9562306a36Sopenharmony_ci XC2028_WAITING_FIRMWARE, 9662306a36Sopenharmony_ci XC2028_ACTIVE, 9762306a36Sopenharmony_ci XC2028_SLEEP, 9862306a36Sopenharmony_ci XC2028_NODEV, 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistruct xc2028_data { 10262306a36Sopenharmony_ci struct list_head hybrid_tuner_instance_list; 10362306a36Sopenharmony_ci struct tuner_i2c_props i2c_props; 10462306a36Sopenharmony_ci __u32 frequency; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci enum xc2028_state state; 10762306a36Sopenharmony_ci const char *fname; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci struct firmware_description *firm; 11062306a36Sopenharmony_ci int firm_size; 11162306a36Sopenharmony_ci __u16 firm_version; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci __u16 hwmodel; 11462306a36Sopenharmony_ci __u16 hwvers; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci struct xc2028_ctrl ctrl; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci struct firmware_properties cur_fw; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci struct mutex lock; 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci#define i2c_send(priv, buf, size) ({ \ 12462306a36Sopenharmony_ci int _rc; \ 12562306a36Sopenharmony_ci _rc = tuner_i2c_xfer_send(&priv->i2c_props, buf, size); \ 12662306a36Sopenharmony_ci if (size != _rc) \ 12762306a36Sopenharmony_ci tuner_info("i2c output error: rc = %d (should be %d)\n",\ 12862306a36Sopenharmony_ci _rc, (int)size); \ 12962306a36Sopenharmony_ci if (priv->ctrl.msleep) \ 13062306a36Sopenharmony_ci msleep(priv->ctrl.msleep); \ 13162306a36Sopenharmony_ci _rc; \ 13262306a36Sopenharmony_ci}) 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci#define i2c_send_recv(priv, obuf, osize, ibuf, isize) ({ \ 13562306a36Sopenharmony_ci int _rc; \ 13662306a36Sopenharmony_ci _rc = tuner_i2c_xfer_send_recv(&priv->i2c_props, obuf, osize, \ 13762306a36Sopenharmony_ci ibuf, isize); \ 13862306a36Sopenharmony_ci if (isize != _rc) \ 13962306a36Sopenharmony_ci tuner_err("i2c input error: rc = %d (should be %d)\n", \ 14062306a36Sopenharmony_ci _rc, (int)isize); \ 14162306a36Sopenharmony_ci if (priv->ctrl.msleep) \ 14262306a36Sopenharmony_ci msleep(priv->ctrl.msleep); \ 14362306a36Sopenharmony_ci _rc; \ 14462306a36Sopenharmony_ci}) 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci#define send_seq(priv, data...) ({ \ 14762306a36Sopenharmony_ci static u8 _val[] = data; \ 14862306a36Sopenharmony_ci int _rc; \ 14962306a36Sopenharmony_ci if (sizeof(_val) != \ 15062306a36Sopenharmony_ci (_rc = tuner_i2c_xfer_send(&priv->i2c_props, \ 15162306a36Sopenharmony_ci _val, sizeof(_val)))) { \ 15262306a36Sopenharmony_ci tuner_err("Error on line %d: %d\n", __LINE__, _rc); \ 15362306a36Sopenharmony_ci } else if (priv->ctrl.msleep) \ 15462306a36Sopenharmony_ci msleep(priv->ctrl.msleep); \ 15562306a36Sopenharmony_ci _rc; \ 15662306a36Sopenharmony_ci}) 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci unsigned char buf[2]; 16162306a36Sopenharmony_ci unsigned char ibuf[2]; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci tuner_dbg("%s %04x called\n", __func__, reg); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci buf[0] = reg >> 8; 16662306a36Sopenharmony_ci buf[1] = (unsigned char) reg; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (i2c_send_recv(priv, buf, 2, ibuf, 2) != 2) 16962306a36Sopenharmony_ci return -EIO; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci *val = (ibuf[1]) | (ibuf[0] << 8); 17262306a36Sopenharmony_ci return 0; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci#define dump_firm_type(t) dump_firm_type_and_int_freq(t, 0) 17662306a36Sopenharmony_cistatic void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci if (type & BASE) 17962306a36Sopenharmony_ci printk(KERN_CONT "BASE "); 18062306a36Sopenharmony_ci if (type & INIT1) 18162306a36Sopenharmony_ci printk(KERN_CONT "INIT1 "); 18262306a36Sopenharmony_ci if (type & F8MHZ) 18362306a36Sopenharmony_ci printk(KERN_CONT "F8MHZ "); 18462306a36Sopenharmony_ci if (type & MTS) 18562306a36Sopenharmony_ci printk(KERN_CONT "MTS "); 18662306a36Sopenharmony_ci if (type & D2620) 18762306a36Sopenharmony_ci printk(KERN_CONT "D2620 "); 18862306a36Sopenharmony_ci if (type & D2633) 18962306a36Sopenharmony_ci printk(KERN_CONT "D2633 "); 19062306a36Sopenharmony_ci if (type & DTV6) 19162306a36Sopenharmony_ci printk(KERN_CONT "DTV6 "); 19262306a36Sopenharmony_ci if (type & QAM) 19362306a36Sopenharmony_ci printk(KERN_CONT "QAM "); 19462306a36Sopenharmony_ci if (type & DTV7) 19562306a36Sopenharmony_ci printk(KERN_CONT "DTV7 "); 19662306a36Sopenharmony_ci if (type & DTV78) 19762306a36Sopenharmony_ci printk(KERN_CONT "DTV78 "); 19862306a36Sopenharmony_ci if (type & DTV8) 19962306a36Sopenharmony_ci printk(KERN_CONT "DTV8 "); 20062306a36Sopenharmony_ci if (type & FM) 20162306a36Sopenharmony_ci printk(KERN_CONT "FM "); 20262306a36Sopenharmony_ci if (type & INPUT1) 20362306a36Sopenharmony_ci printk(KERN_CONT "INPUT1 "); 20462306a36Sopenharmony_ci if (type & LCD) 20562306a36Sopenharmony_ci printk(KERN_CONT "LCD "); 20662306a36Sopenharmony_ci if (type & NOGD) 20762306a36Sopenharmony_ci printk(KERN_CONT "NOGD "); 20862306a36Sopenharmony_ci if (type & MONO) 20962306a36Sopenharmony_ci printk(KERN_CONT "MONO "); 21062306a36Sopenharmony_ci if (type & ATSC) 21162306a36Sopenharmony_ci printk(KERN_CONT "ATSC "); 21262306a36Sopenharmony_ci if (type & IF) 21362306a36Sopenharmony_ci printk(KERN_CONT "IF "); 21462306a36Sopenharmony_ci if (type & LG60) 21562306a36Sopenharmony_ci printk(KERN_CONT "LG60 "); 21662306a36Sopenharmony_ci if (type & ATI638) 21762306a36Sopenharmony_ci printk(KERN_CONT "ATI638 "); 21862306a36Sopenharmony_ci if (type & OREN538) 21962306a36Sopenharmony_ci printk(KERN_CONT "OREN538 "); 22062306a36Sopenharmony_ci if (type & OREN36) 22162306a36Sopenharmony_ci printk(KERN_CONT "OREN36 "); 22262306a36Sopenharmony_ci if (type & TOYOTA388) 22362306a36Sopenharmony_ci printk(KERN_CONT "TOYOTA388 "); 22462306a36Sopenharmony_ci if (type & TOYOTA794) 22562306a36Sopenharmony_ci printk(KERN_CONT "TOYOTA794 "); 22662306a36Sopenharmony_ci if (type & DIBCOM52) 22762306a36Sopenharmony_ci printk(KERN_CONT "DIBCOM52 "); 22862306a36Sopenharmony_ci if (type & ZARLINK456) 22962306a36Sopenharmony_ci printk(KERN_CONT "ZARLINK456 "); 23062306a36Sopenharmony_ci if (type & CHINA) 23162306a36Sopenharmony_ci printk(KERN_CONT "CHINA "); 23262306a36Sopenharmony_ci if (type & F6MHZ) 23362306a36Sopenharmony_ci printk(KERN_CONT "F6MHZ "); 23462306a36Sopenharmony_ci if (type & INPUT2) 23562306a36Sopenharmony_ci printk(KERN_CONT "INPUT2 "); 23662306a36Sopenharmony_ci if (type & SCODE) 23762306a36Sopenharmony_ci printk(KERN_CONT "SCODE "); 23862306a36Sopenharmony_ci if (type & HAS_IF) 23962306a36Sopenharmony_ci printk(KERN_CONT "HAS_IF_%d ", int_freq); 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic v4l2_std_id parse_audio_std_option(void) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci if (strcasecmp(audio_std, "A2") == 0) 24562306a36Sopenharmony_ci return V4L2_STD_A2; 24662306a36Sopenharmony_ci if (strcasecmp(audio_std, "A2/A") == 0) 24762306a36Sopenharmony_ci return V4L2_STD_A2_A; 24862306a36Sopenharmony_ci if (strcasecmp(audio_std, "A2/B") == 0) 24962306a36Sopenharmony_ci return V4L2_STD_A2_B; 25062306a36Sopenharmony_ci if (strcasecmp(audio_std, "NICAM") == 0) 25162306a36Sopenharmony_ci return V4L2_STD_NICAM; 25262306a36Sopenharmony_ci if (strcasecmp(audio_std, "NICAM/A") == 0) 25362306a36Sopenharmony_ci return V4L2_STD_NICAM_A; 25462306a36Sopenharmony_ci if (strcasecmp(audio_std, "NICAM/B") == 0) 25562306a36Sopenharmony_ci return V4L2_STD_NICAM_B; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic int check_device_status(struct xc2028_data *priv) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci switch (priv->state) { 26362306a36Sopenharmony_ci case XC2028_NO_FIRMWARE: 26462306a36Sopenharmony_ci case XC2028_WAITING_FIRMWARE: 26562306a36Sopenharmony_ci return -EAGAIN; 26662306a36Sopenharmony_ci case XC2028_ACTIVE: 26762306a36Sopenharmony_ci return 1; 26862306a36Sopenharmony_ci case XC2028_SLEEP: 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci case XC2028_NODEV: 27162306a36Sopenharmony_ci return -ENODEV; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci return 0; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic void free_firmware(struct xc2028_data *priv) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci int i; 27962306a36Sopenharmony_ci tuner_dbg("%s called\n", __func__); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* free allocated f/w string */ 28262306a36Sopenharmony_ci if (priv->fname != firmware_name) 28362306a36Sopenharmony_ci kfree(priv->fname); 28462306a36Sopenharmony_ci priv->fname = NULL; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci priv->state = XC2028_NO_FIRMWARE; 28762306a36Sopenharmony_ci memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (!priv->firm) 29062306a36Sopenharmony_ci return; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci for (i = 0; i < priv->firm_size; i++) 29362306a36Sopenharmony_ci kfree(priv->firm[i].ptr); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci kfree(priv->firm); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci priv->firm = NULL; 29862306a36Sopenharmony_ci priv->firm_size = 0; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic int load_all_firmwares(struct dvb_frontend *fe, 30262306a36Sopenharmony_ci const struct firmware *fw) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct xc2028_data *priv = fe->tuner_priv; 30562306a36Sopenharmony_ci const unsigned char *p, *endp; 30662306a36Sopenharmony_ci int rc = 0; 30762306a36Sopenharmony_ci int n, n_array; 30862306a36Sopenharmony_ci char name[33]; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci tuner_dbg("%s called\n", __func__); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci p = fw->data; 31362306a36Sopenharmony_ci endp = p + fw->size; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (fw->size < sizeof(name) - 1 + 2 + 2) { 31662306a36Sopenharmony_ci tuner_err("Error: firmware file %s has invalid size!\n", 31762306a36Sopenharmony_ci priv->fname); 31862306a36Sopenharmony_ci goto corrupt; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci memcpy(name, p, sizeof(name) - 1); 32262306a36Sopenharmony_ci name[sizeof(name) - 1] = 0; 32362306a36Sopenharmony_ci p += sizeof(name) - 1; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci priv->firm_version = get_unaligned_le16(p); 32662306a36Sopenharmony_ci p += 2; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci n_array = get_unaligned_le16(p); 32962306a36Sopenharmony_ci p += 2; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci tuner_info("Loading %d firmware images from %s, type: %s, ver %d.%d\n", 33262306a36Sopenharmony_ci n_array, priv->fname, name, 33362306a36Sopenharmony_ci priv->firm_version >> 8, priv->firm_version & 0xff); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci priv->firm = kcalloc(n_array, sizeof(*priv->firm), GFP_KERNEL); 33662306a36Sopenharmony_ci if (priv->firm == NULL) { 33762306a36Sopenharmony_ci tuner_err("Not enough memory to load firmware file.\n"); 33862306a36Sopenharmony_ci rc = -ENOMEM; 33962306a36Sopenharmony_ci goto err; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci priv->firm_size = n_array; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci n = -1; 34462306a36Sopenharmony_ci while (p < endp) { 34562306a36Sopenharmony_ci __u32 type, size; 34662306a36Sopenharmony_ci v4l2_std_id id; 34762306a36Sopenharmony_ci __u16 int_freq = 0; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci n++; 35062306a36Sopenharmony_ci if (n >= n_array) { 35162306a36Sopenharmony_ci tuner_err("More firmware images in file than were expected!\n"); 35262306a36Sopenharmony_ci goto corrupt; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci /* Checks if there's enough bytes to read */ 35662306a36Sopenharmony_ci if (endp - p < sizeof(type) + sizeof(id) + sizeof(size)) 35762306a36Sopenharmony_ci goto header; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci type = get_unaligned_le32(p); 36062306a36Sopenharmony_ci p += sizeof(type); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci id = get_unaligned_le64(p); 36362306a36Sopenharmony_ci p += sizeof(id); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (type & HAS_IF) { 36662306a36Sopenharmony_ci int_freq = get_unaligned_le16(p); 36762306a36Sopenharmony_ci p += sizeof(int_freq); 36862306a36Sopenharmony_ci if (endp - p < sizeof(size)) 36962306a36Sopenharmony_ci goto header; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci size = get_unaligned_le32(p); 37362306a36Sopenharmony_ci p += sizeof(size); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (!size || size > endp - p) { 37662306a36Sopenharmony_ci tuner_err("Firmware type "); 37762306a36Sopenharmony_ci dump_firm_type(type); 37862306a36Sopenharmony_ci printk(KERN_CONT 37962306a36Sopenharmony_ci "(%x), id %llx is corrupted (size=%zd, expected %d)\n", 38062306a36Sopenharmony_ci type, (unsigned long long)id, (endp - p), size); 38162306a36Sopenharmony_ci goto corrupt; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci priv->firm[n].ptr = kmemdup(p, size, GFP_KERNEL); 38562306a36Sopenharmony_ci if (priv->firm[n].ptr == NULL) { 38662306a36Sopenharmony_ci tuner_err("Not enough memory to load firmware file.\n"); 38762306a36Sopenharmony_ci rc = -ENOMEM; 38862306a36Sopenharmony_ci goto err; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci tuner_dbg("Reading firmware type "); 39162306a36Sopenharmony_ci if (debug) { 39262306a36Sopenharmony_ci dump_firm_type_and_int_freq(type, int_freq); 39362306a36Sopenharmony_ci printk(KERN_CONT "(%x), id %llx, size=%d.\n", 39462306a36Sopenharmony_ci type, (unsigned long long)id, size); 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci priv->firm[n].type = type; 39862306a36Sopenharmony_ci priv->firm[n].id = id; 39962306a36Sopenharmony_ci priv->firm[n].size = size; 40062306a36Sopenharmony_ci priv->firm[n].int_freq = int_freq; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci p += size; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (n + 1 != priv->firm_size) { 40662306a36Sopenharmony_ci tuner_err("Firmware file is incomplete!\n"); 40762306a36Sopenharmony_ci goto corrupt; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci goto done; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ciheader: 41362306a36Sopenharmony_ci tuner_err("Firmware header is incomplete!\n"); 41462306a36Sopenharmony_cicorrupt: 41562306a36Sopenharmony_ci rc = -EINVAL; 41662306a36Sopenharmony_ci tuner_err("Error: firmware file is corrupted!\n"); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cierr: 41962306a36Sopenharmony_ci tuner_info("Releasing partially loaded firmware file.\n"); 42062306a36Sopenharmony_ci free_firmware(priv); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cidone: 42362306a36Sopenharmony_ci if (rc == 0) 42462306a36Sopenharmony_ci tuner_dbg("Firmware files loaded.\n"); 42562306a36Sopenharmony_ci else 42662306a36Sopenharmony_ci priv->state = XC2028_NODEV; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci return rc; 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic int seek_firmware(struct dvb_frontend *fe, unsigned int type, 43262306a36Sopenharmony_ci v4l2_std_id *id) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct xc2028_data *priv = fe->tuner_priv; 43562306a36Sopenharmony_ci int i, best_i = -1, best_nr_matches = 0; 43662306a36Sopenharmony_ci unsigned int type_mask = 0; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci tuner_dbg("%s called, want type=", __func__); 43962306a36Sopenharmony_ci if (debug) { 44062306a36Sopenharmony_ci dump_firm_type(type); 44162306a36Sopenharmony_ci printk(KERN_CONT "(%x), id %016llx.\n", 44262306a36Sopenharmony_ci type, (unsigned long long)*id); 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (!priv->firm) { 44662306a36Sopenharmony_ci tuner_err("Error! firmware not loaded\n"); 44762306a36Sopenharmony_ci return -EINVAL; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (((type & ~SCODE) == 0) && (*id == 0)) 45162306a36Sopenharmony_ci *id = V4L2_STD_PAL; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (type & BASE) 45462306a36Sopenharmony_ci type_mask = BASE_TYPES; 45562306a36Sopenharmony_ci else if (type & SCODE) { 45662306a36Sopenharmony_ci type &= SCODE_TYPES; 45762306a36Sopenharmony_ci type_mask = SCODE_TYPES & ~HAS_IF; 45862306a36Sopenharmony_ci } else if (type & DTV_TYPES) 45962306a36Sopenharmony_ci type_mask = DTV_TYPES; 46062306a36Sopenharmony_ci else if (type & STD_SPECIFIC_TYPES) 46162306a36Sopenharmony_ci type_mask = STD_SPECIFIC_TYPES; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci type &= type_mask; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (!(type & SCODE)) 46662306a36Sopenharmony_ci type_mask = ~0; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* Seek for exact match */ 46962306a36Sopenharmony_ci for (i = 0; i < priv->firm_size; i++) { 47062306a36Sopenharmony_ci if ((type == (priv->firm[i].type & type_mask)) && 47162306a36Sopenharmony_ci (*id == priv->firm[i].id)) 47262306a36Sopenharmony_ci goto found; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* Seek for generic video standard match */ 47662306a36Sopenharmony_ci for (i = 0; i < priv->firm_size; i++) { 47762306a36Sopenharmony_ci v4l2_std_id match_mask; 47862306a36Sopenharmony_ci int nr_matches; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (type != (priv->firm[i].type & type_mask)) 48162306a36Sopenharmony_ci continue; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci match_mask = *id & priv->firm[i].id; 48462306a36Sopenharmony_ci if (!match_mask) 48562306a36Sopenharmony_ci continue; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if ((*id & match_mask) == *id) 48862306a36Sopenharmony_ci goto found; /* Supports all the requested standards */ 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci nr_matches = hweight64(match_mask); 49162306a36Sopenharmony_ci if (nr_matches > best_nr_matches) { 49262306a36Sopenharmony_ci best_nr_matches = nr_matches; 49362306a36Sopenharmony_ci best_i = i; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (best_nr_matches > 0) { 49862306a36Sopenharmony_ci tuner_dbg("Selecting best matching firmware (%d bits) for type=", 49962306a36Sopenharmony_ci best_nr_matches); 50062306a36Sopenharmony_ci dump_firm_type(type); 50162306a36Sopenharmony_ci printk(KERN_CONT 50262306a36Sopenharmony_ci "(%x), id %016llx:\n", type, (unsigned long long)*id); 50362306a36Sopenharmony_ci i = best_i; 50462306a36Sopenharmony_ci goto found; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /*FIXME: Would make sense to seek for type "hint" match ? */ 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci i = -ENOENT; 51062306a36Sopenharmony_ci goto ret; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cifound: 51362306a36Sopenharmony_ci *id = priv->firm[i].id; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ciret: 51662306a36Sopenharmony_ci tuner_dbg("%s firmware for type=", (i < 0) ? "Can't find" : "Found"); 51762306a36Sopenharmony_ci if (debug) { 51862306a36Sopenharmony_ci dump_firm_type(type); 51962306a36Sopenharmony_ci printk(KERN_CONT "(%x), id %016llx.\n", 52062306a36Sopenharmony_ci type, (unsigned long long)*id); 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci return i; 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic inline int do_tuner_callback(struct dvb_frontend *fe, int cmd, int arg) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct xc2028_data *priv = fe->tuner_priv; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* analog side (tuner-core) uses i2c_adap->algo_data. 53062306a36Sopenharmony_ci * digital side is not guaranteed to have algo_data defined. 53162306a36Sopenharmony_ci * 53262306a36Sopenharmony_ci * digital side will always have fe->dvb defined. 53362306a36Sopenharmony_ci * analog side (tuner-core) doesn't (yet) define fe->dvb. 53462306a36Sopenharmony_ci */ 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci return (!fe->callback) ? -EINVAL : 53762306a36Sopenharmony_ci fe->callback(((fe->dvb) && (fe->dvb->priv)) ? 53862306a36Sopenharmony_ci fe->dvb->priv : priv->i2c_props.adap->algo_data, 53962306a36Sopenharmony_ci DVB_FRONTEND_COMPONENT_TUNER, cmd, arg); 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic int load_firmware(struct dvb_frontend *fe, unsigned int type, 54362306a36Sopenharmony_ci v4l2_std_id *id) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci struct xc2028_data *priv = fe->tuner_priv; 54662306a36Sopenharmony_ci int pos, rc; 54762306a36Sopenharmony_ci unsigned char *p, *endp, buf[MAX_XFER_SIZE]; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (priv->ctrl.max_len > sizeof(buf)) 55062306a36Sopenharmony_ci priv->ctrl.max_len = sizeof(buf); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci tuner_dbg("%s called\n", __func__); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci pos = seek_firmware(fe, type, id); 55562306a36Sopenharmony_ci if (pos < 0) 55662306a36Sopenharmony_ci return pos; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci tuner_info("Loading firmware for type="); 55962306a36Sopenharmony_ci dump_firm_type(priv->firm[pos].type); 56062306a36Sopenharmony_ci printk(KERN_CONT "(%x), id %016llx.\n", 56162306a36Sopenharmony_ci priv->firm[pos].type, (unsigned long long)*id); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci p = priv->firm[pos].ptr; 56462306a36Sopenharmony_ci endp = p + priv->firm[pos].size; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci while (p < endp) { 56762306a36Sopenharmony_ci __u16 size; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* Checks if there's enough bytes to read */ 57062306a36Sopenharmony_ci if (p + sizeof(size) > endp) { 57162306a36Sopenharmony_ci tuner_err("Firmware chunk size is wrong\n"); 57262306a36Sopenharmony_ci return -EINVAL; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci size = le16_to_cpu(*(__le16 *) p); 57662306a36Sopenharmony_ci p += sizeof(size); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (size == 0xffff) 57962306a36Sopenharmony_ci return 0; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (!size) { 58262306a36Sopenharmony_ci /* Special callback command received */ 58362306a36Sopenharmony_ci rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0); 58462306a36Sopenharmony_ci if (rc < 0) { 58562306a36Sopenharmony_ci tuner_err("Error at RESET code %d\n", 58662306a36Sopenharmony_ci (*p) & 0x7f); 58762306a36Sopenharmony_ci return -EINVAL; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci continue; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci if (size >= 0xff00) { 59262306a36Sopenharmony_ci switch (size) { 59362306a36Sopenharmony_ci case 0xff00: 59462306a36Sopenharmony_ci rc = do_tuner_callback(fe, XC2028_RESET_CLK, 0); 59562306a36Sopenharmony_ci if (rc < 0) { 59662306a36Sopenharmony_ci tuner_err("Error at RESET code %d\n", 59762306a36Sopenharmony_ci (*p) & 0x7f); 59862306a36Sopenharmony_ci return -EINVAL; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci break; 60162306a36Sopenharmony_ci default: 60262306a36Sopenharmony_ci tuner_info("Invalid RESET code %d\n", 60362306a36Sopenharmony_ci size & 0x7f); 60462306a36Sopenharmony_ci return -EINVAL; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci continue; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci /* Checks for a sleep command */ 61162306a36Sopenharmony_ci if (size & 0x8000) { 61262306a36Sopenharmony_ci msleep(size & 0x7fff); 61362306a36Sopenharmony_ci continue; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci if ((size + p > endp)) { 61762306a36Sopenharmony_ci tuner_err("missing bytes: need %d, have %zd\n", 61862306a36Sopenharmony_ci size, (endp - p)); 61962306a36Sopenharmony_ci return -EINVAL; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci buf[0] = *p; 62362306a36Sopenharmony_ci p++; 62462306a36Sopenharmony_ci size--; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* Sends message chunks */ 62762306a36Sopenharmony_ci while (size > 0) { 62862306a36Sopenharmony_ci int len = (size < priv->ctrl.max_len - 1) ? 62962306a36Sopenharmony_ci size : priv->ctrl.max_len - 1; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci memcpy(buf + 1, p, len); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci rc = i2c_send(priv, buf, len + 1); 63462306a36Sopenharmony_ci if (rc < 0) { 63562306a36Sopenharmony_ci tuner_err("%d returned from send\n", rc); 63662306a36Sopenharmony_ci return -EINVAL; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci p += len; 64062306a36Sopenharmony_ci size -= len; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci /* silently fail if the frontend doesn't support I2C flush */ 64462306a36Sopenharmony_ci rc = do_tuner_callback(fe, XC2028_I2C_FLUSH, 0); 64562306a36Sopenharmony_ci if ((rc < 0) && (rc != -EINVAL)) { 64662306a36Sopenharmony_ci tuner_err("error executing flush: %d\n", rc); 64762306a36Sopenharmony_ci return rc; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci return 0; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic int load_scode(struct dvb_frontend *fe, unsigned int type, 65462306a36Sopenharmony_ci v4l2_std_id *id, __u16 int_freq, int scode) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci struct xc2028_data *priv = fe->tuner_priv; 65762306a36Sopenharmony_ci int pos, rc; 65862306a36Sopenharmony_ci unsigned char *p; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci tuner_dbg("%s called\n", __func__); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (!int_freq) { 66362306a36Sopenharmony_ci pos = seek_firmware(fe, type, id); 66462306a36Sopenharmony_ci if (pos < 0) 66562306a36Sopenharmony_ci return pos; 66662306a36Sopenharmony_ci } else { 66762306a36Sopenharmony_ci for (pos = 0; pos < priv->firm_size; pos++) { 66862306a36Sopenharmony_ci if ((priv->firm[pos].int_freq == int_freq) && 66962306a36Sopenharmony_ci (priv->firm[pos].type & HAS_IF)) 67062306a36Sopenharmony_ci break; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci if (pos == priv->firm_size) 67362306a36Sopenharmony_ci return -ENOENT; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci p = priv->firm[pos].ptr; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (priv->firm[pos].type & HAS_IF) { 67962306a36Sopenharmony_ci if (priv->firm[pos].size != 12 * 16 || scode >= 16) 68062306a36Sopenharmony_ci return -EINVAL; 68162306a36Sopenharmony_ci p += 12 * scode; 68262306a36Sopenharmony_ci } else { 68362306a36Sopenharmony_ci /* 16 SCODE entries per file; each SCODE entry is 12 bytes and 68462306a36Sopenharmony_ci * has a 2-byte size header in the firmware format. */ 68562306a36Sopenharmony_ci if (priv->firm[pos].size != 14 * 16 || scode >= 16 || 68662306a36Sopenharmony_ci le16_to_cpu(*(__le16 *)(p + 14 * scode)) != 12) 68762306a36Sopenharmony_ci return -EINVAL; 68862306a36Sopenharmony_ci p += 14 * scode + 2; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci tuner_info("Loading SCODE for type="); 69262306a36Sopenharmony_ci dump_firm_type_and_int_freq(priv->firm[pos].type, 69362306a36Sopenharmony_ci priv->firm[pos].int_freq); 69462306a36Sopenharmony_ci printk(KERN_CONT "(%x), id %016llx.\n", priv->firm[pos].type, 69562306a36Sopenharmony_ci (unsigned long long)*id); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci if (priv->firm_version < 0x0202) 69862306a36Sopenharmony_ci rc = send_seq(priv, {0x20, 0x00, 0x00, 0x00}); 69962306a36Sopenharmony_ci else 70062306a36Sopenharmony_ci rc = send_seq(priv, {0xa0, 0x00, 0x00, 0x00}); 70162306a36Sopenharmony_ci if (rc < 0) 70262306a36Sopenharmony_ci return -EIO; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci rc = i2c_send(priv, p, 12); 70562306a36Sopenharmony_ci if (rc < 0) 70662306a36Sopenharmony_ci return -EIO; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci rc = send_seq(priv, {0x00, 0x8c}); 70962306a36Sopenharmony_ci if (rc < 0) 71062306a36Sopenharmony_ci return -EIO; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci return 0; 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic int xc2028_sleep(struct dvb_frontend *fe); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic int check_firmware(struct dvb_frontend *fe, unsigned int type, 71862306a36Sopenharmony_ci v4l2_std_id std, __u16 int_freq) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci struct xc2028_data *priv = fe->tuner_priv; 72162306a36Sopenharmony_ci struct firmware_properties new_fw; 72262306a36Sopenharmony_ci int rc, retry_count = 0; 72362306a36Sopenharmony_ci u16 version, hwmodel; 72462306a36Sopenharmony_ci v4l2_std_id std0; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci tuner_dbg("%s called\n", __func__); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci rc = check_device_status(priv); 72962306a36Sopenharmony_ci if (rc < 0) 73062306a36Sopenharmony_ci return rc; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (priv->ctrl.mts && !(type & FM)) 73362306a36Sopenharmony_ci type |= MTS; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ciretry: 73662306a36Sopenharmony_ci new_fw.type = type; 73762306a36Sopenharmony_ci new_fw.id = std; 73862306a36Sopenharmony_ci new_fw.std_req = std; 73962306a36Sopenharmony_ci new_fw.scode_table = SCODE | priv->ctrl.scode_table; 74062306a36Sopenharmony_ci new_fw.scode_nr = 0; 74162306a36Sopenharmony_ci new_fw.int_freq = int_freq; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci tuner_dbg("checking firmware, user requested type="); 74462306a36Sopenharmony_ci if (debug) { 74562306a36Sopenharmony_ci dump_firm_type(new_fw.type); 74662306a36Sopenharmony_ci printk(KERN_CONT "(%x), id %016llx, ", new_fw.type, 74762306a36Sopenharmony_ci (unsigned long long)new_fw.std_req); 74862306a36Sopenharmony_ci if (!int_freq) { 74962306a36Sopenharmony_ci printk(KERN_CONT "scode_tbl "); 75062306a36Sopenharmony_ci dump_firm_type(priv->ctrl.scode_table); 75162306a36Sopenharmony_ci printk(KERN_CONT "(%x), ", priv->ctrl.scode_table); 75262306a36Sopenharmony_ci } else 75362306a36Sopenharmony_ci printk(KERN_CONT "int_freq %d, ", new_fw.int_freq); 75462306a36Sopenharmony_ci printk(KERN_CONT "scode_nr %d\n", new_fw.scode_nr); 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci /* 75862306a36Sopenharmony_ci * No need to reload base firmware if it matches and if the tuner 75962306a36Sopenharmony_ci * is not at sleep mode 76062306a36Sopenharmony_ci */ 76162306a36Sopenharmony_ci if ((priv->state == XC2028_ACTIVE) && 76262306a36Sopenharmony_ci (((BASE | new_fw.type) & BASE_TYPES) == 76362306a36Sopenharmony_ci (priv->cur_fw.type & BASE_TYPES))) { 76462306a36Sopenharmony_ci tuner_dbg("BASE firmware not changed.\n"); 76562306a36Sopenharmony_ci goto skip_base; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci /* Updating BASE - forget about all currently loaded firmware */ 76962306a36Sopenharmony_ci memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci /* Reset is needed before loading firmware */ 77262306a36Sopenharmony_ci rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0); 77362306a36Sopenharmony_ci if (rc < 0) 77462306a36Sopenharmony_ci goto fail; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci /* BASE firmwares are all std0 */ 77762306a36Sopenharmony_ci std0 = 0; 77862306a36Sopenharmony_ci rc = load_firmware(fe, BASE | new_fw.type, &std0); 77962306a36Sopenharmony_ci if (rc < 0) { 78062306a36Sopenharmony_ci tuner_err("Error %d while loading base firmware\n", 78162306a36Sopenharmony_ci rc); 78262306a36Sopenharmony_ci goto fail; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci /* Load INIT1, if needed */ 78662306a36Sopenharmony_ci tuner_dbg("Load init1 firmware, if exists\n"); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci rc = load_firmware(fe, BASE | INIT1 | new_fw.type, &std0); 78962306a36Sopenharmony_ci if (rc == -ENOENT) 79062306a36Sopenharmony_ci rc = load_firmware(fe, (BASE | INIT1 | new_fw.type) & ~F8MHZ, 79162306a36Sopenharmony_ci &std0); 79262306a36Sopenharmony_ci if (rc < 0 && rc != -ENOENT) { 79362306a36Sopenharmony_ci tuner_err("Error %d while loading init1 firmware\n", 79462306a36Sopenharmony_ci rc); 79562306a36Sopenharmony_ci goto fail; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ciskip_base: 79962306a36Sopenharmony_ci /* 80062306a36Sopenharmony_ci * No need to reload standard specific firmware if base firmware 80162306a36Sopenharmony_ci * was not reloaded and requested video standards have not changed. 80262306a36Sopenharmony_ci */ 80362306a36Sopenharmony_ci if (priv->cur_fw.type == (BASE | new_fw.type) && 80462306a36Sopenharmony_ci priv->cur_fw.std_req == std) { 80562306a36Sopenharmony_ci tuner_dbg("Std-specific firmware already loaded.\n"); 80662306a36Sopenharmony_ci goto skip_std_specific; 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci /* Reloading std-specific firmware forces a SCODE update */ 81062306a36Sopenharmony_ci priv->cur_fw.scode_table = 0; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci rc = load_firmware(fe, new_fw.type, &new_fw.id); 81362306a36Sopenharmony_ci if (rc == -ENOENT) 81462306a36Sopenharmony_ci rc = load_firmware(fe, new_fw.type & ~F8MHZ, &new_fw.id); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (rc < 0) 81762306a36Sopenharmony_ci goto fail; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ciskip_std_specific: 82062306a36Sopenharmony_ci if (priv->cur_fw.scode_table == new_fw.scode_table && 82162306a36Sopenharmony_ci priv->cur_fw.scode_nr == new_fw.scode_nr) { 82262306a36Sopenharmony_ci tuner_dbg("SCODE firmware already loaded.\n"); 82362306a36Sopenharmony_ci goto check_device; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (new_fw.type & FM) 82762306a36Sopenharmony_ci goto check_device; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci /* Load SCODE firmware, if exists */ 83062306a36Sopenharmony_ci tuner_dbg("Trying to load scode %d\n", new_fw.scode_nr); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci rc = load_scode(fe, new_fw.type | new_fw.scode_table, &new_fw.id, 83362306a36Sopenharmony_ci new_fw.int_freq, new_fw.scode_nr); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_cicheck_device: 83662306a36Sopenharmony_ci if (xc2028_get_reg(priv, 0x0004, &version) < 0 || 83762306a36Sopenharmony_ci xc2028_get_reg(priv, 0x0008, &hwmodel) < 0) { 83862306a36Sopenharmony_ci tuner_err("Unable to read tuner registers.\n"); 83962306a36Sopenharmony_ci goto fail; 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci tuner_dbg("Device is Xceive %d version %d.%d, firmware version %d.%d\n", 84362306a36Sopenharmony_ci hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8, 84462306a36Sopenharmony_ci (version & 0xf0) >> 4, version & 0xf); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (priv->ctrl.read_not_reliable) 84862306a36Sopenharmony_ci goto read_not_reliable; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci /* Check firmware version against what we downloaded. */ 85162306a36Sopenharmony_ci if (priv->firm_version != ((version & 0xf0) << 4 | (version & 0x0f))) { 85262306a36Sopenharmony_ci if (!priv->ctrl.read_not_reliable) { 85362306a36Sopenharmony_ci tuner_err("Incorrect readback of firmware version.\n"); 85462306a36Sopenharmony_ci goto fail; 85562306a36Sopenharmony_ci } else { 85662306a36Sopenharmony_ci tuner_err("Returned an incorrect version. However, read is not reliable enough. Ignoring it.\n"); 85762306a36Sopenharmony_ci hwmodel = 3028; 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci /* Check that the tuner hardware model remains consistent over time. */ 86262306a36Sopenharmony_ci if (priv->hwmodel == 0 && (hwmodel == 2028 || hwmodel == 3028)) { 86362306a36Sopenharmony_ci priv->hwmodel = hwmodel; 86462306a36Sopenharmony_ci priv->hwvers = version & 0xff00; 86562306a36Sopenharmony_ci } else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel || 86662306a36Sopenharmony_ci priv->hwvers != (version & 0xff00)) { 86762306a36Sopenharmony_ci tuner_err("Read invalid device hardware information - tuner hung?\n"); 86862306a36Sopenharmony_ci goto fail; 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ciread_not_reliable: 87262306a36Sopenharmony_ci priv->cur_fw = new_fw; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci /* 87562306a36Sopenharmony_ci * By setting BASE in cur_fw.type only after successfully loading all 87662306a36Sopenharmony_ci * firmwares, we can: 87762306a36Sopenharmony_ci * 1. Identify that BASE firmware with type=0 has been loaded; 87862306a36Sopenharmony_ci * 2. Tell whether BASE firmware was just changed the next time through. 87962306a36Sopenharmony_ci */ 88062306a36Sopenharmony_ci priv->cur_fw.type |= BASE; 88162306a36Sopenharmony_ci priv->state = XC2028_ACTIVE; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci return 0; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cifail: 88662306a36Sopenharmony_ci free_firmware(priv); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci if (retry_count < 8) { 88962306a36Sopenharmony_ci msleep(50); 89062306a36Sopenharmony_ci retry_count++; 89162306a36Sopenharmony_ci tuner_dbg("Retrying firmware load\n"); 89262306a36Sopenharmony_ci goto retry; 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci /* Firmware didn't load. Put the device to sleep */ 89662306a36Sopenharmony_ci xc2028_sleep(fe); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (rc == -ENOENT) 89962306a36Sopenharmony_ci rc = -EINVAL; 90062306a36Sopenharmony_ci return rc; 90162306a36Sopenharmony_ci} 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cistatic int xc2028_signal(struct dvb_frontend *fe, u16 *strength) 90462306a36Sopenharmony_ci{ 90562306a36Sopenharmony_ci struct xc2028_data *priv = fe->tuner_priv; 90662306a36Sopenharmony_ci u16 frq_lock, signal = 0; 90762306a36Sopenharmony_ci int rc, i; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci tuner_dbg("%s called\n", __func__); 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci rc = check_device_status(priv); 91262306a36Sopenharmony_ci if (rc < 0) 91362306a36Sopenharmony_ci return rc; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci /* If the device is sleeping, no channel is tuned */ 91662306a36Sopenharmony_ci if (!rc) { 91762306a36Sopenharmony_ci *strength = 0; 91862306a36Sopenharmony_ci return 0; 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci mutex_lock(&priv->lock); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* Sync Lock Indicator */ 92462306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 92562306a36Sopenharmony_ci rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock); 92662306a36Sopenharmony_ci if (rc < 0) 92762306a36Sopenharmony_ci goto ret; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci if (frq_lock) 93062306a36Sopenharmony_ci break; 93162306a36Sopenharmony_ci msleep(6); 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci /* Frequency didn't lock */ 93562306a36Sopenharmony_ci if (frq_lock == 2) 93662306a36Sopenharmony_ci goto ret; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci /* Get SNR of the video signal */ 93962306a36Sopenharmony_ci rc = xc2028_get_reg(priv, XREG_SNR, &signal); 94062306a36Sopenharmony_ci if (rc < 0) 94162306a36Sopenharmony_ci goto ret; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci /* Signal level is 3 bits only */ 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci signal = ((1 << 12) - 1) | ((signal & 0x07) << 12); 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ciret: 94862306a36Sopenharmony_ci mutex_unlock(&priv->lock); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci *strength = signal; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci tuner_dbg("signal strength is %d\n", signal); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci return rc; 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_cistatic int xc2028_get_afc(struct dvb_frontend *fe, s32 *afc) 95862306a36Sopenharmony_ci{ 95962306a36Sopenharmony_ci struct xc2028_data *priv = fe->tuner_priv; 96062306a36Sopenharmony_ci int i, rc; 96162306a36Sopenharmony_ci u16 frq_lock = 0; 96262306a36Sopenharmony_ci s16 afc_reg = 0; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci rc = check_device_status(priv); 96562306a36Sopenharmony_ci if (rc < 0) 96662306a36Sopenharmony_ci return rc; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci /* If the device is sleeping, no channel is tuned */ 96962306a36Sopenharmony_ci if (!rc) { 97062306a36Sopenharmony_ci *afc = 0; 97162306a36Sopenharmony_ci return 0; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci mutex_lock(&priv->lock); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* Sync Lock Indicator */ 97762306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 97862306a36Sopenharmony_ci rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock); 97962306a36Sopenharmony_ci if (rc < 0) 98062306a36Sopenharmony_ci goto ret; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci if (frq_lock) 98362306a36Sopenharmony_ci break; 98462306a36Sopenharmony_ci msleep(6); 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci /* Frequency didn't lock */ 98862306a36Sopenharmony_ci if (frq_lock == 2) 98962306a36Sopenharmony_ci goto ret; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci /* Get AFC */ 99262306a36Sopenharmony_ci rc = xc2028_get_reg(priv, XREG_FREQ_ERROR, &afc_reg); 99362306a36Sopenharmony_ci if (rc < 0) 99462306a36Sopenharmony_ci goto ret; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci *afc = afc_reg * 15625; /* Hz */ 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci tuner_dbg("AFC is %d Hz\n", *afc); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ciret: 100162306a36Sopenharmony_ci mutex_unlock(&priv->lock); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci return rc; 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci#define DIV 15625 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_cistatic int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */, 100962306a36Sopenharmony_ci enum v4l2_tuner_type new_type, 101062306a36Sopenharmony_ci unsigned int type, 101162306a36Sopenharmony_ci v4l2_std_id std, 101262306a36Sopenharmony_ci u16 int_freq) 101362306a36Sopenharmony_ci{ 101462306a36Sopenharmony_ci struct xc2028_data *priv = fe->tuner_priv; 101562306a36Sopenharmony_ci int rc = -EINVAL; 101662306a36Sopenharmony_ci unsigned char buf[4]; 101762306a36Sopenharmony_ci u32 div, offset = 0; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci tuner_dbg("%s called\n", __func__); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci mutex_lock(&priv->lock); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci tuner_dbg("should set frequency %d kHz\n", freq / 1000); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci if (check_firmware(fe, type, std, int_freq) < 0) 102662306a36Sopenharmony_ci goto ret; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci /* On some cases xc2028 can disable video output, if 102962306a36Sopenharmony_ci * very weak signals are received. By sending a soft 103062306a36Sopenharmony_ci * reset, this is re-enabled. So, it is better to always 103162306a36Sopenharmony_ci * send a soft reset before changing channels, to be sure 103262306a36Sopenharmony_ci * that xc2028 will be in a safe state. 103362306a36Sopenharmony_ci * Maybe this might also be needed for DTV. 103462306a36Sopenharmony_ci */ 103562306a36Sopenharmony_ci switch (new_type) { 103662306a36Sopenharmony_ci case V4L2_TUNER_ANALOG_TV: 103762306a36Sopenharmony_ci rc = send_seq(priv, {0x00, 0x00}); 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci /* Analog mode requires offset = 0 */ 104062306a36Sopenharmony_ci break; 104162306a36Sopenharmony_ci case V4L2_TUNER_RADIO: 104262306a36Sopenharmony_ci /* Radio mode requires offset = 0 */ 104362306a36Sopenharmony_ci break; 104462306a36Sopenharmony_ci case V4L2_TUNER_DIGITAL_TV: 104562306a36Sopenharmony_ci /* 104662306a36Sopenharmony_ci * Digital modes require an offset to adjust to the 104762306a36Sopenharmony_ci * proper frequency. The offset depends on what 104862306a36Sopenharmony_ci * firmware version is used. 104962306a36Sopenharmony_ci */ 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci /* 105262306a36Sopenharmony_ci * Adjust to the center frequency. This is calculated by the 105362306a36Sopenharmony_ci * formula: offset = 1.25MHz - BW/2 105462306a36Sopenharmony_ci * For DTV 7/8, the firmware uses BW = 8000, so it needs a 105562306a36Sopenharmony_ci * further adjustment to get the frequency center on VHF 105662306a36Sopenharmony_ci */ 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci /* 105962306a36Sopenharmony_ci * The firmware DTV78 used to work fine in UHF band (8 MHz 106062306a36Sopenharmony_ci * bandwidth) but not at all in VHF band (7 MHz bandwidth). 106162306a36Sopenharmony_ci * The real problem was connected to the formula used to 106262306a36Sopenharmony_ci * calculate the center frequency offset in VHF band. 106362306a36Sopenharmony_ci * In fact, removing the 500KHz adjustment fixed the problem. 106462306a36Sopenharmony_ci * This is coherent to what was implemented for the DTV7 106562306a36Sopenharmony_ci * firmware. 106662306a36Sopenharmony_ci * In the end, now the center frequency is the same for all 3 106762306a36Sopenharmony_ci * firmwares (DTV7, DTV8, DTV78) and doesn't depend on channel 106862306a36Sopenharmony_ci * bandwidth. 106962306a36Sopenharmony_ci */ 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci if (priv->cur_fw.type & DTV6) 107262306a36Sopenharmony_ci offset = 1750000; 107362306a36Sopenharmony_ci else /* DTV7 or DTV8 or DTV78 */ 107462306a36Sopenharmony_ci offset = 2750000; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci /* 107762306a36Sopenharmony_ci * xc3028 additional "magic" 107862306a36Sopenharmony_ci * Depending on the firmware version, it needs some adjustments 107962306a36Sopenharmony_ci * to properly centralize the frequency. This seems to be 108062306a36Sopenharmony_ci * needed to compensate the SCODE table adjustments made by 108162306a36Sopenharmony_ci * newer firmwares 108262306a36Sopenharmony_ci */ 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci /* 108562306a36Sopenharmony_ci * The proper adjustment would be to do it at s-code table. 108662306a36Sopenharmony_ci * However, this didn't work, as reported by 108762306a36Sopenharmony_ci * Robert Lowery <rglowery@exemail.com.au> 108862306a36Sopenharmony_ci */ 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci#if 0 109162306a36Sopenharmony_ci /* 109262306a36Sopenharmony_ci * Still need tests for XC3028L (firmware 3.2 or upper) 109362306a36Sopenharmony_ci * So, for now, let's just comment the per-firmware 109462306a36Sopenharmony_ci * version of this change. Reports with xc3028l working 109562306a36Sopenharmony_ci * with and without the lines below are welcome 109662306a36Sopenharmony_ci */ 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci if (priv->firm_version < 0x0302) { 109962306a36Sopenharmony_ci if (priv->cur_fw.type & DTV7) 110062306a36Sopenharmony_ci offset += 500000; 110162306a36Sopenharmony_ci } else { 110262306a36Sopenharmony_ci if (priv->cur_fw.type & DTV7) 110362306a36Sopenharmony_ci offset -= 300000; 110462306a36Sopenharmony_ci else if (type != ATSC) /* DVB @6MHz, DTV 8 and DTV 7/8 */ 110562306a36Sopenharmony_ci offset += 200000; 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci#endif 110862306a36Sopenharmony_ci break; 110962306a36Sopenharmony_ci default: 111062306a36Sopenharmony_ci tuner_err("Unsupported tuner type %d.\n", new_type); 111162306a36Sopenharmony_ci break; 111262306a36Sopenharmony_ci } 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci div = (freq - offset + DIV / 2) / DIV; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci /* CMD= Set frequency */ 111762306a36Sopenharmony_ci if (priv->firm_version < 0x0202) 111862306a36Sopenharmony_ci rc = send_seq(priv, {0x00, XREG_RF_FREQ, 0x00, 0x00}); 111962306a36Sopenharmony_ci else 112062306a36Sopenharmony_ci rc = send_seq(priv, {0x80, XREG_RF_FREQ, 0x00, 0x00}); 112162306a36Sopenharmony_ci if (rc < 0) 112262306a36Sopenharmony_ci goto ret; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci /* Return code shouldn't be checked. 112562306a36Sopenharmony_ci The reset CLK is needed only with tm6000. 112662306a36Sopenharmony_ci Driver should work fine even if this fails. 112762306a36Sopenharmony_ci */ 112862306a36Sopenharmony_ci if (priv->ctrl.msleep) 112962306a36Sopenharmony_ci msleep(priv->ctrl.msleep); 113062306a36Sopenharmony_ci do_tuner_callback(fe, XC2028_RESET_CLK, 1); 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci msleep(10); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci buf[0] = 0xff & (div >> 24); 113562306a36Sopenharmony_ci buf[1] = 0xff & (div >> 16); 113662306a36Sopenharmony_ci buf[2] = 0xff & (div >> 8); 113762306a36Sopenharmony_ci buf[3] = 0xff & (div); 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci rc = i2c_send(priv, buf, sizeof(buf)); 114062306a36Sopenharmony_ci if (rc < 0) 114162306a36Sopenharmony_ci goto ret; 114262306a36Sopenharmony_ci msleep(100); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci priv->frequency = freq; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci tuner_dbg("divisor= %*ph (freq=%d.%03d)\n", 4, buf, 114762306a36Sopenharmony_ci freq / 1000000, (freq % 1000000) / 1000); 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci rc = 0; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ciret: 115262306a36Sopenharmony_ci mutex_unlock(&priv->lock); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci return rc; 115562306a36Sopenharmony_ci} 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_cistatic int xc2028_set_analog_freq(struct dvb_frontend *fe, 115862306a36Sopenharmony_ci struct analog_parameters *p) 115962306a36Sopenharmony_ci{ 116062306a36Sopenharmony_ci struct xc2028_data *priv = fe->tuner_priv; 116162306a36Sopenharmony_ci unsigned int type=0; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci tuner_dbg("%s called\n", __func__); 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci if (p->mode == V4L2_TUNER_RADIO) { 116662306a36Sopenharmony_ci type |= FM; 116762306a36Sopenharmony_ci if (priv->ctrl.input1) 116862306a36Sopenharmony_ci type |= INPUT1; 116962306a36Sopenharmony_ci return generic_set_freq(fe, (625l * p->frequency) / 10, 117062306a36Sopenharmony_ci V4L2_TUNER_RADIO, type, 0, 0); 117162306a36Sopenharmony_ci } 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci /* if std is not defined, choose one */ 117462306a36Sopenharmony_ci if (!p->std) 117562306a36Sopenharmony_ci p->std = V4L2_STD_MN; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci /* PAL/M, PAL/N, PAL/Nc and NTSC variants should use 6MHz firmware */ 117862306a36Sopenharmony_ci if (!(p->std & V4L2_STD_MN)) 117962306a36Sopenharmony_ci type |= F8MHZ; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci /* Add audio hack to std mask */ 118262306a36Sopenharmony_ci p->std |= parse_audio_std_option(); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci return generic_set_freq(fe, 62500l * p->frequency, 118562306a36Sopenharmony_ci V4L2_TUNER_ANALOG_TV, type, p->std, 0); 118662306a36Sopenharmony_ci} 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_cistatic int xc2028_set_params(struct dvb_frontend *fe) 118962306a36Sopenharmony_ci{ 119062306a36Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 119162306a36Sopenharmony_ci u32 delsys = c->delivery_system; 119262306a36Sopenharmony_ci u32 bw = c->bandwidth_hz; 119362306a36Sopenharmony_ci struct xc2028_data *priv = fe->tuner_priv; 119462306a36Sopenharmony_ci int rc; 119562306a36Sopenharmony_ci unsigned int type = 0; 119662306a36Sopenharmony_ci u16 demod = 0; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci tuner_dbg("%s called\n", __func__); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci rc = check_device_status(priv); 120162306a36Sopenharmony_ci if (rc < 0) 120262306a36Sopenharmony_ci return rc; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci switch (delsys) { 120562306a36Sopenharmony_ci case SYS_DVBT: 120662306a36Sopenharmony_ci case SYS_DVBT2: 120762306a36Sopenharmony_ci /* 120862306a36Sopenharmony_ci * The only countries with 6MHz seem to be Taiwan/Uruguay. 120962306a36Sopenharmony_ci * Both seem to require QAM firmware for OFDM decoding 121062306a36Sopenharmony_ci * Tested in Taiwan by Terry Wu <terrywu2009@gmail.com> 121162306a36Sopenharmony_ci */ 121262306a36Sopenharmony_ci if (bw <= 6000000) 121362306a36Sopenharmony_ci type |= QAM; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci switch (priv->ctrl.type) { 121662306a36Sopenharmony_ci case XC2028_D2633: 121762306a36Sopenharmony_ci type |= D2633; 121862306a36Sopenharmony_ci break; 121962306a36Sopenharmony_ci case XC2028_D2620: 122062306a36Sopenharmony_ci type |= D2620; 122162306a36Sopenharmony_ci break; 122262306a36Sopenharmony_ci case XC2028_AUTO: 122362306a36Sopenharmony_ci default: 122462306a36Sopenharmony_ci /* Zarlink seems to need D2633 */ 122562306a36Sopenharmony_ci if (priv->ctrl.demod == XC3028_FE_ZARLINK456) 122662306a36Sopenharmony_ci type |= D2633; 122762306a36Sopenharmony_ci else 122862306a36Sopenharmony_ci type |= D2620; 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci break; 123162306a36Sopenharmony_ci case SYS_ATSC: 123262306a36Sopenharmony_ci /* The only ATSC firmware (at least on v2.7) is D2633 */ 123362306a36Sopenharmony_ci type |= ATSC | D2633; 123462306a36Sopenharmony_ci break; 123562306a36Sopenharmony_ci /* DVB-S and pure QAM (FE_QAM) are not supported */ 123662306a36Sopenharmony_ci default: 123762306a36Sopenharmony_ci return -EINVAL; 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci if (bw <= 6000000) { 124162306a36Sopenharmony_ci type |= DTV6; 124262306a36Sopenharmony_ci priv->ctrl.vhfbw7 = 0; 124362306a36Sopenharmony_ci priv->ctrl.uhfbw8 = 0; 124462306a36Sopenharmony_ci } else if (bw <= 7000000) { 124562306a36Sopenharmony_ci if (c->frequency < 470000000) 124662306a36Sopenharmony_ci priv->ctrl.vhfbw7 = 1; 124762306a36Sopenharmony_ci else 124862306a36Sopenharmony_ci priv->ctrl.uhfbw8 = 0; 124962306a36Sopenharmony_ci type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV7; 125062306a36Sopenharmony_ci type |= F8MHZ; 125162306a36Sopenharmony_ci } else { 125262306a36Sopenharmony_ci if (c->frequency < 470000000) 125362306a36Sopenharmony_ci priv->ctrl.vhfbw7 = 0; 125462306a36Sopenharmony_ci else 125562306a36Sopenharmony_ci priv->ctrl.uhfbw8 = 1; 125662306a36Sopenharmony_ci type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV8; 125762306a36Sopenharmony_ci type |= F8MHZ; 125862306a36Sopenharmony_ci } 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci /* All S-code tables need a 200kHz shift */ 126162306a36Sopenharmony_ci if (priv->ctrl.demod) { 126262306a36Sopenharmony_ci demod = priv->ctrl.demod; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci /* 126562306a36Sopenharmony_ci * Newer firmwares require a 200 kHz offset only for ATSC 126662306a36Sopenharmony_ci */ 126762306a36Sopenharmony_ci if (type == ATSC || priv->firm_version < 0x0302) 126862306a36Sopenharmony_ci demod += 200; 126962306a36Sopenharmony_ci /* 127062306a36Sopenharmony_ci * The DTV7 S-code table needs a 700 kHz shift. 127162306a36Sopenharmony_ci * 127262306a36Sopenharmony_ci * DTV7 is only used in Australia. Germany or Italy may also 127362306a36Sopenharmony_ci * use this firmware after initialization, but a tune to a UHF 127462306a36Sopenharmony_ci * channel should then cause DTV78 to be used. 127562306a36Sopenharmony_ci * 127662306a36Sopenharmony_ci * Unfortunately, on real-field tests, the s-code offset 127762306a36Sopenharmony_ci * didn't work as expected, as reported by 127862306a36Sopenharmony_ci * Robert Lowery <rglowery@exemail.com.au> 127962306a36Sopenharmony_ci */ 128062306a36Sopenharmony_ci } 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci return generic_set_freq(fe, c->frequency, 128362306a36Sopenharmony_ci V4L2_TUNER_DIGITAL_TV, type, 0, demod); 128462306a36Sopenharmony_ci} 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_cistatic int xc2028_sleep(struct dvb_frontend *fe) 128762306a36Sopenharmony_ci{ 128862306a36Sopenharmony_ci struct xc2028_data *priv = fe->tuner_priv; 128962306a36Sopenharmony_ci int rc; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci rc = check_device_status(priv); 129262306a36Sopenharmony_ci if (rc < 0) 129362306a36Sopenharmony_ci return rc; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci /* Device is already in sleep mode */ 129662306a36Sopenharmony_ci if (!rc) 129762306a36Sopenharmony_ci return 0; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci /* Avoid firmware reload on slow devices or if PM disabled */ 130062306a36Sopenharmony_ci if (no_poweroff || priv->ctrl.disable_power_mgmt) 130162306a36Sopenharmony_ci return 0; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci tuner_dbg("Putting xc2028/3028 into poweroff mode.\n"); 130462306a36Sopenharmony_ci if (debug > 1) { 130562306a36Sopenharmony_ci tuner_dbg("Printing sleep stack trace:\n"); 130662306a36Sopenharmony_ci dump_stack(); 130762306a36Sopenharmony_ci } 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci mutex_lock(&priv->lock); 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci if (priv->firm_version < 0x0202) 131262306a36Sopenharmony_ci rc = send_seq(priv, {0x00, XREG_POWER_DOWN, 0x00, 0x00}); 131362306a36Sopenharmony_ci else 131462306a36Sopenharmony_ci rc = send_seq(priv, {0x80, XREG_POWER_DOWN, 0x00, 0x00}); 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci if (rc >= 0) 131762306a36Sopenharmony_ci priv->state = XC2028_SLEEP; 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci mutex_unlock(&priv->lock); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci return rc; 132262306a36Sopenharmony_ci} 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_cistatic void xc2028_dvb_release(struct dvb_frontend *fe) 132562306a36Sopenharmony_ci{ 132662306a36Sopenharmony_ci struct xc2028_data *priv = fe->tuner_priv; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci tuner_dbg("%s called\n", __func__); 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci mutex_lock(&xc2028_list_mutex); 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci /* only perform final cleanup if this is the last instance */ 133362306a36Sopenharmony_ci if (hybrid_tuner_report_instance_count(priv) == 1) 133462306a36Sopenharmony_ci free_firmware(priv); 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci if (priv) 133762306a36Sopenharmony_ci hybrid_tuner_release_state(priv); 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci mutex_unlock(&xc2028_list_mutex); 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci fe->tuner_priv = NULL; 134262306a36Sopenharmony_ci} 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_cistatic int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency) 134562306a36Sopenharmony_ci{ 134662306a36Sopenharmony_ci struct xc2028_data *priv = fe->tuner_priv; 134762306a36Sopenharmony_ci int rc; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci tuner_dbg("%s called\n", __func__); 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci rc = check_device_status(priv); 135262306a36Sopenharmony_ci if (rc < 0) 135362306a36Sopenharmony_ci return rc; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci *frequency = priv->frequency; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci return 0; 135862306a36Sopenharmony_ci} 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_cistatic void load_firmware_cb(const struct firmware *fw, 136162306a36Sopenharmony_ci void *context) 136262306a36Sopenharmony_ci{ 136362306a36Sopenharmony_ci struct dvb_frontend *fe = context; 136462306a36Sopenharmony_ci struct xc2028_data *priv = fe->tuner_priv; 136562306a36Sopenharmony_ci int rc; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci tuner_dbg("request_firmware_nowait(): %s\n", fw ? "OK" : "error"); 136862306a36Sopenharmony_ci if (!fw) { 136962306a36Sopenharmony_ci tuner_err("Could not load firmware %s.\n", priv->fname); 137062306a36Sopenharmony_ci priv->state = XC2028_NODEV; 137162306a36Sopenharmony_ci return; 137262306a36Sopenharmony_ci } 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci rc = load_all_firmwares(fe, fw); 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci release_firmware(fw); 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci if (rc < 0) 137962306a36Sopenharmony_ci return; 138062306a36Sopenharmony_ci priv->state = XC2028_ACTIVE; 138162306a36Sopenharmony_ci} 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_cistatic int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg) 138462306a36Sopenharmony_ci{ 138562306a36Sopenharmony_ci struct xc2028_data *priv = fe->tuner_priv; 138662306a36Sopenharmony_ci struct xc2028_ctrl *p = priv_cfg; 138762306a36Sopenharmony_ci int rc = 0; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci tuner_dbg("%s called\n", __func__); 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci mutex_lock(&priv->lock); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci /* 139462306a36Sopenharmony_ci * Copy the config data. 139562306a36Sopenharmony_ci */ 139662306a36Sopenharmony_ci memcpy(&priv->ctrl, p, sizeof(priv->ctrl)); 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci /* 139962306a36Sopenharmony_ci * If firmware name changed, frees firmware. As free_firmware will 140062306a36Sopenharmony_ci * reset the status to NO_FIRMWARE, this forces a new request_firmware 140162306a36Sopenharmony_ci */ 140262306a36Sopenharmony_ci if (!firmware_name[0] && p->fname && 140362306a36Sopenharmony_ci priv->fname && strcmp(p->fname, priv->fname)) 140462306a36Sopenharmony_ci free_firmware(priv); 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci if (priv->ctrl.max_len < 9) 140762306a36Sopenharmony_ci priv->ctrl.max_len = 13; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci if (priv->state == XC2028_NO_FIRMWARE) { 141062306a36Sopenharmony_ci if (!firmware_name[0]) 141162306a36Sopenharmony_ci priv->fname = kstrdup(p->fname, GFP_KERNEL); 141262306a36Sopenharmony_ci else 141362306a36Sopenharmony_ci priv->fname = firmware_name; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci if (!priv->fname) { 141662306a36Sopenharmony_ci rc = -ENOMEM; 141762306a36Sopenharmony_ci goto unlock; 141862306a36Sopenharmony_ci } 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci rc = request_firmware_nowait(THIS_MODULE, 1, 142162306a36Sopenharmony_ci priv->fname, 142262306a36Sopenharmony_ci priv->i2c_props.adap->dev.parent, 142362306a36Sopenharmony_ci GFP_KERNEL, 142462306a36Sopenharmony_ci fe, load_firmware_cb); 142562306a36Sopenharmony_ci if (rc < 0) { 142662306a36Sopenharmony_ci tuner_err("Failed to request firmware %s\n", 142762306a36Sopenharmony_ci priv->fname); 142862306a36Sopenharmony_ci priv->state = XC2028_NODEV; 142962306a36Sopenharmony_ci } else 143062306a36Sopenharmony_ci priv->state = XC2028_WAITING_FIRMWARE; 143162306a36Sopenharmony_ci } 143262306a36Sopenharmony_ciunlock: 143362306a36Sopenharmony_ci mutex_unlock(&priv->lock); 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci return rc; 143662306a36Sopenharmony_ci} 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_cistatic const struct dvb_tuner_ops xc2028_dvb_tuner_ops = { 143962306a36Sopenharmony_ci .info = { 144062306a36Sopenharmony_ci .name = "Xceive XC3028", 144162306a36Sopenharmony_ci .frequency_min_hz = 42 * MHz, 144262306a36Sopenharmony_ci .frequency_max_hz = 864 * MHz, 144362306a36Sopenharmony_ci .frequency_step_hz = 50 * kHz, 144462306a36Sopenharmony_ci }, 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci .set_config = xc2028_set_config, 144762306a36Sopenharmony_ci .set_analog_params = xc2028_set_analog_freq, 144862306a36Sopenharmony_ci .release = xc2028_dvb_release, 144962306a36Sopenharmony_ci .get_frequency = xc2028_get_frequency, 145062306a36Sopenharmony_ci .get_rf_strength = xc2028_signal, 145162306a36Sopenharmony_ci .get_afc = xc2028_get_afc, 145262306a36Sopenharmony_ci .set_params = xc2028_set_params, 145362306a36Sopenharmony_ci .sleep = xc2028_sleep, 145462306a36Sopenharmony_ci}; 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_cistruct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, 145762306a36Sopenharmony_ci struct xc2028_config *cfg) 145862306a36Sopenharmony_ci{ 145962306a36Sopenharmony_ci struct xc2028_data *priv; 146062306a36Sopenharmony_ci int instance; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci if (debug) 146362306a36Sopenharmony_ci printk(KERN_DEBUG "xc2028: Xcv2028/3028 init called!\n"); 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci if (NULL == cfg) 146662306a36Sopenharmony_ci return NULL; 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci if (!fe) { 146962306a36Sopenharmony_ci printk(KERN_ERR "xc2028: No frontend!\n"); 147062306a36Sopenharmony_ci return NULL; 147162306a36Sopenharmony_ci } 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci mutex_lock(&xc2028_list_mutex); 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci instance = hybrid_tuner_request_state(struct xc2028_data, priv, 147662306a36Sopenharmony_ci hybrid_tuner_instance_list, 147762306a36Sopenharmony_ci cfg->i2c_adap, cfg->i2c_addr, 147862306a36Sopenharmony_ci "xc2028"); 147962306a36Sopenharmony_ci switch (instance) { 148062306a36Sopenharmony_ci case 0: 148162306a36Sopenharmony_ci /* memory allocation failure */ 148262306a36Sopenharmony_ci goto fail; 148362306a36Sopenharmony_ci case 1: 148462306a36Sopenharmony_ci /* new tuner instance */ 148562306a36Sopenharmony_ci priv->ctrl.max_len = 13; 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci mutex_init(&priv->lock); 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci fe->tuner_priv = priv; 149062306a36Sopenharmony_ci break; 149162306a36Sopenharmony_ci case 2: 149262306a36Sopenharmony_ci /* existing tuner instance */ 149362306a36Sopenharmony_ci fe->tuner_priv = priv; 149462306a36Sopenharmony_ci break; 149562306a36Sopenharmony_ci } 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops, 149862306a36Sopenharmony_ci sizeof(xc2028_dvb_tuner_ops)); 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci tuner_info("type set to %s\n", "XCeive xc2028/xc3028 tuner"); 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci if (cfg->ctrl) 150362306a36Sopenharmony_ci xc2028_set_config(fe, cfg->ctrl); 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci mutex_unlock(&xc2028_list_mutex); 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci return fe; 150862306a36Sopenharmony_cifail: 150962306a36Sopenharmony_ci mutex_unlock(&xc2028_list_mutex); 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci xc2028_dvb_release(fe); 151262306a36Sopenharmony_ci return NULL; 151362306a36Sopenharmony_ci} 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xc2028_attach); 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ciMODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver"); 151862306a36Sopenharmony_ciMODULE_AUTHOR("Michel Ludwig <michel.ludwig@gmail.com>"); 151962306a36Sopenharmony_ciMODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@kernel.org>"); 152062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 152162306a36Sopenharmony_ciMODULE_FIRMWARE(XC2028_DEFAULT_FIRMWARE); 152262306a36Sopenharmony_ciMODULE_FIRMWARE(XC3028L_DEFAULT_FIRMWARE); 1523