--- /dev/null
+/*
+ * Driver for the MaxLinear MxL5xx family of tuners/demods
+ *
+ * Copyright (C) 2014-2015 Ralph Metzler <rjkm@metzlerbros.de>
+ * Marcus Metzler <mocm@metzlerbros.de>
+ * developed for Digital Devices GmbH
+ *
+ * based on code:
+ * Copyright (c) 2011-2013 MaxLinear, Inc. All rights reserved
+ * which was released under GPL V2
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/version.h>
+#include <linux/mutex.h>
+#include <linux/vmalloc.h>
+#include <asm/div64.h>
+#include <asm/unaligned.h>
+
+#include "dvb_frontend.h"
+#include "mxl5xx.h"
+#include "mxl5xx_regs.h"
+#include "mxl5xx_defs.h"
+
+#define BYTE0(v) ((v >> 0) & 0xff)
+#define BYTE1(v) ((v >> 8) & 0xff)
+#define BYTE2(v) ((v >> 16) & 0xff)
+#define BYTE3(v) ((v >> 24) & 0xff)
+
+LIST_HEAD(mxllist);
+
+struct mxl_base {
+ struct list_head mxllist;
+ struct list_head mxls;
+
+ u8 adr;
+ struct i2c_adapter *i2c;
+
+ u32 count;
+ u32 type;
+ u32 sku_type;
+ u32 chipversion;
+ u32 clock;
+ u32 fwversion;
+
+ u8 *ts_map;
+ u8 can_clkout;
+ u8 chan_bond;
+ u8 demod_num;
+ u8 tuner_num;
+
+ unsigned long next_tune;
+
+ struct mutex i2c_lock;
+ struct mutex status_lock;
+ struct mutex tune_lock;
+
+ u8 buf[MXL_HYDRA_OEM_MAX_CMD_BUFF_LEN];
+
+ u32 cmd_size;
+ u8 cmd_data[MAX_CMD_DATA];
+};
+
+struct mxl {
+ struct list_head mxl;
+
+ struct mxl_base *base;
+ struct dvb_frontend fe;
+ struct device *i2cdev;
+ u32 demod;
+ u32 tuner;
+ u32 tuner_in_use;
+ u8 xbar[3];
+
+ unsigned long tune_time;
+};
+
+static void convert_endian(u8 flag, u32 size, u8 *d)
+{
+ u32 i;
+
+ if (!flag)
+ return;
+ for (i = 0; i < (size & ~3); i += 4) {
+ d[i + 0] ^= d[i + 3];
+ d[i + 3] ^= d[i + 0];
+ d[i + 0] ^= d[i + 3];
+
+ d[i + 1] ^= d[i + 2];
+ d[i + 2] ^= d[i + 1];
+ d[i + 1] ^= d[i + 2];
+ }
+
+ switch (size & 3) {
+ case 0:
+ case 1:
+ /* do nothing */
+ break;
+ case 2:
+ d[i + 0] ^= d[i + 1];
+ d[i + 1] ^= d[i + 0];
+ d[i + 0] ^= d[i + 1];
+ break;
+
+ case 3:
+ d[i + 0] ^= d[i + 2];
+ d[i + 2] ^= d[i + 0];
+ d[i + 0] ^= d[i + 2];
+ break;
+ }
+
+}
+
+static int i2c_write(struct i2c_adapter *adap, u8 adr,
+ u8 *data, u32 len)
+{
+ struct i2c_msg msg = {.addr = adr, .flags = 0,
+ .buf = data, .len = len};
+
+ return (i2c_transfer(adap, &msg, 1) == 1) ? 0 : -1;
+}
+
+static int i2c_read(struct i2c_adapter *adap, u8 adr,
+ u8 *data, u32 len)
+{
+ struct i2c_msg msg = {.addr = adr, .flags = I2C_M_RD,
+ .buf = data, .len = len};
+
+ return (i2c_transfer(adap, &msg, 1) == 1) ? 0 : -1;
+}
+
+static int i2cread(struct mxl *state, u8 *data, int len)
+{
+ return i2c_read(state->base->i2c, state->base->adr, data, len);
+}
+
+static int i2cwrite(struct mxl *state, u8 *data, int len)
+{
+ return i2c_write(state->base->i2c, state->base->adr, data, len);
+}
+
+static int read_register_unlocked(struct mxl *state, u32 reg, u32 *val)
+{
+ int stat;
+ u8 data[MXL_HYDRA_REG_SIZE_IN_BYTES + MXL_HYDRA_I2C_HDR_SIZE] = {
+ MXL_HYDRA_PLID_REG_READ, 0x04,
+ GET_BYTE(reg, 0), GET_BYTE(reg, 1),
+ GET_BYTE(reg, 2), GET_BYTE(reg, 3),
+ };
+
+ stat = i2cwrite(state, data,
+ MXL_HYDRA_REG_SIZE_IN_BYTES + MXL_HYDRA_I2C_HDR_SIZE);
+ if (stat)
+ dev_err(state->i2cdev, "i2c read error 1\n");
+ if (!stat)
+ stat = i2cread(state, (u8 *) val,
+ MXL_HYDRA_REG_SIZE_IN_BYTES);
+ le32_to_cpus(val);
+ if (stat)
+ dev_err(state->i2cdev, "i2c read error 2\n");
+ return stat;
+}
+
+#define DMA_I2C_INTERRUPT_ADDR 0x8000011C
+#define DMA_INTR_PROT_WR_CMP 0x08
+
+static int send_command(struct mxl *state, u32 size, u8 *buf)
+{
+ int stat;
+ u32 val, count = 10;
+
+ mutex_lock(&state->base->i2c_lock);
+ if (state->base->fwversion > 0x02010109) {
+ read_register_unlocked(state, DMA_I2C_INTERRUPT_ADDR, &val);
+ if (DMA_INTR_PROT_WR_CMP & val)
+ dev_info(state->i2cdev, "%s busy\n", __func__);
+ while ((DMA_INTR_PROT_WR_CMP & val) && --count) {
+ mutex_unlock(&state->base->i2c_lock);
+ usleep_range(1000, 2000);
+ mutex_lock(&state->base->i2c_lock);
+ read_register_unlocked(state, DMA_I2C_INTERRUPT_ADDR,
+ &val);
+ }
+ if (!count) {
+ dev_info(state->i2cdev, "%s busy\n", __func__);
+ mutex_unlock(&state->base->i2c_lock);
+ return -EBUSY;
+ }
+ }
+ stat = i2cwrite(state, buf, size);
+ mutex_unlock(&state->base->i2c_lock);
+ return stat;
+}
+
+static int write_register(struct mxl *state, u32 reg, u32 val)
+{
+ int stat;
+ u8 data[MXL_HYDRA_REG_WRITE_LEN] = {
+ MXL_HYDRA_PLID_REG_WRITE, 0x08,
+ BYTE0(reg), BYTE1(reg), BYTE2(reg), BYTE3(reg),
+ BYTE0(val), BYTE1(val), BYTE2(val), BYTE3(val),
+ };
+ mutex_lock(&state->base->i2c_lock);
+ stat = i2cwrite(state, data, sizeof(data));
+ mutex_unlock(&state->base->i2c_lock);
+ if (stat)
+ dev_err(state->i2cdev, "i2c write error\n");
+ return stat;
+}
+
+static int write_firmware_block(struct mxl *state,
+ u32 reg, u32 size, u8 *reg_data_ptr)
+{
+ int stat;
+ u8 *buf = state->base->buf;
+
+ mutex_lock(&state->base->i2c_lock);
+ buf[0] = MXL_HYDRA_PLID_REG_WRITE;
+ buf[1] = size + 4;
+ buf[2] = GET_BYTE(reg, 0);
+ buf[3] = GET_BYTE(reg, 1);
+ buf[4] = GET_BYTE(reg, 2);
+ buf[5] = GET_BYTE(reg, 3);
+ memcpy(&buf[6], reg_data_ptr, size);
+ stat = i2cwrite(state, buf,
+ MXL_HYDRA_I2C_HDR_SIZE +
+ MXL_HYDRA_REG_SIZE_IN_BYTES + size);
+ mutex_unlock(&state->base->i2c_lock);
+ if (stat)
+ dev_err(state->i2cdev, "fw block write failed\n");
+ return stat;
+}
+
+static int read_register(struct mxl *state, u32 reg, u32 *val)
+{
+ int stat;
+ u8 data[MXL_HYDRA_REG_SIZE_IN_BYTES + MXL_HYDRA_I2C_HDR_SIZE] = {
+ MXL_HYDRA_PLID_REG_READ, 0x04,
+ GET_BYTE(reg, 0), GET_BYTE(reg, 1),
+ GET_BYTE(reg, 2), GET_BYTE(reg, 3),
+ };
+
+ mutex_lock(&state->base->i2c_lock);
+ stat = i2cwrite(state, data,
+ MXL_HYDRA_REG_SIZE_IN_BYTES + MXL_HYDRA_I2C_HDR_SIZE);
+ if (stat)
+ dev_err(state->i2cdev, "i2c read error 1\n");
+ if (!stat)
+ stat = i2cread(state, (u8 *) val,
+ MXL_HYDRA_REG_SIZE_IN_BYTES);
+ mutex_unlock(&state->base->i2c_lock);
+ le32_to_cpus(val);
+ if (stat)
+ dev_err(state->i2cdev, "i2c read error 2\n");
+ return stat;
+}
+
+static int read_register_block(struct mxl *state, u32 reg, u32 size, u8 *data)
+{
+ int stat;
+ u8 *buf = state->base->buf;
+
+ mutex_lock(&state->base->i2c_lock);
+
+ buf[0] = MXL_HYDRA_PLID_REG_READ;
+ buf[1] = size + 4;
+ buf[2] = GET_BYTE(reg, 0);
+ buf[3] = GET_BYTE(reg, 1);
+ buf[4] = GET_BYTE(reg, 2);
+ buf[5] = GET_BYTE(reg, 3);
+ stat = i2cwrite(state, buf,
+ MXL_HYDRA_I2C_HDR_SIZE + MXL_HYDRA_REG_SIZE_IN_BYTES);
+ if (!stat) {
+ stat = i2cread(state, data, size);
+ convert_endian(MXL_ENABLE_BIG_ENDIAN, size, data);
+ }
+ mutex_unlock(&state->base->i2c_lock);
+ return stat;
+}
+
+static int read_by_mnemonic(struct mxl *state,
+ u32 reg, u8 lsbloc, u8 numofbits, u32 *val)
+{
+ u32 data = 0, mask = 0;
+ int stat;
+
+ stat = read_register(state, reg, &data);
+ if (stat)
+ return stat;
+ mask = MXL_GET_REG_MASK_32(lsbloc, numofbits);
+ data &= mask;
+ data >>= lsbloc;
+ *val = data;
+ return 0;
+}
+
+
+static int update_by_mnemonic(struct mxl *state,
+ u32 reg, u8 lsbloc, u8 numofbits, u32 val)
+{
+ u32 data, mask;
+ int stat;
+
+ stat = read_register(state, reg, &data);
+ if (stat)
+ return stat;
+ mask = MXL_GET_REG_MASK_32(lsbloc, numofbits);
+ data = (data & ~mask) | ((val << lsbloc) & mask);
+ stat = write_register(state, reg, data);
+ return stat;
+}
+
+static int firmware_is_alive(struct mxl *state)
+{
+ u32 hb0, hb1;
+
+ if (read_register(state, HYDRA_HEAR_BEAT, &hb0))
+ return 0;
+ msleep(20);
+ if (read_register(state, HYDRA_HEAR_BEAT, &hb1))
+ return 0;
+ if (hb1 == hb0)
+ return 0;
+ return 1;
+}
+
+static int init(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ /* init fe stats */
+ p->strength.len = 1;
+ p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->cnr.len = 1;
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->pre_bit_error.len = 1;
+ p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->pre_bit_count.len = 1;
+ p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_error.len = 1;
+ p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_count.len = 1;
+ p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ return 0;
+}
+
+static void release(struct dvb_frontend *fe)
+{
+ struct mxl *state = fe->demodulator_priv;
+
+ list_del(&state->mxl);
+ /* Release one frontend, two more shall take its place! */
+ state->base->count--;
+ if (state->base->count == 0) {
+ list_del(&state->base->mxllist);
+ kfree(state->base);
+ }
+ kfree(state);
+}
+
+static int get_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_HW;
+}
+
+static int cfg_demod_abort_tune(struct mxl *state)
+{
+ struct MXL_HYDRA_DEMOD_ABORT_TUNE_T abort_tune_cmd;
+ u8 cmd_size = sizeof(abort_tune_cmd);
+ u8 cmd_buff[MXL_HYDRA_OEM_MAX_CMD_BUFF_LEN];
+
+ abort_tune_cmd.demod_id = state->demod;
+ BUILD_HYDRA_CMD(MXL_HYDRA_ABORT_TUNE_CMD, MXL_CMD_WRITE,
+ cmd_size, &abort_tune_cmd, cmd_buff);
+ return send_command(state, cmd_size + MXL_HYDRA_CMD_HEADER_SIZE,
+ &cmd_buff[0]);
+}
+
+static int send_master_cmd(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *cmd)
+{
+ /*struct mxl *state = fe->demodulator_priv;*/
+
+ return 0; /*CfgDemodAbortTune(state);*/
+}
+
+static int set_parameters(struct dvb_frontend *fe)
+{
+ struct mxl *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct MXL_HYDRA_DEMOD_PARAM_T demod_chan_cfg;
+ u8 cmd_size = sizeof(demod_chan_cfg);
+ u8 cmd_buff[MXL_HYDRA_OEM_MAX_CMD_BUFF_LEN];
+ u32 srange = 10;
+ int stat;
+
+ if (p->frequency < 950000 || p->frequency > 2150000)
+ return -EINVAL;
+ if (p->symbol_rate < 1000000 || p->symbol_rate > 45000000)
+ return -EINVAL;
+
+ /* CfgDemodAbortTune(state); */
+
+ switch (p->delivery_system) {
+ case SYS_DSS:
+ demod_chan_cfg.standard = MXL_HYDRA_DSS;
+ demod_chan_cfg.roll_off = MXL_HYDRA_ROLLOFF_AUTO;
+ break;
+ case SYS_DVBS:
+ srange = p->symbol_rate / 1000000;
+ if (srange > 10)
+ srange = 10;
+ demod_chan_cfg.standard = MXL_HYDRA_DVBS;
+ demod_chan_cfg.roll_off = MXL_HYDRA_ROLLOFF_0_35;
+ demod_chan_cfg.modulation_scheme = MXL_HYDRA_MOD_QPSK;
+ demod_chan_cfg.pilots = MXL_HYDRA_PILOTS_OFF;
+ break;
+ case SYS_DVBS2:
+ demod_chan_cfg.standard = MXL_HYDRA_DVBS2;
+ demod_chan_cfg.roll_off = MXL_HYDRA_ROLLOFF_AUTO;
+ demod_chan_cfg.modulation_scheme = MXL_HYDRA_MOD_AUTO;
+ demod_chan_cfg.pilots = MXL_HYDRA_PILOTS_AUTO;
+ /* cfg_scrambler(state); */
+ break;
+ default:
+ return -EINVAL;
+ }
+ demod_chan_cfg.tuner_index = state->tuner;
+ demod_chan_cfg.demod_index = state->demod;
+ demod_chan_cfg.frequency_in_hz = p->frequency * 1000;
+ demod_chan_cfg.symbol_rate_in_hz = p->symbol_rate;
+ demod_chan_cfg.max_carrier_offset_in_mhz = srange;
+ demod_chan_cfg.spectrum_inversion = MXL_HYDRA_SPECTRUM_AUTO;
+ demod_chan_cfg.fec_code_rate = MXL_HYDRA_FEC_AUTO;
+
+ mutex_lock(&state->base->tune_lock);
+ if (time_after(jiffies + msecs_to_jiffies(200),
+ state->base->next_tune))
+ while (time_before(jiffies, state->base->next_tune))
+ usleep_range(10000, 11000);
+ state->base->next_tune = jiffies + msecs_to_jiffies(100);
+ state->tuner_in_use = state->tuner;
+ BUILD_HYDRA_CMD(MXL_HYDRA_DEMOD_SET_PARAM_CMD, MXL_CMD_WRITE,
+ cmd_size, &demod_chan_cfg, cmd_buff);
+ stat = send_command(state, cmd_size + MXL_HYDRA_CMD_HEADER_SIZE,
+ &cmd_buff[0]);
+ mutex_unlock(&state->base->tune_lock);
+ return stat;
+}
+
+static int enable_tuner(struct mxl *state, u32 tuner, u32 enable);
+
+static int sleep(struct dvb_frontend *fe)
+{
+ struct mxl *state = fe->demodulator_priv;
+ struct mxl *p;
+
+ cfg_demod_abort_tune(state);
+ if (state->tuner_in_use != 0xffffffff) {
+ mutex_lock(&state->base->tune_lock);
+ state->tuner_in_use = 0xffffffff;
+ list_for_each_entry(p, &state->base->mxls, mxl) {
+ if (p->tuner_in_use == state->tuner)
+ break;
+ }
+ if (&p->mxl == &state->base->mxls)
+ enable_tuner(state, state->tuner, 0);
+ mutex_unlock(&state->base->tune_lock);
+ }
+ return 0;
+}
+
+static int read_snr(struct dvb_frontend *fe)
+{
+ struct mxl *state = fe->demodulator_priv;
+ int stat;
+ u32 reg_data = 0;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ mutex_lock(&state->base->status_lock);
+ HYDRA_DEMOD_STATUS_LOCK(state, state->demod);
+ stat = read_register(state, (HYDRA_DMD_SNR_ADDR_OFFSET +
+ HYDRA_DMD_STATUS_OFFSET(state->demod)),
+ ®_data);
+ HYDRA_DEMOD_STATUS_UNLOCK(state, state->demod);
+ mutex_unlock(&state->base->status_lock);
+
+ p->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ p->cnr.stat[0].svalue = (s16)reg_data * 10;
+
+ return stat;
+}
+
+static int read_ber(struct dvb_frontend *fe)
+{
+ struct mxl *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ u32 reg[8];
+
+ mutex_lock(&state->base->status_lock);
+ HYDRA_DEMOD_STATUS_LOCK(state, state->demod);
+ read_register_block(state,
+ (HYDRA_DMD_DVBS_1ST_CORR_RS_ERRORS_ADDR_OFFSET +
+ HYDRA_DMD_STATUS_OFFSET(state->demod)),
+ (4 * sizeof(u32)),
+ (u8 *) ®[0]);
+ HYDRA_DEMOD_STATUS_UNLOCK(state, state->demod);
+
+ switch (p->delivery_system) {
+ case SYS_DSS:
+ case SYS_DVBS:
+ p->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->pre_bit_error.stat[0].uvalue = reg[2];
+ p->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ p->pre_bit_count.stat[0].uvalue = reg[3];
+ break;
+ default:
+ break;
+ }
+
+ read_register_block(state,
+ (HYDRA_DMD_DVBS2_CRC_ERRORS_ADDR_OFFSET +
+ HYDRA_DMD_STATUS_OFFSET(state->demod)),
+ (7 * sizeof(u32)),
+ (u8 *) ®[0]);
+
+ switch (p->delivery_system) {
+ case SYS_DSS:
+ case SYS_DVBS:
+ p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->post_bit_error.stat[0].uvalue = reg[5];
+ p->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ p->post_bit_count.stat[0].uvalue = reg[6];
+ break;
+ case SYS_DVBS2:
+ p->post_bit_error.stat[0].scale = FE_SCALE_COUNTER;
+ p->post_bit_error.stat[0].uvalue = reg[1];
+ p->post_bit_count.stat[0].scale = FE_SCALE_COUNTER;
+ p->post_bit_count.stat[0].uvalue = reg[2];
+ break;
+ default:
+ break;
+ }
+
+ mutex_unlock(&state->base->status_lock);
+
+ return 0;
+}
+
+static int read_signal_strength(struct dvb_frontend *fe)
+{
+ struct mxl *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ int stat;
+ u32 reg_data = 0;
+
+ mutex_lock(&state->base->status_lock);
+ HYDRA_DEMOD_STATUS_LOCK(state, state->demod);
+ stat = read_register(state, (HYDRA_DMD_STATUS_INPUT_POWER_ADDR +
+ HYDRA_DMD_STATUS_OFFSET(state->demod)),
+ ®_data);
+ HYDRA_DEMOD_STATUS_UNLOCK(state, state->demod);
+ mutex_unlock(&state->base->status_lock);
+
+ p->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ p->strength.stat[0].svalue = (s16) reg_data * 10; /* fix scale */
+
+ return stat;
+}
+
+static int read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct mxl *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ u32 reg_data = 0;
+
+ mutex_lock(&state->base->status_lock);
+ HYDRA_DEMOD_STATUS_LOCK(state, state->demod);
+ read_register(state, (HYDRA_DMD_LOCK_STATUS_ADDR_OFFSET +
+ HYDRA_DMD_STATUS_OFFSET(state->demod)),
+ ®_data);
+ HYDRA_DEMOD_STATUS_UNLOCK(state, state->demod);
+ mutex_unlock(&state->base->status_lock);
+
+ *status = (reg_data == 1) ? 0x1f : 0;
+
+ /* signal statistics */
+
+ /* signal strength is always available */
+ read_signal_strength(fe);
+
+ if (*status & FE_HAS_CARRIER)
+ read_snr(fe);
+ else
+ p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+
+ if (*status & FE_HAS_SYNC)
+ read_ber(fe);
+ else {
+ p->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ p->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
+ }
+
+ return 0;
+}
+
+static int tune(struct dvb_frontend *fe, bool re_tune,
+ unsigned int mode_flags,
+ unsigned int *delay, enum fe_status *status)
+{
+ struct mxl *state = fe->demodulator_priv;
+ int r = 0;
+
+ *delay = HZ / 2;
+ if (re_tune) {
+ r = set_parameters(fe);
+ if (r)
+ return r;
+ state->tune_time = jiffies;
+ return 0;
+ }
+ if (*status & FE_HAS_LOCK)
+ return 0;
+
+ r = read_status(fe, status);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+static enum fe_code_rate conv_fec(enum MXL_HYDRA_FEC_E fec)
+{
+ enum fe_code_rate fec2fec[11] = {
+ FEC_NONE, FEC_1_2, FEC_3_5, FEC_2_3,
+ FEC_3_4, FEC_4_5, FEC_5_6, FEC_6_7,
+ FEC_7_8, FEC_8_9, FEC_9_10
+ };
+
+ if (fec > MXL_HYDRA_FEC_9_10)
+ return FEC_NONE;
+ return fec2fec[fec];
+}
+
+static int get_frontend(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *p)
+{
+ struct mxl *state = fe->demodulator_priv;
+ u32 reg_data[MXL_DEMOD_CHAN_PARAMS_BUFF_SIZE];
+ u32 freq;
+
+ mutex_lock(&state->base->status_lock);
+ HYDRA_DEMOD_STATUS_LOCK(state, state->demod);
+ read_register_block(state,
+ (HYDRA_DMD_STANDARD_ADDR_OFFSET +
+ HYDRA_DMD_STATUS_OFFSET(state->demod)),
+ (MXL_DEMOD_CHAN_PARAMS_BUFF_SIZE * 4), /* 25 * 4 bytes */
+ (u8 *) ®_data[0]);
+ /* read demod channel parameters */
+ read_register_block(state,
+ (HYDRA_DMD_STATUS_CENTER_FREQ_IN_KHZ_ADDR +
+ HYDRA_DMD_STATUS_OFFSET(state->demod)),
+ (4), /* 4 bytes */
+ (u8 *) &freq);
+ HYDRA_DEMOD_STATUS_UNLOCK(state, state->demod);
+ mutex_unlock(&state->base->status_lock);
+
+ dev_dbg(state->i2cdev, "freq=%u delsys=%u srate=%u\n",
+ freq * 1000, reg_data[DMD_STANDARD_ADDR],
+ reg_data[DMD_SYMBOL_RATE_ADDR]);
+ p->symbol_rate = reg_data[DMD_SYMBOL_RATE_ADDR];
+ p->frequency = freq;
+ /*
+ * p->delivery_system =
+ * (MXL_HYDRA_BCAST_STD_E) regData[DMD_STANDARD_ADDR];
+ * p->inversion =
+ * (MXL_HYDRA_SPECTRUM_E) regData[DMD_SPECTRUM_INVERSION_ADDR];
+ * freqSearchRangeKHz =
+ * (regData[DMD_FREQ_SEARCH_RANGE_IN_KHZ_ADDR]);
+ */
+
+ p->fec_inner = conv_fec(reg_data[DMD_FEC_CODE_RATE_ADDR]);
+ switch (p->delivery_system) {
+ case SYS_DSS:
+ break;
+ case SYS_DVBS2:
+ switch ((enum MXL_HYDRA_PILOTS_E)
+ reg_data[DMD_DVBS2_PILOT_ON_OFF_ADDR]) {
+ case MXL_HYDRA_PILOTS_OFF:
+ p->pilot = PILOT_OFF;
+ break;
+ case MXL_HYDRA_PILOTS_ON:
+ p->pilot = PILOT_ON;
+ break;
+ default:
+ break;
+ }
+ case SYS_DVBS:
+ switch ((enum MXL_HYDRA_MODULATION_E)
+ reg_data[DMD_MODULATION_SCHEME_ADDR]) {
+ case MXL_HYDRA_MOD_QPSK:
+ p->modulation = QPSK;
+ break;
+ case MXL_HYDRA_MOD_8PSK:
+ p->modulation = PSK_8;
+ break;
+ default:
+ break;
+ }
+ switch ((enum MXL_HYDRA_ROLLOFF_E)
+ reg_data[DMD_SPECTRUM_ROLL_OFF_ADDR]) {
+ case MXL_HYDRA_ROLLOFF_0_20:
+ p->rolloff = ROLLOFF_20;
+ break;
+ case MXL_HYDRA_ROLLOFF_0_35:
+ p->rolloff = ROLLOFF_35;
+ break;
+ case MXL_HYDRA_ROLLOFF_0_25:
+ p->rolloff = ROLLOFF_25;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int set_input(struct dvb_frontend *fe, int input)
+{
+ struct mxl *state = fe->demodulator_priv;
+
+ state->tuner = input;
+ return 0;
+}
+
+static struct dvb_frontend_ops mxl_ops = {
+ .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS },
+ .info = {
+ .name = "MaxLinear MxL5xx DVB-S/S2 tuner-demodulator",
+ .frequency_min = 300000,
+ .frequency_max = 2350000,
+ .frequency_stepsize = 0,
+ .frequency_tolerance = 0,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_2G_MODULATION
+ },
+ .init = init,
+ .release = release,
+ .get_frontend_algo = get_algo,
+ .tune = tune,
+ .read_status = read_status,
+ .sleep = sleep,
+ .get_frontend = get_frontend,
+ .diseqc_send_master_cmd = send_master_cmd,
+};
+
+static struct mxl_base *match_base(struct i2c_adapter *i2c, u8 adr)
+{
+ struct mxl_base *p;
+
+ list_for_each_entry(p, &mxllist, mxllist)
+ if (p->i2c == i2c && p->adr == adr)
+ return p;
+ return NULL;
+}
+
+static void cfg_dev_xtal(struct mxl *state, u32 freq, u32 cap, u32 enable)
+{
+ if (state->base->can_clkout || !enable)
+ update_by_mnemonic(state, 0x90200054, 23, 1, enable);
+
+ if (freq == 24000000)
+ write_register(state, HYDRA_CRYSTAL_SETTING, 0);
+ else
+ write_register(state, HYDRA_CRYSTAL_SETTING, 1);
+
+ write_register(state, HYDRA_CRYSTAL_CAP, cap);
+}
+
+static u32 get_big_endian(u8 num_of_bits, const u8 buf[])
+{
+ u32 ret_value = 0;
+
+ switch (num_of_bits) {
+ case 24:
+ ret_value = (((u32) buf[0]) << 16) |
+ (((u32) buf[1]) << 8) | buf[2];
+ break;
+ case 32:
+ ret_value = (((u32) buf[0]) << 24) |
+ (((u32) buf[1]) << 16) |
+ (((u32) buf[2]) << 8) | buf[3];
+ break;
+ default:
+ break;
+ }
+
+ return ret_value;
+}
+
+static int write_fw_segment(struct mxl *state,
+ u32 mem_addr, u32 total_size, u8 *data_ptr)
+{
+ int status;
+ u32 data_count = 0;
+ u32 size = 0;
+ u32 orig_size = 0;
+ u8 *w_buf_ptr = NULL;
+ u32 block_size = ((MXL_HYDRA_OEM_MAX_BLOCK_WRITE_LENGTH -
+ (MXL_HYDRA_I2C_HDR_SIZE +
+ MXL_HYDRA_REG_SIZE_IN_BYTES)) / 4) * 4;
+ u8 w_msg_buffer[MXL_HYDRA_OEM_MAX_BLOCK_WRITE_LENGTH -
+ (MXL_HYDRA_I2C_HDR_SIZE + MXL_HYDRA_REG_SIZE_IN_BYTES)];
+
+ do {
+ size = orig_size = (((u32)(data_count + block_size)) > total_size) ?
+ (total_size - data_count) : block_size;
+
+ if (orig_size & 3)
+ size = (orig_size + 4) & ~3;
+ w_buf_ptr = &w_msg_buffer[0];
+ memset((void *) w_buf_ptr, 0, size);
+ memcpy((void *) w_buf_ptr, (void *) data_ptr, orig_size);
+ convert_endian(1, size, w_buf_ptr);
+ status = write_firmware_block(state, mem_addr, size, w_buf_ptr);
+ if (status)
+ return status;
+ data_count += size;
+ mem_addr += size;
+ data_ptr += size;
+ } while (data_count < total_size);
+
+ return status;
+}
+
+static int do_firmware_download(struct mxl *state, u8 *mbin_buffer_ptr,
+ u32 mbin_buffer_size)
+
+{
+ int status;
+ u32 index = 0;
+ u32 seg_length = 0;
+ u32 seg_address = 0;
+ struct MBIN_FILE_T *mbin_ptr = (struct MBIN_FILE_T *)mbin_buffer_ptr;
+ struct MBIN_SEGMENT_T *segment_ptr;
+ enum MXL_BOOL_E xcpu_fw_flag = MXL_FALSE;
+
+ if (mbin_ptr->header.id != MBIN_FILE_HEADER_ID) {
+ dev_err(state->i2cdev, "%s: Invalid file header ID (%c)\n",
+ __func__, mbin_ptr->header.id);
+ return -EINVAL;
+ }
+ status = write_register(state, FW_DL_SIGN_ADDR, 0);
+ if (status)
+ return status;
+ segment_ptr = (struct MBIN_SEGMENT_T *) (&mbin_ptr->data[0]);
+ for (index = 0; index < mbin_ptr->header.num_segments; index++) {
+ if (segment_ptr->header.id != MBIN_SEGMENT_HEADER_ID) {
+ dev_err(state->i2cdev, "%s: Invalid segment header ID (%c)\n",
+ __func__, segment_ptr->header.id);
+ return -EINVAL;
+ }
+ seg_length = get_big_endian(24,
+ &(segment_ptr->header.len24[0]));
+ seg_address = get_big_endian(32,
+ &(segment_ptr->header.address[0]));
+
+ if (state->base->type == MXL_HYDRA_DEVICE_568) {
+ if ((((seg_address & 0x90760000) == 0x90760000) ||
+ ((seg_address & 0x90740000) == 0x90740000)) &&
+ (xcpu_fw_flag == MXL_FALSE)) {
+ update_by_mnemonic(state, 0x8003003C, 0, 1, 1);
+ msleep(200);
+ write_register(state, 0x90720000, 0);
+ usleep_range(10000, 11000);
+ xcpu_fw_flag = MXL_TRUE;
+ }
+ status = write_fw_segment(state, seg_address,
+ seg_length,
+ (u8 *) segment_ptr->data);
+ } else {
+ if (((seg_address & 0x90760000) != 0x90760000) &&
+ ((seg_address & 0x90740000) != 0x90740000))
+ status = write_fw_segment(state, seg_address,
+ seg_length, (u8 *) segment_ptr->data);
+ }
+ if (status)
+ return status;
+ segment_ptr = (struct MBIN_SEGMENT_T *)
+ &(segment_ptr->data[((seg_length + 3) / 4) * 4]);
+ }
+ return status;
+}
+
+static int check_fw(struct mxl *state, u8 *mbin, u32 mbin_len)
+{
+ struct MBIN_FILE_HEADER_T *fh = (struct MBIN_FILE_HEADER_T *) mbin;
+ u32 flen = (fh->image_size24[0] << 16) |
+ (fh->image_size24[1] << 8) | fh->image_size24[2];
+ u8 *fw, cs = 0;
+ u32 i;
+
+ if (fh->id != 'M' || fh->fmt_version != '1' || flen > 0x3FFF0) {
+ dev_info(state->i2cdev, "Invalid FW Header\n");
+ return -1;
+ }
+ fw = mbin + sizeof(struct MBIN_FILE_HEADER_T);
+ for (i = 0; i < flen; i += 1)
+ cs += fw[i];
+ if (cs != fh->image_checksum) {
+ dev_info(state->i2cdev, "Invalid FW Checksum\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int firmware_download(struct mxl *state, u8 *mbin, u32 mbin_len)
+{
+ int status;
+ u32 reg_data = 0;
+ struct MXL_HYDRA_SKU_COMMAND_T dev_sku_cfg;
+ u8 cmd_size = sizeof(struct MXL_HYDRA_SKU_COMMAND_T);
+ u8 cmd_buff[sizeof(struct MXL_HYDRA_SKU_COMMAND_T) + 6];
+
+ if (check_fw(state, mbin, mbin_len))
+ return -1;
+
+ /* put CPU into reset */
+ status = update_by_mnemonic(state, 0x8003003C, 0, 1, 0);
+ if (status)
+ return status;
+ usleep_range(1000, 2000);
+
+ /* Reset TX FIFO's, BBAND, XBAR */
+ status = write_register(state, HYDRA_RESET_TRANSPORT_FIFO_REG,
+ HYDRA_RESET_TRANSPORT_FIFO_DATA);
+ if (status)
+ return status;
+ status = write_register(state, HYDRA_RESET_BBAND_REG,
+ HYDRA_RESET_BBAND_DATA);
+ if (status)
+ return status;
+ status = write_register(state, HYDRA_RESET_XBAR_REG,
+ HYDRA_RESET_XBAR_DATA);
+ if (status)
+ return status;
+
+ /* Disable clock to Baseband, Wideband, SerDes,
+ * Alias ext & Transport modules
+ */
+ status = write_register(state, HYDRA_MODULES_CLK_2_REG,
+ HYDRA_DISABLE_CLK_2);
+ if (status)
+ return status;
+ /* Clear Software & Host interrupt status - (Clear on read) */
+ status = read_register(state, HYDRA_PRCM_ROOT_CLK_REG, ®_data);
+ if (status)
+ return status;
+ status = do_firmware_download(state, mbin, mbin_len);
+ if (status)
+ return status;
+
+ if (state->base->type == MXL_HYDRA_DEVICE_568) {
+ usleep_range(10000, 11000);
+
+ /* bring XCPU out of reset */
+ status = write_register(state, 0x90720000, 1);
+ if (status)
+ return status;
+ msleep(500);
+
+ /* Enable XCPU UART message processing in MCPU */
+ status = write_register(state, 0x9076B510, 1);
+ if (status)
+ return status;
+ } else {
+ /* Bring CPU out of reset */
+ status = update_by_mnemonic(state, 0x8003003C, 0, 1, 1);
+ if (status)
+ return status;
+ /* Wait until FW boots */
+ msleep(150);
+ }
+
+ /* Initialize XPT XBAR */
+ status = write_register(state, XPT_DMD0_BASEADDR, 0x76543210);
+ if (status)
+ return status;
+
+ if (!firmware_is_alive(state))
+ return -1;
+
+ dev_info(state->i2cdev, "Hydra FW alive. Hail!\n");
+
+ /* sometimes register values are wrong shortly
+ * after first heart beats
+ */
+ msleep(50);
+
+ dev_sku_cfg.sku_type = state->base->sku_type;
+ BUILD_HYDRA_CMD(MXL_HYDRA_DEV_CFG_SKU_CMD, MXL_CMD_WRITE,
+ cmd_size, &dev_sku_cfg, cmd_buff);
+ status = send_command(state, cmd_size + MXL_HYDRA_CMD_HEADER_SIZE,
+ &cmd_buff[0]);
+
+ return status;
+}
+
+static int cfg_ts_pad_mux(struct mxl *state, enum MXL_BOOL_E enable_serial_ts)
+{
+ int status = 0;
+ u32 pad_mux_value = 0;
+
+ if (enable_serial_ts == MXL_TRUE) {
+ pad_mux_value = 0;
+ if ((state->base->type == MXL_HYDRA_DEVICE_541) ||
+ (state->base->type == MXL_HYDRA_DEVICE_541S))
+ pad_mux_value = 2;
+ } else {
+ if ((state->base->type == MXL_HYDRA_DEVICE_581) ||
+ (state->base->type == MXL_HYDRA_DEVICE_581S))
+ pad_mux_value = 2;
+ else
+ pad_mux_value = 3;
+ }
+
+ switch (state->base->type) {
+ case MXL_HYDRA_DEVICE_561:
+ case MXL_HYDRA_DEVICE_581:
+ case MXL_HYDRA_DEVICE_541:
+ case MXL_HYDRA_DEVICE_541S:
+ case MXL_HYDRA_DEVICE_561S:
+ case MXL_HYDRA_DEVICE_581S:
+ status |= update_by_mnemonic(state, 0x90000170, 24, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000170, 28, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000174, 0, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000174, 4, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000174, 8, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000174, 12, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000174, 16, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000174, 20, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000174, 24, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000174, 28, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000178, 0, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000178, 4, 3,
+ pad_mux_value);
+ status |= update_by_mnemonic(state, 0x90000178, 8, 3,
+ pad_mux_value);
+ break;
+
+ case MXL_HYDRA_DEVICE_544:
+ case MXL_HYDRA_DEVICE_542:
+ status |= update_by_mnemonic(state, 0x9000016C, 4, 3, 1);
+ status |= update_by_mnemonic(state, 0x9000016C, 8, 3, 0);
+ status |= update_by_mnemonic(state, 0x9000016C, 12, 3, 0);
+ status |= update_by_mnemonic(state, 0x9000016C, 16, 3, 0);
+ status |= update_by_mnemonic(state, 0x90000170, 0, 3, 0);
+ status |= update_by_mnemonic(state, 0x90000178, 12, 3, 1);
+ status |= update_by_mnemonic(state, 0x90000178, 16, 3, 1);
+ status |= update_by_mnemonic(state, 0x90000178, 20, 3, 1);
+ status |= update_by_mnemonic(state, 0x90000178, 24, 3, 1);
+ status |= update_by_mnemonic(state, 0x9000017C, 0, 3, 1);
+ status |= update_by_mnemonic(state, 0x9000017C, 4, 3, 1);
+ if (enable_serial_ts == MXL_ENABLE) {
+ status |= update_by_mnemonic(state,
+ 0x90000170, 4, 3, 0);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 8, 3, 0);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 12, 3, 0);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 16, 3, 0);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 20, 3, 1);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 24, 3, 1);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 28, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 0, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 4, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 8, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 12, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 16, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 20, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 24, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 28, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 0, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 4, 3, 2);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 8, 3, 2);
+ } else {
+ status |= update_by_mnemonic(state,
+ 0x90000170, 4, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 8, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 12, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 16, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 20, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 24, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 28, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 0, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 4, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 8, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 12, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 16, 3, 3);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 20, 3, 1);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 24, 3, 1);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 28, 3, 1);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 0, 3, 1);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 4, 3, 1);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 8, 3, 1);
+ }
+ break;
+
+ case MXL_HYDRA_DEVICE_568:
+ if (enable_serial_ts == MXL_FALSE) {
+ status |= update_by_mnemonic(state,
+ 0x9000016C, 8, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x9000016C, 12, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x9000016C, 16, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x9000016C, 20, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x9000016C, 24, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x9000016C, 28, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 0, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 4, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 8, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 12, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 16, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 20, 3, 5);
+
+ status |= update_by_mnemonic(state,
+ 0x90000170, 24, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 0, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 4, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 8, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 12, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 16, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 20, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 24, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 28, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 0, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 4, 3, pad_mux_value);
+
+ status |= update_by_mnemonic(state,
+ 0x90000178, 8, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 12, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 16, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 20, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 24, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x90000178, 28, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x9000017C, 0, 3, 5);
+ status |= update_by_mnemonic(state,
+ 0x9000017C, 4, 3, 5);
+ } else {
+ status |= update_by_mnemonic(state,
+ 0x90000170, 4, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 8, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 12, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 16, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 20, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 24, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 28, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 0, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 4, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 8, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 12, 3, pad_mux_value);
+ }
+ break;
+
+
+ case MXL_HYDRA_DEVICE_584:
+ default:
+ status |= update_by_mnemonic(state,
+ 0x90000170, 4, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 8, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 12, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 16, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 20, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 24, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000170, 28, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 0, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 4, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 8, 3, pad_mux_value);
+ status |= update_by_mnemonic(state,
+ 0x90000174, 12, 3, pad_mux_value);
+ break;
+ }
+ return status;
+}
+
+static int set_drive_strength(struct mxl *state,
+ enum MXL_HYDRA_TS_DRIVE_STRENGTH_E ts_drive_strength)
+{
+ int stat = 0;
+ u32 val;
+
+ read_register(state, 0x90000194, &val);
+ dev_info(state->i2cdev, "DIGIO = %08x\n", val);
+ dev_info(state->i2cdev, "set drive_strength = %u\n", ts_drive_strength);
+
+
+ stat |= update_by_mnemonic(state, 0x90000194, 0, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x90000194, 20, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x90000194, 24, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x90000198, 12, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x90000198, 16, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x90000198, 20, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x90000198, 24, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x9000019C, 0, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x9000019C, 4, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x9000019C, 8, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x9000019C, 24, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x9000019C, 28, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x900001A0, 0, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x900001A0, 4, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x900001A0, 20, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x900001A0, 24, 3, ts_drive_strength);
+ stat |= update_by_mnemonic(state, 0x900001A0, 28, 3, ts_drive_strength);
+
+ return stat;
+}
+
+static int enable_tuner(struct mxl *state, u32 tuner, u32 enable)
+{
+ int stat = 0;
+ struct MXL_HYDRA_TUNER_CMD ctrl_tuner_cmd;
+ u8 cmd_size = sizeof(ctrl_tuner_cmd);
+ u8 cmd_buff[MXL_HYDRA_OEM_MAX_CMD_BUFF_LEN];
+ u32 val, count = 10;
+
+ ctrl_tuner_cmd.tuner_id = tuner;
+ ctrl_tuner_cmd.enable = enable;
+ BUILD_HYDRA_CMD(MXL_HYDRA_TUNER_ACTIVATE_CMD, MXL_CMD_WRITE,
+ cmd_size, &ctrl_tuner_cmd, cmd_buff);
+ stat = send_command(state, cmd_size + MXL_HYDRA_CMD_HEADER_SIZE,
+ &cmd_buff[0]);
+ if (stat)
+ return stat;
+ read_register(state, HYDRA_TUNER_ENABLE_COMPLETE, &val);
+ while (--count && ((val >> tuner) & 1) != enable) {
+ msleep(20);
+ read_register(state, HYDRA_TUNER_ENABLE_COMPLETE, &val);
+ }
+ if (!count)
+ return -1;
+ read_register(state, HYDRA_TUNER_ENABLE_COMPLETE, &val);
+ dev_dbg(state->i2cdev, "tuner %u ready = %u\n",
+ tuner, (val >> tuner) & 1);
+
+ return 0;
+}
+
+
+static int config_ts(struct mxl *state, enum MXL_HYDRA_DEMOD_ID_E demod_id,
+ struct MXL_HYDRA_MPEGOUT_PARAM_T *mpeg_out_param_ptr)
+{
+ int status = 0;
+ u32 nco_count_min = 0;
+ u32 clk_type = 0;
+
+ struct MXL_REG_FIELD_T xpt_sync_polarity[MXL_HYDRA_DEMOD_MAX] = {
+ {0x90700010, 8, 1}, {0x90700010, 9, 1},
+ {0x90700010, 10, 1}, {0x90700010, 11, 1},
+ {0x90700010, 12, 1}, {0x90700010, 13, 1},
+ {0x90700010, 14, 1}, {0x90700010, 15, 1} };
+ struct MXL_REG_FIELD_T xpt_clock_polarity[MXL_HYDRA_DEMOD_MAX] = {
+ {0x90700010, 16, 1}, {0x90700010, 17, 1},
+ {0x90700010, 18, 1}, {0x90700010, 19, 1},
+ {0x90700010, 20, 1}, {0x90700010, 21, 1},
+ {0x90700010, 22, 1}, {0x90700010, 23, 1} };
+ struct MXL_REG_FIELD_T xpt_valid_polarity[MXL_HYDRA_DEMOD_MAX] = {
+ {0x90700014, 0, 1}, {0x90700014, 1, 1},
+ {0x90700014, 2, 1}, {0x90700014, 3, 1},
+ {0x90700014, 4, 1}, {0x90700014, 5, 1},
+ {0x90700014, 6, 1}, {0x90700014, 7, 1} };
+ struct MXL_REG_FIELD_T xpt_ts_clock_phase[MXL_HYDRA_DEMOD_MAX] = {
+ {0x90700018, 0, 3}, {0x90700018, 4, 3},
+ {0x90700018, 8, 3}, {0x90700018, 12, 3},
+ {0x90700018, 16, 3}, {0x90700018, 20, 3},
+ {0x90700018, 24, 3}, {0x90700018, 28, 3} };
+ struct MXL_REG_FIELD_T xpt_lsb_first[MXL_HYDRA_DEMOD_MAX] = {
+ {0x9070000C, 16, 1}, {0x9070000C, 17, 1},
+ {0x9070000C, 18, 1}, {0x9070000C, 19, 1},
+ {0x9070000C, 20, 1}, {0x9070000C, 21, 1},
+ {0x9070000C, 22, 1}, {0x9070000C, 23, 1} };
+ struct MXL_REG_FIELD_T xpt_sync_byte[MXL_HYDRA_DEMOD_MAX] = {
+ {0x90700010, 0, 1}, {0x90700010, 1, 1},
+ {0x90700010, 2, 1}, {0x90700010, 3, 1},
+ {0x90700010, 4, 1}, {0x90700010, 5, 1},
+ {0x90700010, 6, 1}, {0x90700010, 7, 1} };
+ struct MXL_REG_FIELD_T xpt_enable_output[MXL_HYDRA_DEMOD_MAX] = {
+ {0x9070000C, 0, 1}, {0x9070000C, 1, 1},
+ {0x9070000C, 2, 1}, {0x9070000C, 3, 1},
+ {0x9070000C, 4, 1}, {0x9070000C, 5, 1},
+ {0x9070000C, 6, 1}, {0x9070000C, 7, 1} };
+ struct MXL_REG_FIELD_T xpt_err_replace_sync[MXL_HYDRA_DEMOD_MAX] = {
+ {0x9070000C, 24, 1}, {0x9070000C, 25, 1},
+ {0x9070000C, 26, 1}, {0x9070000C, 27, 1},
+ {0x9070000C, 28, 1}, {0x9070000C, 29, 1},
+ {0x9070000C, 30, 1}, {0x9070000C, 31, 1} };
+ struct MXL_REG_FIELD_T xpt_err_replace_valid[MXL_HYDRA_DEMOD_MAX] = {
+ {0x90700014, 8, 1}, {0x90700014, 9, 1},
+ {0x90700014, 10, 1}, {0x90700014, 11, 1},
+ {0x90700014, 12, 1}, {0x90700014, 13, 1},
+ {0x90700014, 14, 1}, {0x90700014, 15, 1} };
+ struct MXL_REG_FIELD_T xpt_continuous_clock[MXL_HYDRA_DEMOD_MAX] = {
+ {0x907001D4, 0, 1}, {0x907001D4, 1, 1},
+ {0x907001D4, 2, 1}, {0x907001D4, 3, 1},
+ {0x907001D4, 4, 1}, {0x907001D4, 5, 1},
+ {0x907001D4, 6, 1}, {0x907001D4, 7, 1} };
+ struct MXL_REG_FIELD_T xpt_nco_clock_rate[MXL_HYDRA_DEMOD_MAX] = {
+ {0x90700044, 16, 80}, {0x90700044, 16, 81},
+ {0x90700044, 16, 82}, {0x90700044, 16, 83},
+ {0x90700044, 16, 84}, {0x90700044, 16, 85},
+ {0x90700044, 16, 86}, {0x90700044, 16, 87} };
+
+ demod_id = state->base->ts_map[demod_id];
+
+ if (mpeg_out_param_ptr->enable == MXL_ENABLE) {
+ if (mpeg_out_param_ptr->mpeg_mode ==
+ MXL_HYDRA_MPEG_MODE_PARALLEL) {
+ } else {
+ cfg_ts_pad_mux(state, MXL_TRUE);
+ update_by_mnemonic(state,
+ 0x90700010, 27, 1, MXL_FALSE);
+ }
+ }
+
+ nco_count_min =
+ (u32)(MXL_HYDRA_NCO_CLK / mpeg_out_param_ptr->max_mpeg_clk_rate);
+
+ if (state->base->chipversion >= 2) {
+ status |= update_by_mnemonic(state,
+ xpt_nco_clock_rate[demod_id].reg_addr, /* Reg Addr */
+ xpt_nco_clock_rate[demod_id].lsb_pos, /* LSB pos */
+ xpt_nco_clock_rate[demod_id].num_of_bits, /* Num of bits */
+ nco_count_min); /* Data */
+ } else
+ update_by_mnemonic(state, 0x90700044, 16, 8, nco_count_min);
+
+ if (mpeg_out_param_ptr->mpeg_clk_type == MXL_HYDRA_MPEG_CLK_CONTINUOUS)
+ clk_type = 1;
+
+ if (mpeg_out_param_ptr->mpeg_mode < MXL_HYDRA_MPEG_MODE_PARALLEL) {
+ status |= update_by_mnemonic(state,
+ xpt_continuous_clock[demod_id].reg_addr,
+ xpt_continuous_clock[demod_id].lsb_pos,
+ xpt_continuous_clock[demod_id].num_of_bits,
+ clk_type);
+ } else
+ update_by_mnemonic(state, 0x907001D4, 8, 1, clk_type);
+
+ status |= update_by_mnemonic(state,
+ xpt_sync_polarity[demod_id].reg_addr,
+ xpt_sync_polarity[demod_id].lsb_pos,
+ xpt_sync_polarity[demod_id].num_of_bits,
+ mpeg_out_param_ptr->mpeg_sync_pol);
+
+ status |= update_by_mnemonic(state,
+ xpt_valid_polarity[demod_id].reg_addr,
+ xpt_valid_polarity[demod_id].lsb_pos,
+ xpt_valid_polarity[demod_id].num_of_bits,
+ mpeg_out_param_ptr->mpeg_valid_pol);
+
+ status |= update_by_mnemonic(state,
+ xpt_clock_polarity[demod_id].reg_addr,
+ xpt_clock_polarity[demod_id].lsb_pos,
+ xpt_clock_polarity[demod_id].num_of_bits,
+ mpeg_out_param_ptr->mpeg_clk_pol);
+
+ status |= update_by_mnemonic(state,
+ xpt_sync_byte[demod_id].reg_addr,
+ xpt_sync_byte[demod_id].lsb_pos,
+ xpt_sync_byte[demod_id].num_of_bits,
+ mpeg_out_param_ptr->mpeg_sync_pulse_width);
+
+ status |= update_by_mnemonic(state,
+ xpt_ts_clock_phase[demod_id].reg_addr,
+ xpt_ts_clock_phase[demod_id].lsb_pos,
+ xpt_ts_clock_phase[demod_id].num_of_bits,
+ mpeg_out_param_ptr->mpeg_clk_phase);
+
+ status |= update_by_mnemonic(state,
+ xpt_lsb_first[demod_id].reg_addr,
+ xpt_lsb_first[demod_id].lsb_pos,
+ xpt_lsb_first[demod_id].num_of_bits,
+ mpeg_out_param_ptr->lsb_or_msb_first);
+
+ switch (mpeg_out_param_ptr->mpeg_error_indication) {
+ case MXL_HYDRA_MPEG_ERR_REPLACE_SYNC:
+ status |= update_by_mnemonic(state,
+ xpt_err_replace_sync[demod_id].reg_addr,
+ xpt_err_replace_sync[demod_id].lsb_pos,
+ xpt_err_replace_sync[demod_id].num_of_bits,
+ MXL_TRUE);
+ status |= update_by_mnemonic(state,
+ xpt_err_replace_valid[demod_id].reg_addr,
+ xpt_err_replace_valid[demod_id].lsb_pos,
+ xpt_err_replace_valid[demod_id].num_of_bits,
+ MXL_FALSE);
+ break;
+
+ case MXL_HYDRA_MPEG_ERR_REPLACE_VALID:
+ status |= update_by_mnemonic(state,
+ xpt_err_replace_sync[demod_id].reg_addr,
+ xpt_err_replace_sync[demod_id].lsb_pos,
+ xpt_err_replace_sync[demod_id].num_of_bits,
+ MXL_FALSE);
+
+ status |= update_by_mnemonic(state,
+ xpt_err_replace_valid[demod_id].reg_addr,
+ xpt_err_replace_valid[demod_id].lsb_pos,
+ xpt_err_replace_valid[demod_id].num_of_bits,
+ MXL_TRUE);
+ break;
+
+ case MXL_HYDRA_MPEG_ERR_INDICATION_DISABLED:
+ default:
+ status |= update_by_mnemonic(state,
+ xpt_err_replace_sync[demod_id].reg_addr,
+ xpt_err_replace_sync[demod_id].lsb_pos,
+ xpt_err_replace_sync[demod_id].num_of_bits,
+ MXL_FALSE);
+
+ status |= update_by_mnemonic(state,
+ xpt_err_replace_valid[demod_id].reg_addr,
+ xpt_err_replace_valid[demod_id].lsb_pos,
+ xpt_err_replace_valid[demod_id].num_of_bits,
+ MXL_FALSE);
+
+ break;
+
+ }
+
+ if (mpeg_out_param_ptr->mpeg_mode != MXL_HYDRA_MPEG_MODE_PARALLEL) {
+ status |= update_by_mnemonic(state,
+ xpt_enable_output[demod_id].reg_addr,
+ xpt_enable_output[demod_id].lsb_pos,
+ xpt_enable_output[demod_id].num_of_bits,
+ mpeg_out_param_ptr->enable);
+ }
+ return status;
+}
+
+static int config_mux(struct mxl *state)
+{
+ update_by_mnemonic(state, 0x9070000C, 0, 1, 0);
+ update_by_mnemonic(state, 0x9070000C, 1, 1, 0);
+ update_by_mnemonic(state, 0x9070000C, 2, 1, 0);
+ update_by_mnemonic(state, 0x9070000C, 3, 1, 0);
+ update_by_mnemonic(state, 0x9070000C, 4, 1, 0);
+ update_by_mnemonic(state, 0x9070000C, 5, 1, 0);
+ update_by_mnemonic(state, 0x9070000C, 6, 1, 0);
+ update_by_mnemonic(state, 0x9070000C, 7, 1, 0);
+ update_by_mnemonic(state, 0x90700008, 0, 2, 1);
+ update_by_mnemonic(state, 0x90700008, 2, 2, 1);
+ return 0;
+}
+
+static int load_fw(struct mxl *state, struct mxl5xx_cfg *cfg)
+{
+ int stat = 0;
+ u8 *buf;
+
+ if (cfg->fw)
+ return firmware_download(state, cfg->fw, cfg->fw_len);
+
+ if (!cfg->fw_read)
+ return -1;
+
+ buf = vmalloc(0x40000);
+ if (!buf)
+ return -ENOMEM;
+
+ cfg->fw_read(cfg->fw_priv, buf, 0x40000);
+ stat = firmware_download(state, buf, 0x40000);
+ vfree(buf);
+
+ return stat;
+}
+
+static int validate_sku(struct mxl *state)
+{
+ u32 pad_mux_bond = 0, prcm_chip_id = 0, prcm_so_cid = 0;
+ int status;
+ u32 type = state->base->type;
+
+ status = read_by_mnemonic(state, 0x90000190, 0, 3, &pad_mux_bond);
+ status |= read_by_mnemonic(state, 0x80030000, 0, 12, &prcm_chip_id);
+ status |= read_by_mnemonic(state, 0x80030004, 24, 8, &prcm_so_cid);
+ if (status)
+ return -1;
+
+ dev_info(state->i2cdev, "padMuxBond=%08x, prcmChipId=%08x, prcmSoCId=%08x\n",
+ pad_mux_bond, prcm_chip_id, prcm_so_cid);
+
+ if (prcm_chip_id != 0x560) {
+ switch (pad_mux_bond) {
+ case MXL_HYDRA_SKU_ID_581:
+ if (type == MXL_HYDRA_DEVICE_581)
+ return 0;
+ if (type == MXL_HYDRA_DEVICE_581S) {
+ state->base->type = MXL_HYDRA_DEVICE_581;
+ return 0;
+ }
+ break;
+ case MXL_HYDRA_SKU_ID_584:
+ if (type == MXL_HYDRA_DEVICE_584)
+ return 0;
+ break;
+ case MXL_HYDRA_SKU_ID_544:
+ if (type == MXL_HYDRA_DEVICE_544)
+ return 0;
+ if (type == MXL_HYDRA_DEVICE_542)
+ return 0;
+ break;
+ case MXL_HYDRA_SKU_ID_582:
+ if (type == MXL_HYDRA_DEVICE_582)
+ return 0;
+ break;
+ default:
+ return -1;
+ }
+ } else {
+
+ }
+ return -1;
+}
+
+static int get_fwinfo(struct mxl *state)
+{
+ int status;
+ u32 val = 0;
+
+ status = read_by_mnemonic(state, 0x90000190, 0, 3, &val);
+ if (status)
+ return status;
+ dev_info(state->i2cdev, "chipID=%08x\n", val);
+
+ status = read_by_mnemonic(state, 0x80030004, 8, 8, &val);
+ if (status)
+ return status;
+ dev_info(state->i2cdev, "chipVer=%08x\n", val);
+
+ status = read_register(state, HYDRA_FIRMWARE_VERSION, &val);
+ if (status)
+ return status;
+ dev_info(state->i2cdev, "FWVer=%08x\n", val);
+
+ state->base->fwversion = val;
+ return status;
+}
+
+
+static u8 ts_map1_to_1[MXL_HYDRA_DEMOD_MAX] = {
+ MXL_HYDRA_DEMOD_ID_0,
+ MXL_HYDRA_DEMOD_ID_1,
+ MXL_HYDRA_DEMOD_ID_2,
+ MXL_HYDRA_DEMOD_ID_3,
+ MXL_HYDRA_DEMOD_ID_4,
+ MXL_HYDRA_DEMOD_ID_5,
+ MXL_HYDRA_DEMOD_ID_6,
+ MXL_HYDRA_DEMOD_ID_7,
+};
+
+static u8 ts_map54x[MXL_HYDRA_DEMOD_MAX] = {
+ MXL_HYDRA_DEMOD_ID_2,
+ MXL_HYDRA_DEMOD_ID_3,
+ MXL_HYDRA_DEMOD_ID_4,
+ MXL_HYDRA_DEMOD_ID_5,
+ MXL_HYDRA_DEMOD_MAX,
+ MXL_HYDRA_DEMOD_MAX,
+ MXL_HYDRA_DEMOD_MAX,
+ MXL_HYDRA_DEMOD_MAX,
+};
+
+static int probe(struct mxl *state, struct mxl5xx_cfg *cfg)
+{
+ u32 chipver;
+ int fw, status, j;
+ struct MXL_HYDRA_MPEGOUT_PARAM_T mpeg_interface_cfg;
+
+ state->base->ts_map = ts_map1_to_1;
+
+ switch (state->base->type) {
+ case MXL_HYDRA_DEVICE_581:
+ case MXL_HYDRA_DEVICE_581S:
+ state->base->can_clkout = 1;
+ state->base->demod_num = 8;
+ state->base->tuner_num = 1;
+ state->base->sku_type = MXL_HYDRA_SKU_TYPE_581;
+ break;
+ case MXL_HYDRA_DEVICE_582:
+ state->base->can_clkout = 1;
+ state->base->demod_num = 8;
+ state->base->tuner_num = 3;
+ state->base->sku_type = MXL_HYDRA_SKU_TYPE_582;
+ break;
+ case MXL_HYDRA_DEVICE_585:
+ state->base->can_clkout = 0;
+ state->base->demod_num = 8;
+ state->base->tuner_num = 4;
+ state->base->sku_type = MXL_HYDRA_SKU_TYPE_585;
+ break;
+ case MXL_HYDRA_DEVICE_544:
+ state->base->can_clkout = 0;
+ state->base->demod_num = 4;
+ state->base->tuner_num = 4;
+ state->base->sku_type = MXL_HYDRA_SKU_TYPE_544;
+ state->base->ts_map = ts_map54x;
+ break;
+ case MXL_HYDRA_DEVICE_541:
+ case MXL_HYDRA_DEVICE_541S:
+ state->base->can_clkout = 0;
+ state->base->demod_num = 4;
+ state->base->tuner_num = 1;
+ state->base->sku_type = MXL_HYDRA_SKU_TYPE_541;
+ state->base->ts_map = ts_map54x;
+ break;
+ case MXL_HYDRA_DEVICE_561:
+ case MXL_HYDRA_DEVICE_561S:
+ state->base->can_clkout = 0;
+ state->base->demod_num = 6;
+ state->base->tuner_num = 1;
+ state->base->sku_type = MXL_HYDRA_SKU_TYPE_561;
+ break;
+ case MXL_HYDRA_DEVICE_568:
+ state->base->can_clkout = 0;
+ state->base->demod_num = 8;
+ state->base->tuner_num = 1;
+ state->base->chan_bond = 1;
+ state->base->sku_type = MXL_HYDRA_SKU_TYPE_568;
+ break;
+ case MXL_HYDRA_DEVICE_542:
+ state->base->can_clkout = 1;
+ state->base->demod_num = 4;
+ state->base->tuner_num = 3;
+ state->base->sku_type = MXL_HYDRA_SKU_TYPE_542;
+ state->base->ts_map = ts_map54x;
+ break;
+ case MXL_HYDRA_DEVICE_TEST:
+ case MXL_HYDRA_DEVICE_584:
+ default:
+ state->base->can_clkout = 0;
+ state->base->demod_num = 8;
+ state->base->tuner_num = 4;
+ state->base->sku_type = MXL_HYDRA_SKU_TYPE_584;
+ break;
+ }
+
+ status = validate_sku(state);
+ if (status)
+ return status;
+
+ update_by_mnemonic(state, 0x80030014, 9, 1, 1);
+ update_by_mnemonic(state, 0x8003003C, 12, 1, 1);
+ status = read_by_mnemonic(state, 0x80030000, 12, 4, &chipver);
+ if (status)
+ state->base->chipversion = 0;
+ else
+ state->base->chipversion = (chipver == 2) ? 2 : 1;
+ dev_info(state->i2cdev, "Hydra chip version %u\n",
+ state->base->chipversion);
+
+ cfg_dev_xtal(state, cfg->clk, cfg->cap, 0);
+
+ fw = firmware_is_alive(state);
+ if (!fw) {
+ status = load_fw(state, cfg);
+ if (status)
+ return status;
+ }
+ get_fwinfo(state);
+
+ config_mux(state);
+ mpeg_interface_cfg.enable = MXL_ENABLE;
+ mpeg_interface_cfg.lsb_or_msb_first = MXL_HYDRA_MPEG_SERIAL_MSB_1ST;
+ /* supports only (0-104&139)MHz */
+ if (cfg->ts_clk)
+ mpeg_interface_cfg.max_mpeg_clk_rate = cfg->ts_clk;
+ else
+ mpeg_interface_cfg.max_mpeg_clk_rate = 69; /* 139; */
+ mpeg_interface_cfg.mpeg_clk_phase = MXL_HYDRA_MPEG_CLK_PHASE_SHIFT_0_DEG;
+ mpeg_interface_cfg.mpeg_clk_pol = MXL_HYDRA_MPEG_CLK_IN_PHASE;
+ /* MXL_HYDRA_MPEG_CLK_GAPPED; */
+ mpeg_interface_cfg.mpeg_clk_type = MXL_HYDRA_MPEG_CLK_CONTINUOUS;
+ mpeg_interface_cfg.mpeg_error_indication =
+ MXL_HYDRA_MPEG_ERR_INDICATION_DISABLED;
+ mpeg_interface_cfg.mpeg_mode = MXL_HYDRA_MPEG_MODE_SERIAL_3_WIRE;
+ mpeg_interface_cfg.mpeg_sync_pol = MXL_HYDRA_MPEG_ACTIVE_HIGH;
+ mpeg_interface_cfg.mpeg_sync_pulse_width = MXL_HYDRA_MPEG_SYNC_WIDTH_BIT;
+ mpeg_interface_cfg.mpeg_valid_pol = MXL_HYDRA_MPEG_ACTIVE_HIGH;
+
+ for (j = 0; j < state->base->demod_num; j++) {
+ status = config_ts(state, (enum MXL_HYDRA_DEMOD_ID_E) j,
+ &mpeg_interface_cfg);
+ if (status)
+ return status;
+ }
+ set_drive_strength(state, 1);
+ return 0;
+}
+
+struct dvb_frontend *mxl5xx_attach(struct i2c_adapter *i2c,
+ struct mxl5xx_cfg *cfg, u32 demod, u32 tuner,
+ int (**fn_set_input)(struct dvb_frontend *, int))
+{
+ struct mxl *state;
+ struct mxl_base *base;
+
+ state = kzalloc(sizeof(struct mxl), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ state->demod = demod;
+ state->tuner = tuner;
+ state->tuner_in_use = 0xffffffff;
+ state->i2cdev = &i2c->dev;
+
+ base = match_base(i2c, cfg->adr);
+ if (base) {
+ base->count++;
+ if (base->count > base->demod_num)
+ goto fail;
+ state->base = base;
+ } else {
+ base = kzalloc(sizeof(struct mxl_base), GFP_KERNEL);
+ if (!base)
+ goto fail;
+ base->i2c = i2c;
+ base->adr = cfg->adr;
+ base->type = cfg->type;
+ base->count = 1;
+ mutex_init(&base->i2c_lock);
+ mutex_init(&base->status_lock);
+ mutex_init(&base->tune_lock);
+ INIT_LIST_HEAD(&base->mxls);
+
+ state->base = base;
+ if (probe(state, cfg) < 0) {
+ kfree(base);
+ goto fail;
+ }
+ list_add(&base->mxllist, &mxllist);
+ }
+ state->fe.ops = mxl_ops;
+ state->xbar[0] = 4;
+ state->xbar[1] = demod;
+ state->xbar[2] = 8;
+ state->fe.demodulator_priv = state;
+ *fn_set_input = set_input;
+
+ list_add(&state->mxl, &base->mxls);
+ return &state->fe;
+
+fail:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(mxl5xx_attach);
+
+MODULE_DESCRIPTION("MaxLinear MxL5xx DVB-S/S2 tuner-demodulator driver");
+MODULE_AUTHOR("Ralph and Marcus Metzler, Metzler Brothers Systementwicklung GbR");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Defines for the Maxlinear MX58x family of tuners/demods
+ *
+ * Copyright (C) 2014 Digital Devices GmbH
+ *
+ * based on code:
+ * Copyright (c) 2011-2013 MaxLinear, Inc. All rights reserved
+ * which was released under GPL V2
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ */
+
+enum MXL_BOOL_E {
+ MXL_DISABLE = 0,
+ MXL_ENABLE = 1,
+
+ MXL_FALSE = 0,
+ MXL_TRUE = 1,
+
+ MXL_INVALID = 0,
+ MXL_VALID = 1,
+
+ MXL_NO = 0,
+ MXL_YES = 1,
+
+ MXL_OFF = 0,
+ MXL_ON = 1
+};
+
+/* Firmware-Host Command IDs */
+enum MXL_HYDRA_HOST_CMD_ID_E {
+ /* --Device command IDs-- */
+ MXL_HYDRA_DEV_NO_OP_CMD = 0, /* No OP */
+
+ MXL_HYDRA_DEV_SET_POWER_MODE_CMD = 1,
+ MXL_HYDRA_DEV_SET_OVERWRITE_DEF_CMD = 2,
+
+ /* Host-used CMD, not used by firmware */
+ MXL_HYDRA_DEV_FIRMWARE_DOWNLOAD_CMD = 3,
+
+ /* Additional CONTROL types from DTV */
+ MXL_HYDRA_DEV_SET_BROADCAST_PID_STB_ID_CMD = 4,
+ MXL_HYDRA_DEV_GET_PMM_SLEEP_CMD = 5,
+
+ /* --Tuner command IDs-- */
+ MXL_HYDRA_TUNER_TUNE_CMD = 6,
+ MXL_HYDRA_TUNER_GET_STATUS_CMD = 7,
+
+ /* --Demod command IDs-- */
+ MXL_HYDRA_DEMOD_SET_PARAM_CMD = 8,
+ MXL_HYDRA_DEMOD_GET_STATUS_CMD = 9,
+
+ MXL_HYDRA_DEMOD_RESET_FEC_COUNTER_CMD = 10,
+
+ MXL_HYDRA_DEMOD_SET_PKT_NUM_CMD = 11,
+
+ MXL_HYDRA_DEMOD_SET_IQ_SOURCE_CMD = 12,
+ MXL_HYDRA_DEMOD_GET_IQ_DATA_CMD = 13,
+
+ MXL_HYDRA_DEMOD_GET_M68HC05_VER_CMD = 14,
+
+ MXL_HYDRA_DEMOD_SET_ERROR_COUNTER_MODE_CMD = 15,
+
+ /* --- ABORT channel tune */
+ MXL_HYDRA_ABORT_TUNE_CMD = 16, /* Abort current tune command. */
+
+ /* --SWM/FSK command IDs-- */
+ MXL_HYDRA_FSK_RESET_CMD = 17,
+ MXL_HYDRA_FSK_MSG_CMD = 18,
+ MXL_HYDRA_FSK_SET_OP_MODE_CMD = 19,
+
+ /* --DiSeqC command IDs-- */
+ MXL_HYDRA_DISEQC_MSG_CMD = 20,
+ MXL_HYDRA_DISEQC_COPY_MSG_TO_MAILBOX = 21,
+ MXL_HYDRA_DISEQC_CFG_MSG_CMD = 22,
+
+ /* --- FFT Debug Command IDs-- */
+ MXL_HYDRA_REQ_FFT_SPECTRUM_CMD = 23,
+
+ /* -- Demod scramblle code */
+ MXL_HYDRA_DEMOD_SCRAMBLE_CODE_CMD = 24,
+
+ /* ---For host to know how many commands in total */
+ MXL_HYDRA_LAST_HOST_CMD = 25,
+
+ MXL_HYDRA_DEMOD_INTR_TYPE_CMD = 47,
+ MXL_HYDRA_DEV_INTR_CLEAR_CMD = 48,
+ MXL_HYDRA_TUNER_SPECTRUM_REQ_CMD = 53,
+ MXL_HYDRA_TUNER_ACTIVATE_CMD = 55,
+ MXL_HYDRA_DEV_CFG_POWER_MODE_CMD = 56,
+ MXL_HYDRA_DEV_XTAL_CAP_CMD = 57,
+ MXL_HYDRA_DEV_CFG_SKU_CMD = 58,
+ MXL_HYDRA_TUNER_SPECTRUM_MIN_GAIN_CMD = 59,
+ MXL_HYDRA_DISEQC_CONT_TONE_CFG = 60,
+ MXL_HYDRA_DEV_RF_WAKE_UP_CMD = 61,
+ MXL_HYDRA_DEMOD_CFG_EQ_CTRL_PARAM_CMD = 62,
+ MXL_HYDRA_DEMOD_FREQ_OFFSET_SEARCH_RANGE_CMD = 63,
+ MXL_HYDRA_DEV_REQ_PWR_FROM_ADCRSSI_CMD = 64,
+
+ MXL_XCPU_PID_FLT_CFG_CMD = 65,
+ MXL_XCPU_SHMEM_TEST_CMD = 66,
+ MXL_XCPU_ABORT_TUNE_CMD = 67,
+ MXL_XCPU_CHAN_TUNE_CMD = 68,
+ MXL_XCPU_FLT_BOND_HDRS_CMD = 69,
+
+ MXL_HYDRA_DEV_BROADCAST_WAKE_UP_CMD = 70,
+ MXL_HYDRA_FSK_CFG_FSK_FREQ_CMD = 71,
+ MXL_HYDRA_FSK_POWER_DOWN_CMD = 72,
+ MXL_XCPU_CLEAR_CB_STATS_CMD = 73,
+ MXL_XCPU_CHAN_BOND_RESTART_CMD = 74
+};
+
+#define MXL_ENABLE_BIG_ENDIAN (0)
+
+#define MXL_HYDRA_OEM_MAX_BLOCK_WRITE_LENGTH 248
+
+#define MXL_HYDRA_OEM_MAX_CMD_BUFF_LEN (248)
+
+#define MXL_HYDRA_CAP_MIN 10
+#define MXL_HYDRA_CAP_MAX 33
+
+#define MXL_HYDRA_PLID_REG_READ 0xFB /* Read register PLID */
+#define MXL_HYDRA_PLID_REG_WRITE 0xFC /* Write register PLID */
+
+#define MXL_HYDRA_PLID_CMD_READ 0xFD /* Command Read PLID */
+#define MXL_HYDRA_PLID_CMD_WRITE 0xFE /* Command Write PLID */
+
+#define MXL_HYDRA_REG_SIZE_IN_BYTES 4 /* Hydra register size in bytes */
+#define MXL_HYDRA_I2C_HDR_SIZE (2 * sizeof(u8)) /* PLID + LEN(0xFF) */
+#define MXL_HYDRA_CMD_HEADER_SIZE (MXL_HYDRA_REG_SIZE_IN_BYTES + MXL_HYDRA_I2C_HDR_SIZE)
+
+#define MXL_HYDRA_SKU_ID_581 0
+#define MXL_HYDRA_SKU_ID_584 1
+#define MXL_HYDRA_SKU_ID_585 2
+#define MXL_HYDRA_SKU_ID_544 3
+#define MXL_HYDRA_SKU_ID_561 4
+#define MXL_HYDRA_SKU_ID_582 5
+#define MXL_HYDRA_SKU_ID_568 6
+
+/* macro for register write data buffer size
+ * (PLID + LEN (0xFF) + RegAddr + RegData)
+ */
+#define MXL_HYDRA_REG_WRITE_LEN (MXL_HYDRA_I2C_HDR_SIZE + (2 * MXL_HYDRA_REG_SIZE_IN_BYTES))
+
+/* macro to extract a single byte from 4-byte(32-bit) data */
+#define GET_BYTE(x, n) (((x) >> (8*(n))) & 0xFF)
+
+#define MAX_CMD_DATA 512
+
+#define MXL_GET_REG_MASK_32(lsb_loc, num_of_bits) ((0xFFFFFFFF >> (32 - (num_of_bits))) << (lsb_loc))
+
+#define FW_DL_SIGN (0xDEADBEEF)
+
+#define MBIN_FORMAT_VERSION '1'
+#define MBIN_FILE_HEADER_ID 'M'
+#define MBIN_SEGMENT_HEADER_ID 'S'
+#define MBIN_MAX_FILE_LENGTH (1<<23)
+
+struct MBIN_FILE_HEADER_T {
+ u8 id;
+ u8 fmt_version;
+ u8 header_len;
+ u8 num_segments;
+ u8 entry_address[4];
+ u8 image_size24[3];
+ u8 image_checksum;
+ u8 reserved[4];
+};
+
+struct MBIN_FILE_T {
+ struct MBIN_FILE_HEADER_T header;
+ u8 data[1];
+};
+
+struct MBIN_SEGMENT_HEADER_T {
+ u8 id;
+ u8 len24[3];
+ u8 address[4];
+};
+
+struct MBIN_SEGMENT_T {
+ struct MBIN_SEGMENT_HEADER_T header;
+ u8 data[1];
+};
+
+enum MXL_CMD_TYPE_E { MXL_CMD_WRITE = 0, MXL_CMD_READ };
+
+#define BUILD_HYDRA_CMD(cmd_id, req_type, size, data_ptr, cmd_buff) \
+ do { \
+ cmd_buff[0] = ((req_type == MXL_CMD_WRITE) ? MXL_HYDRA_PLID_CMD_WRITE : MXL_HYDRA_PLID_CMD_READ); \
+ cmd_buff[1] = (size > 251) ? 0xff : (u8) (size + 4); \
+ cmd_buff[2] = size; \
+ cmd_buff[3] = cmd_id; \
+ cmd_buff[4] = 0x00; \
+ cmd_buff[5] = 0x00; \
+ convert_endian(MXL_ENABLE_BIG_ENDIAN, size, (u8 *)data_ptr); \
+ memcpy((void *)&cmd_buff[6], data_ptr, size); \
+ } while (0)
+
+struct MXL_REG_FIELD_T {
+ u32 reg_addr;
+ u8 lsb_pos;
+ u8 num_of_bits;
+};
+
+struct MXL_DEV_CMD_DATA_T {
+ u32 data_size;
+ u8 data[MAX_CMD_DATA];
+};
+
+enum MXL_HYDRA_SKU_TYPE_E {
+ MXL_HYDRA_SKU_TYPE_MIN = 0x00,
+ MXL_HYDRA_SKU_TYPE_581 = 0x00,
+ MXL_HYDRA_SKU_TYPE_584 = 0x01,
+ MXL_HYDRA_SKU_TYPE_585 = 0x02,
+ MXL_HYDRA_SKU_TYPE_544 = 0x03,
+ MXL_HYDRA_SKU_TYPE_561 = 0x04,
+ MXL_HYDRA_SKU_TYPE_5XX = 0x05,
+ MXL_HYDRA_SKU_TYPE_5YY = 0x06,
+ MXL_HYDRA_SKU_TYPE_511 = 0x07,
+ MXL_HYDRA_SKU_TYPE_561_DE = 0x08,
+ MXL_HYDRA_SKU_TYPE_582 = 0x09,
+ MXL_HYDRA_SKU_TYPE_541 = 0x0A,
+ MXL_HYDRA_SKU_TYPE_568 = 0x0B,
+ MXL_HYDRA_SKU_TYPE_542 = 0x0C,
+ MXL_HYDRA_SKU_TYPE_MAX = 0x0D,
+};
+
+struct MXL_HYDRA_SKU_COMMAND_T {
+ enum MXL_HYDRA_SKU_TYPE_E sku_type;
+};
+
+enum MXL_HYDRA_DEMOD_ID_E {
+ MXL_HYDRA_DEMOD_ID_0 = 0,
+ MXL_HYDRA_DEMOD_ID_1,
+ MXL_HYDRA_DEMOD_ID_2,
+ MXL_HYDRA_DEMOD_ID_3,
+ MXL_HYDRA_DEMOD_ID_4,
+ MXL_HYDRA_DEMOD_ID_5,
+ MXL_HYDRA_DEMOD_ID_6,
+ MXL_HYDRA_DEMOD_ID_7,
+ MXL_HYDRA_DEMOD_MAX
+};
+
+#define MXL_DEMOD_SCRAMBLE_SEQ_LEN 12
+
+#define MAX_STEP_SIZE_24_XTAL_102_05_KHZ 195
+#define MAX_STEP_SIZE_24_XTAL_204_10_KHZ 215
+#define MAX_STEP_SIZE_24_XTAL_306_15_KHZ 203
+#define MAX_STEP_SIZE_24_XTAL_408_20_KHZ 177
+
+#define MAX_STEP_SIZE_27_XTAL_102_05_KHZ 195
+#define MAX_STEP_SIZE_27_XTAL_204_10_KHZ 215
+#define MAX_STEP_SIZE_27_XTAL_306_15_KHZ 203
+#define MAX_STEP_SIZE_27_XTAL_408_20_KHZ 177
+
+#define MXL_HYDRA_SPECTRUM_MIN_FREQ_KHZ 300000
+#define MXL_HYDRA_SPECTRUM_MAX_FREQ_KHZ 2350000
+
+enum MXL_DEMOD_CHAN_PARAMS_OFFSET_E {
+ DMD_STANDARD_ADDR = 0,
+ DMD_SPECTRUM_INVERSION_ADDR,
+ DMD_SPECTRUM_ROLL_OFF_ADDR,
+ DMD_SYMBOL_RATE_ADDR,
+ DMD_MODULATION_SCHEME_ADDR,
+ DMD_FEC_CODE_RATE_ADDR,
+ DMD_SNR_ADDR,
+ DMD_FREQ_OFFSET_ADDR,
+ DMD_CTL_FREQ_OFFSET_ADDR,
+ DMD_STR_FREQ_OFFSET_ADDR,
+ DMD_FTL_FREQ_OFFSET_ADDR,
+ DMD_STR_NBC_SYNC_LOCK_ADDR,
+ DMD_CYCLE_SLIP_COUNT_ADDR,
+ DMD_DISPLAY_IQ_ADDR,
+ DMD_DVBS2_CRC_ERRORS_ADDR,
+ DMD_DVBS2_PER_COUNT_ADDR,
+ DMD_DVBS2_PER_WINDOW_ADDR,
+ DMD_DVBS_CORR_RS_ERRORS_ADDR,
+ DMD_DVBS_UNCORR_RS_ERRORS_ADDR,
+ DMD_DVBS_BER_COUNT_ADDR,
+ DMD_DVBS_BER_WINDOW_ADDR,
+ DMD_TUNER_ID_ADDR,
+ DMD_DVBS2_PILOT_ON_OFF_ADDR,
+ DMD_FREQ_SEARCH_RANGE_IN_KHZ_ADDR,
+
+ MXL_DEMOD_CHAN_PARAMS_BUFF_SIZE,
+};
+
+enum MXL_HYDRA_TUNER_ID_E {
+ MXL_HYDRA_TUNER_ID_0 = 0,
+ MXL_HYDRA_TUNER_ID_1,
+ MXL_HYDRA_TUNER_ID_2,
+ MXL_HYDRA_TUNER_ID_3,
+ MXL_HYDRA_TUNER_MAX
+};
+
+enum MXL_HYDRA_BCAST_STD_E {
+ MXL_HYDRA_DSS = 0,
+ MXL_HYDRA_DVBS,
+ MXL_HYDRA_DVBS2,
+};
+
+enum MXL_HYDRA_FEC_E {
+ MXL_HYDRA_FEC_AUTO = 0,
+ MXL_HYDRA_FEC_1_2,
+ MXL_HYDRA_FEC_3_5,
+ MXL_HYDRA_FEC_2_3,
+ MXL_HYDRA_FEC_3_4,
+ MXL_HYDRA_FEC_4_5,
+ MXL_HYDRA_FEC_5_6,
+ MXL_HYDRA_FEC_6_7,
+ MXL_HYDRA_FEC_7_8,
+ MXL_HYDRA_FEC_8_9,
+ MXL_HYDRA_FEC_9_10,
+};
+
+enum MXL_HYDRA_MODULATION_E {
+ MXL_HYDRA_MOD_AUTO = 0,
+ MXL_HYDRA_MOD_QPSK,
+ MXL_HYDRA_MOD_8PSK
+};
+
+enum MXL_HYDRA_SPECTRUM_E {
+ MXL_HYDRA_SPECTRUM_AUTO = 0,
+ MXL_HYDRA_SPECTRUM_INVERTED,
+ MXL_HYDRA_SPECTRUM_NON_INVERTED,
+};
+
+enum MXL_HYDRA_ROLLOFF_E {
+ MXL_HYDRA_ROLLOFF_AUTO = 0,
+ MXL_HYDRA_ROLLOFF_0_20,
+ MXL_HYDRA_ROLLOFF_0_25,
+ MXL_HYDRA_ROLLOFF_0_35
+};
+
+enum MXL_HYDRA_PILOTS_E {
+ MXL_HYDRA_PILOTS_OFF = 0,
+ MXL_HYDRA_PILOTS_ON,
+ MXL_HYDRA_PILOTS_AUTO
+};
+
+enum MXL_HYDRA_CONSTELLATION_SRC_E {
+ MXL_HYDRA_FORMATTER = 0,
+ MXL_HYDRA_LEGACY_FEC,
+ MXL_HYDRA_FREQ_RECOVERY,
+ MXL_HYDRA_NBC,
+ MXL_HYDRA_CTL,
+ MXL_HYDRA_EQ,
+};
+
+struct MXL_HYDRA_DEMOD_LOCK_T {
+ int agc_lock; /* AGC lock info */
+ int fec_lock; /* Demod FEC block lock info */
+};
+
+struct MXL_HYDRA_DEMOD_STATUS_DVBS_T {
+ u32 rs_errors; /* RS decoder err counter */
+ u32 ber_window; /* Ber Windows */
+ u32 ber_count; /* BER count */
+ u32 ber_window_iter1; /* Ber Windows - post viterbi */
+ u32 ber_count_iter1; /* BER count - post viterbi */
+};
+
+struct MXL_HYDRA_DEMOD_STATUS_DSS_T {
+ u32 rs_errors; /* RS decoder err counter */
+ u32 ber_window; /* Ber Windows */
+ u32 ber_count; /* BER count */
+};
+
+struct MXL_HYDRA_DEMOD_STATUS_DVBS2_T {
+ u32 crc_errors; /* CRC error counter */
+ u32 packet_error_count; /* Number of packet errors */
+ u32 total_packets; /* Total packets */
+};
+
+struct MXL_HYDRA_DEMOD_STATUS_T {
+ enum MXL_HYDRA_BCAST_STD_E standard_mask; /* Standard DVB-S, DVB-S2 or DSS */
+
+ union {
+ struct MXL_HYDRA_DEMOD_STATUS_DVBS_T demod_status_dvbs; /* DVB-S demod status */
+ struct MXL_HYDRA_DEMOD_STATUS_DVBS2_T demod_status_dvbs2; /* DVB-S2 demod status */
+ struct MXL_HYDRA_DEMOD_STATUS_DSS_T demod_status_dss; /* DSS demod status */
+ } u;
+};
+
+struct MXL_HYDRA_DEMOD_SIG_OFFSET_INFO_T {
+ s32 carrier_offset_in_hz; /* CRL offset info */
+ s32 symbol_offset_in_symbol; /* SRL offset info */
+};
+
+struct MXL_HYDRA_DEMOD_SCRAMBLE_INFO_T {
+ u8 scramble_sequence[MXL_DEMOD_SCRAMBLE_SEQ_LEN]; /* scramble sequence */
+ u32 scramble_code; /* scramble gold code */
+};
+
+enum MXL_HYDRA_SPECTRUM_STEP_SIZE_E {
+ MXL_HYDRA_STEP_SIZE_24_XTAL_102_05KHZ, /* 102.05 KHz for 24 MHz XTAL */
+ MXL_HYDRA_STEP_SIZE_24_XTAL_204_10KHZ, /* 204.10 KHz for 24 MHz XTAL */
+ MXL_HYDRA_STEP_SIZE_24_XTAL_306_15KHZ, /* 306.15 KHz for 24 MHz XTAL */
+ MXL_HYDRA_STEP_SIZE_24_XTAL_408_20KHZ, /* 408.20 KHz for 24 MHz XTAL */
+
+ MXL_HYDRA_STEP_SIZE_27_XTAL_102_05KHZ, /* 102.05 KHz for 27 MHz XTAL */
+ MXL_HYDRA_STEP_SIZE_27_XTAL_204_35KHZ, /* 204.35 KHz for 27 MHz XTAL */
+ MXL_HYDRA_STEP_SIZE_27_XTAL_306_52KHZ, /* 306.52 KHz for 27 MHz XTAL */
+ MXL_HYDRA_STEP_SIZE_27_XTAL_408_69KHZ, /* 408.69 KHz for 27 MHz XTAL */
+};
+
+enum MXL_HYDRA_SPECTRUM_RESOLUTION_E {
+ MXL_HYDRA_SPECTRUM_RESOLUTION_00_1_DB, /* 0.1 dB */
+ MXL_HYDRA_SPECTRUM_RESOLUTION_01_0_DB, /* 1.0 dB */
+ MXL_HYDRA_SPECTRUM_RESOLUTION_05_0_DB, /* 5.0 dB */
+ MXL_HYDRA_SPECTRUM_RESOLUTION_10_0_DB, /* 10 dB */
+};
+
+enum MXL_HYDRA_SPECTRUM_ERROR_CODE_E {
+ MXL_SPECTRUM_NO_ERROR,
+ MXL_SPECTRUM_INVALID_PARAMETER,
+ MXL_SPECTRUM_INVALID_STEP_SIZE,
+ MXL_SPECTRUM_BW_CANNOT_BE_COVERED,
+ MXL_SPECTRUM_DEMOD_BUSY,
+ MXL_SPECTRUM_TUNER_NOT_ENABLED,
+};
+
+struct MXL_HYDRA_SPECTRUM_REQ_T {
+ u32 tuner_index; /* TUNER Ctrl: one of MXL58x_TUNER_ID_E */
+ u32 demod_index; /* DEMOD Ctrl: one of MXL58x_DEMOD_ID_E */
+ enum MXL_HYDRA_SPECTRUM_STEP_SIZE_E step_size_in_khz;
+ u32 starting_freq_ink_hz;
+ u32 total_steps;
+ enum MXL_HYDRA_SPECTRUM_RESOLUTION_E spectrum_division;
+};
+
+enum MXL_HYDRA_SEARCH_FREQ_OFFSET_TYPE_E {
+ MXL_HYDRA_SEARCH_MAX_OFFSET = 0, /* DMD searches for max freq offset (i.e. 5MHz) */
+ MXL_HYDRA_SEARCH_BW_PLUS_ROLLOFF, /* DMD searches for BW + ROLLOFF/2 */
+};
+
+struct MXL58X_CFG_FREQ_OFF_SEARCH_RANGE_T {
+ u32 demod_index;
+ enum MXL_HYDRA_SEARCH_FREQ_OFFSET_TYPE_E search_type;
+};
+
+/* there are two slices
+ * slice0 - TS0, TS1, TS2 & TS3
+ * slice1 - TS4, TS5, TS6 & TS7
+ */
+#define MXL_HYDRA_TS_SLICE_MAX 2
+
+#define MAX_FIXED_PID_NUM 32
+
+#define MXL_HYDRA_NCO_CLK 418 /* 418 MHz */
+
+#define MXL_HYDRA_MAX_TS_CLOCK 139 /* 139 MHz */
+
+#define MXL_HYDRA_TS_FIXED_PID_FILT_SIZE 32
+
+#define MXL_HYDRA_SHARED_PID_FILT_SIZE_DEFAULT 33 /* Shared PID filter size in 1-1 mux mode */
+#define MXL_HYDRA_SHARED_PID_FILT_SIZE_2_TO_1 66 /* Shared PID filter size in 2-1 mux mode */
+#define MXL_HYDRA_SHARED_PID_FILT_SIZE_4_TO_1 132 /* Shared PID filter size in 4-1 mux mode */
+
+enum MXL_HYDRA_PID_BANK_TYPE_E {
+ MXL_HYDRA_SOFTWARE_PID_BANK = 0,
+ MXL_HYDRA_HARDWARE_PID_BANK,
+};
+
+enum MXL_HYDRA_TS_MUX_MODE_E {
+ MXL_HYDRA_TS_MUX_PID_REMAP = 0,
+ MXL_HYDRA_TS_MUX_PREFIX_EXTRA_HEADER = 1,
+};
+
+enum MXL_HYDRA_TS_MUX_TYPE_E {
+ MXL_HYDRA_TS_MUX_DISABLE = 0, /* No Mux ( 1 TSIF to 1 TSIF) */
+ MXL_HYDRA_TS_MUX_2_TO_1, /* Mux 2 TSIF to 1 TSIF */
+ MXL_HYDRA_TS_MUX_4_TO_1, /* Mux 4 TSIF to 1 TSIF */
+};
+
+enum MXL_HYDRA_TS_GROUP_E {
+ MXL_HYDRA_TS_GROUP_0_3 = 0, /* TS group 0 to 3 (TS0, TS1, TS2 & TS3) */
+ MXL_HYDRA_TS_GROUP_4_7, /* TS group 0 to 3 (TS4, TS5, TS6 & TS7) */
+};
+
+enum MXL_HYDRA_TS_PID_FLT_CTRL_E {
+ MXL_HYDRA_TS_PIDS_ALLOW_ALL = 0, /* Allow all pids */
+ MXL_HYDRA_TS_PIDS_DROP_ALL, /* Drop all pids */
+ MXL_HYDRA_TS_INVALIDATE_PID_FILTER, /* Delete current PD filter in the device */
+};
+
+enum MXL_HYDRA_TS_PID_TYPE_E {
+ MXL_HYDRA_TS_PID_FIXED = 0,
+ MXL_HYDRA_TS_PID_REGULAR,
+};
+
+struct MXL_HYDRA_TS_PID_T {
+ u16 original_pid; /* pid from TS */
+ u16 remapped_pid; /* remapped pid */
+ enum MXL_BOOL_E enable; /* enable or disable pid */
+ enum MXL_BOOL_E allow_or_drop; /* allow or drop pid */
+ enum MXL_BOOL_E enable_pid_remap; /* enable or disable pid remap */
+ u8 bond_id; /* Bond ID in A0 always 0 - Only for 568 Sku */
+ u8 dest_id; /* Output port ID for the PID - Only for 568 Sku */
+};
+
+struct MXL_HYDRA_TS_MUX_PREFIX_HEADER_T {
+ enum MXL_BOOL_E enable;
+ u8 num_byte;
+ u8 header[12];
+};
+
+enum MXL_HYDRA_PID_FILTER_BANK_E {
+ MXL_HYDRA_PID_BANK_A = 0,
+ MXL_HYDRA_PID_BANK_B,
+};
+
+enum MXL_HYDRA_MPEG_DATA_FMT_E {
+ MXL_HYDRA_MPEG_SERIAL_MSB_1ST = 0,
+ MXL_HYDRA_MPEG_SERIAL_LSB_1ST,
+
+ MXL_HYDRA_MPEG_SYNC_WIDTH_BIT = 0,
+ MXL_HYDRA_MPEG_SYNC_WIDTH_BYTE
+};
+
+enum MXL_HYDRA_MPEG_MODE_E {
+ MXL_HYDRA_MPEG_MODE_SERIAL_4_WIRE = 0, /* MPEG 4 Wire serial mode */
+ MXL_HYDRA_MPEG_MODE_SERIAL_3_WIRE, /* MPEG 3 Wire serial mode */
+ MXL_HYDRA_MPEG_MODE_SERIAL_2_WIRE, /* MPEG 2 Wire serial mode */
+ MXL_HYDRA_MPEG_MODE_PARALLEL /* MPEG parallel mode - valid only for MxL581 */
+};
+
+enum MXL_HYDRA_MPEG_CLK_TYPE_E {
+ MXL_HYDRA_MPEG_CLK_CONTINUOUS = 0, /* Continuous MPEG clock */
+ MXL_HYDRA_MPEG_CLK_GAPPED, /* Gapped (gated) MPEG clock */
+};
+
+enum MXL_HYDRA_MPEG_CLK_FMT_E {
+ MXL_HYDRA_MPEG_ACTIVE_LOW = 0,
+ MXL_HYDRA_MPEG_ACTIVE_HIGH,
+
+ MXL_HYDRA_MPEG_CLK_NEGATIVE = 0,
+ MXL_HYDRA_MPEG_CLK_POSITIVE,
+
+ MXL_HYDRA_MPEG_CLK_IN_PHASE = 0,
+ MXL_HYDRA_MPEG_CLK_INVERTED,
+};
+
+enum MXL_HYDRA_MPEG_CLK_PHASE_E {
+ MXL_HYDRA_MPEG_CLK_PHASE_SHIFT_0_DEG = 0,
+ MXL_HYDRA_MPEG_CLK_PHASE_SHIFT_90_DEG,
+ MXL_HYDRA_MPEG_CLK_PHASE_SHIFT_180_DEG,
+ MXL_HYDRA_MPEG_CLK_PHASE_SHIFT_270_DEG
+};
+
+enum MXL_HYDRA_MPEG_ERR_INDICATION_E {
+ MXL_HYDRA_MPEG_ERR_REPLACE_SYNC = 0,
+ MXL_HYDRA_MPEG_ERR_REPLACE_VALID,
+ MXL_HYDRA_MPEG_ERR_INDICATION_DISABLED
+};
+
+struct MXL_HYDRA_MPEGOUT_PARAM_T {
+ int enable; /* Enable or Disable MPEG OUT */
+ enum MXL_HYDRA_MPEG_CLK_TYPE_E mpeg_clk_type; /* Continuous or gapped */
+ enum MXL_HYDRA_MPEG_CLK_FMT_E mpeg_clk_pol; /* MPEG Clk polarity */
+ u8 max_mpeg_clk_rate; /* Max MPEG Clk rate (0 - 104 MHz, 139 MHz) */
+ enum MXL_HYDRA_MPEG_CLK_PHASE_E mpeg_clk_phase; /* MPEG Clk phase */
+ enum MXL_HYDRA_MPEG_DATA_FMT_E lsb_or_msb_first; /* LSB first or MSB first in TS transmission */
+ enum MXL_HYDRA_MPEG_DATA_FMT_E mpeg_sync_pulse_width; /* MPEG SYNC pulse width (1-bit or 1-byte) */
+ enum MXL_HYDRA_MPEG_CLK_FMT_E mpeg_valid_pol; /* MPEG VALID polarity */
+ enum MXL_HYDRA_MPEG_CLK_FMT_E mpeg_sync_pol; /* MPEG SYNC polarity */
+ enum MXL_HYDRA_MPEG_MODE_E mpeg_mode; /* config 4/3/2-wire serial or parallel TS out */
+ enum MXL_HYDRA_MPEG_ERR_INDICATION_E mpeg_error_indication; /* Enable or Disable MPEG error indication */
+};
+
+enum MXL_HYDRA_EXT_TS_IN_ID_E {
+ MXL_HYDRA_EXT_TS_IN_0 = 0,
+ MXL_HYDRA_EXT_TS_IN_1,
+ MXL_HYDRA_EXT_TS_IN_2,
+ MXL_HYDRA_EXT_TS_IN_3,
+ MXL_HYDRA_EXT_TS_IN_MAX
+};
+
+enum MXL_HYDRA_TS_OUT_ID_E {
+ MXL_HYDRA_TS_OUT_0 = 0,
+ MXL_HYDRA_TS_OUT_1,
+ MXL_HYDRA_TS_OUT_2,
+ MXL_HYDRA_TS_OUT_3,
+ MXL_HYDRA_TS_OUT_4,
+ MXL_HYDRA_TS_OUT_5,
+ MXL_HYDRA_TS_OUT_6,
+ MXL_HYDRA_TS_OUT_7,
+ MXL_HYDRA_TS_OUT_MAX
+};
+
+enum MXL_HYDRA_TS_DRIVE_STRENGTH_E {
+ MXL_HYDRA_TS_DRIVE_STRENGTH_1X = 0,
+ MXL_HYDRA_TS_DRIVE_STRENGTH_2X,
+ MXL_HYDRA_TS_DRIVE_STRENGTH_3X,
+ MXL_HYDRA_TS_DRIVE_STRENGTH_4X,
+ MXL_HYDRA_TS_DRIVE_STRENGTH_5X,
+ MXL_HYDRA_TS_DRIVE_STRENGTH_6X,
+ MXL_HYDRA_TS_DRIVE_STRENGTH_7X,
+ MXL_HYDRA_TS_DRIVE_STRENGTH_8X
+};
+
+enum MXL_HYDRA_DEVICE_E {
+ MXL_HYDRA_DEVICE_581 = 0,
+ MXL_HYDRA_DEVICE_584,
+ MXL_HYDRA_DEVICE_585,
+ MXL_HYDRA_DEVICE_544,
+ MXL_HYDRA_DEVICE_561,
+ MXL_HYDRA_DEVICE_TEST,
+ MXL_HYDRA_DEVICE_582,
+ MXL_HYDRA_DEVICE_541,
+ MXL_HYDRA_DEVICE_568,
+ MXL_HYDRA_DEVICE_542,
+ MXL_HYDRA_DEVICE_541S,
+ MXL_HYDRA_DEVICE_561S,
+ MXL_HYDRA_DEVICE_581S,
+ MXL_HYDRA_DEVICE_MAX
+};
+
+/* Demod IQ data */
+struct MXL_HYDRA_DEMOD_IQ_SRC_T {
+ u32 demod_id;
+ u32 source_of_iq; /* == 0, it means I/Q comes from Formatter
+ * == 1, Legacy FEC
+ * == 2, Frequency Recovery
+ * == 3, NBC
+ * == 4, CTL
+ * == 5, EQ
+ * == 6, FPGA
+ */
+};
+
+struct MXL_HYDRA_DEMOD_ABORT_TUNE_T {
+ u32 demod_id;
+};
+
+struct MXL_HYDRA_TUNER_CMD {
+ u8 tuner_id;
+ u8 enable;
+};
+
+/* Demod Para for Channel Tune */
+struct MXL_HYDRA_DEMOD_PARAM_T {
+ u32 tuner_index;
+ u32 demod_index;
+ u32 frequency_in_hz; /* Frequency */
+ u32 standard; /* one of MXL_HYDRA_BCAST_STD_E */
+ u32 spectrum_inversion; /* Input : Spectrum inversion. */
+ u32 roll_off; /* rollOff (alpha) factor */
+ u32 symbol_rate_in_hz; /* Symbol rate */
+ u32 pilots; /* TRUE = pilots enabled */
+ u32 modulation_scheme; /* Input : Modulation Scheme is one of MXL_HYDRA_MODULATION_E */
+ u32 fec_code_rate; /* Input : Forward error correction rate. Is one of MXL_HYDRA_FEC_E */
+ u32 max_carrier_offset_in_mhz; /* Maximum carrier freq offset in MHz. Same as freqSearchRangeKHz, but in unit of MHz. */
+};
+
+struct MXL_HYDRA_DEMOD_SCRAMBLE_CODE_T {
+ u32 demod_index;
+ u8 scramble_sequence[12]; /* scramble sequence */
+ u32 scramble_code; /* scramble gold code */
+};
+
+struct MXL_INTR_CFG_T {
+ u32 intr_type;
+ u32 intr_duration_in_nano_secs;
+ u32 intr_mask;
+};
+
+struct MXL_HYDRA_POWER_MODE_CMD {
+ u8 power_mode; /* enumeration values are defined in MXL_HYDRA_PWR_MODE_E (device API.h) */
+};
+
+struct MXL_HYDRA_RF_WAKEUP_PARAM_T {
+ u32 time_interval_in_seconds; /* in seconds */
+ u32 tuner_index;
+ s32 rssi_threshold;
+};
+
+struct MXL_HYDRA_RF_WAKEUP_CFG_T {
+ u32 tuner_count;
+ struct MXL_HYDRA_RF_WAKEUP_PARAM_T params;
+};
+
+enum MXL_HYDRA_AUX_CTRL_MODE_E {
+ MXL_HYDRA_AUX_CTRL_MODE_FSK = 0, /* Select FSK controller */
+ MXL_HYDRA_AUX_CTRL_MODE_DISEQC, /* Select DiSEqC controller */
+};
+
+enum MXL_HYDRA_DISEQC_OPMODE_E {
+ MXL_HYDRA_DISEQC_ENVELOPE_MODE = 0,
+ MXL_HYDRA_DISEQC_TONE_MODE,
+};
+
+enum MXL_HYDRA_DISEQC_VER_E {
+ MXL_HYDRA_DISEQC_1_X = 0, /* Config DiSEqC 1.x mode */
+ MXL_HYDRA_DISEQC_2_X, /* Config DiSEqC 2.x mode */
+ MXL_HYDRA_DISEQC_DISABLE /* Disable DiSEqC */
+};
+
+enum MXL_HYDRA_DISEQC_CARRIER_FREQ_E {
+ MXL_HYDRA_DISEQC_CARRIER_FREQ_22KHZ = 0, /* DiSEqC signal frequency of 22 KHz */
+ MXL_HYDRA_DISEQC_CARRIER_FREQ_33KHZ, /* DiSEqC signal frequency of 33 KHz */
+ MXL_HYDRA_DISEQC_CARRIER_FREQ_44KHZ /* DiSEqC signal frequency of 44 KHz */
+};
+
+enum MXL_HYDRA_DISEQC_ID_E {
+ MXL_HYDRA_DISEQC_ID_0 = 0,
+ MXL_HYDRA_DISEQC_ID_1,
+ MXL_HYDRA_DISEQC_ID_2,
+ MXL_HYDRA_DISEQC_ID_3
+};
+
+enum MXL_HYDRA_FSK_OP_MODE_E {
+ MXL_HYDRA_FSK_CFG_TYPE_39KPBS = 0, /* 39.0kbps */
+ MXL_HYDRA_FSK_CFG_TYPE_39_017KPBS, /* 39.017kbps */
+ MXL_HYDRA_FSK_CFG_TYPE_115_2KPBS /* 115.2kbps */
+};
+
+struct MXL58X_DSQ_OP_MODE_T {
+ u32 diseqc_id; /* DSQ 0, 1, 2 or 3 */
+ u32 op_mode; /* Envelope mode (0) or internal tone mode (1) */
+ u32 version; /* 0: 1.0, 1: 1.1, 2: Disable */
+ u32 center_freq; /* 0: 22KHz, 1: 33KHz and 2: 44 KHz */
+};
+
+struct MXL_HYDRA_DISEQC_CFG_CONT_TONE_T {
+ u32 diseqc_id;
+ u32 cont_tone_flag; /* 1: Enable , 0: Disable */
+};