/* * si4713_powerdown - Powers the device down * @sdev: si4713_device structure for the device we are communicating */ static int si4713_powerdown(struct si4713_device *sdev) { int err; u8 resp[SI4713_PWDN_NRESP]; if (!sdev->power_state) return 0; err = si4713_send_command(sdev, SI4713_CMD_POWER_DOWN, NULL, 0, resp, ARRAY_SIZE(resp), DEFAULT_TIMEOUT); if (!err) { v4l2_dbg(1, debug, &sdev->sd, "Power down response: 0x%02x\n", resp[0]); v4l2_dbg(1, debug, &sdev->sd, "Device in reset mode\n"); if (gpio_is_valid(sdev->gpio_reset)) gpio_set_value(sdev->gpio_reset, 0); err = regulator_bulk_disable(ARRAY_SIZE(sdev->supplies), sdev->supplies); if (err) v4l2_err(&sdev->sd, "Failed to disable supplies: %d\n", err); sdev->power_state = POWER_OFF; } return err; }
/* * si4713_tx_tune_power - Sets the RF voltage level between 88 and 115 dBuV in * 1 dB units. A value of 0x00 indicates off. The command * also sets the antenna tuning capacitance. A value of 0 * indicates autotuning, and a value of 1 - 191 indicates * a manual override, which results in a tuning * capacitance of 0.25 pF x @antcap. * @sdev: si4713_device structure for the device we are communicating * @power: tuning power (88 - 115 dBuV, unit/step 1 dB) * @antcap: value of antenna tuning capacitor (0 - 191) */ static int si4713_tx_tune_power(struct si4713_device *sdev, u8 power, u8 antcap) { int err; u8 val[SI4713_TXPWR_NRESP]; /* * .First byte = 0 * .Second byte = 0 * .Third byte = power * .Fourth byte = antcap */ const u8 args[SI4713_TXPWR_NARGS] = { 0x00, 0x00, power, antcap, }; if (((power > 0) && (power < SI4713_MIN_POWER)) || power > SI4713_MAX_POWER || antcap > SI4713_MAX_ANTCAP) return -EDOM; err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_POWER, args, ARRAY_SIZE(args), val, ARRAY_SIZE(val), DEFAULT_TIMEOUT); if (err < 0) return err; v4l2_dbg(1, debug, &sdev->sd, "%s: power=0x%02x antcap=0x%02x status=0x%02x\n", __func__, power, antcap, val[0]); return si4713_wait_stc(sdev, TIMEOUT_TX_TUNE_POWER); }
/* * si4713_tx_tune_freq - Sets the state of the RF carrier and sets the tuning * frequency between 76 and 108 MHz in 10 kHz units and * steps of 50 kHz. * @sdev: si4713_device structure for the device we are communicating * @frequency: desired frequency (76 - 108 MHz, unit 10 KHz, step 50 kHz) */ static int si4713_tx_tune_freq(struct si4713_device *sdev, u16 frequency) { int err; u8 val[SI4713_TXFREQ_NRESP]; /* * .First byte = 0 * .Second byte = frequency's MSB * .Third byte = frequency's LSB */ const u8 args[SI4713_TXFREQ_NARGS] = { 0x00, msb(frequency), lsb(frequency), }; err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_FREQ, args, ARRAY_SIZE(args), val, ARRAY_SIZE(val), DEFAULT_TIMEOUT); if (err < 0) return err; v4l2_dbg(1, debug, &sdev->sd, "%s: frequency=0x%02x status=0x%02x\n", __func__, frequency, val[0]); err = si4713_wait_stc(sdev, TIMEOUT_TX_TUNE); if (err < 0) return err; return compose_u16(args[1], args[2]); }
/* * si4713_tx_tune_power - Sets the RF voltage level between 88 and 120 dBuV in * 1 dB units. A value of 0x00 indicates off. The command * also sets the antenna tuning capacitance. A value of 0 * indicates autotuning, and a value of 1 - 191 indicates * a manual override, which results in a tuning * capacitance of 0.25 pF x @antcap. * @sdev: si4713_device structure for the device we are communicating * @power: tuning power (88 - 120 dBuV, unit/step 1 dB) * @antcap: value of antenna tuning capacitor (0 - 191) */ static int si4713_tx_tune_power(struct si4713_device *sdev, u8 power, u8 antcap) { int err; u8 val[SI4713_TXPWR_NRESP]; /* * .First byte = 0 * .Second byte = 0 * .Third byte = power * .Fourth byte = antcap */ u8 args[SI4713_TXPWR_NARGS] = { 0x00, 0x00, power, antcap, }; /* Map power values 1-87 to MIN_POWER (88) */ if (power > 0 && power < SI4713_MIN_POWER) args[2] = power = SI4713_MIN_POWER; err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_POWER, args, ARRAY_SIZE(args), val, ARRAY_SIZE(val), DEFAULT_TIMEOUT); if (err < 0) return err; v4l2_dbg(1, debug, &sdev->sd, "%s: power=0x%02x antcap=0x%02x status=0x%02x\n", __func__, power, antcap, val[0]); return si4713_wait_stc(sdev, TIMEOUT_TX_TUNE_POWER); }
/* * si4713_read_property - reads a si4713 property * @sdev: si4713_device structure for the device we are communicating * @prop: property identification number * @pv: property value to be returned on success */ static int si4713_read_property(struct si4713_device *sdev, u16 prop, u32 *pv) { int err; u8 val[SI4713_GET_PROP_NRESP]; /* * .First byte = 0 * .Second byte = property's MSB * .Third byte = property's LSB */ const u8 args[SI4713_GET_PROP_NARGS] = { 0x00, msb(prop), lsb(prop), }; err = si4713_send_command(sdev, SI4713_CMD_GET_PROPERTY, args, ARRAY_SIZE(args), val, ARRAY_SIZE(val), DEFAULT_TIMEOUT); if (err < 0) return err; *pv = compose_u16(val[2], val[3]); v4l2_dbg(1, debug, &sdev->sd, "%s: property=0x%02x value=0x%02x status=0x%02x\n", __func__, prop, *pv, val[0]); return err; }
/* * si4713_tx_tune_status- Returns the status of the tx_tune_freq, tx_tune_mea or * tx_tune_power commands. This command return the current * frequency, output voltage in dBuV, the antenna tunning * capacitance value and the received noise level. The * command also clears the stcint interrupt bit when the * first bit of its arguments is high. * @sdev: si4713_device structure for the device we are communicating * @intack: 0x01 to clear the seek/tune complete interrupt status indicator. * @frequency: returned frequency * @power: returned power * @antcap: returned antenna capacitance * @noise: returned noise level */ static int si4713_tx_tune_status(struct si4713_device *sdev, u8 intack, u16 *frequency, u8 *power, u8 *antcap, u8 *noise) { int err; u8 val[SI4713_TXSTATUS_NRESP]; /* * .First byte = intack bit */ const u8 args[SI4713_TXSTATUS_NARGS] = { intack & SI4713_INTACK_MASK, }; err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_STATUS, args, ARRAY_SIZE(args), val, ARRAY_SIZE(val), DEFAULT_TIMEOUT); if (!err) { v4l2_dbg(1, debug, &sdev->sd, "%s: status=0x%02x\n", __func__, val[0]); *frequency = compose_u16(val[2], val[3]); sdev->frequency = *frequency; *power = val[5]; *antcap = val[6]; *noise = val[7]; v4l2_dbg(1, debug, &sdev->sd, "%s: response: %d x 10 kHz " "(power %d, antcap %d, rnl %d)\n", __func__, *frequency, *power, *antcap, *noise); } return err; }
/* * si4713_tx_rds_buff - Loads the RDS group buffer FIFO or circular buffer. * @sdev: si4713_device structure for the device we are communicating * @mode: the buffer operation mode. * @rdsb: RDS Block B * @rdsc: RDS Block C * @rdsd: RDS Block D * @cbleft: returns the number of available circular buffer blocks minus the * number of used circular buffer blocks. */ static int si4713_tx_rds_buff(struct si4713_device *sdev, u8 mode, u16 rdsb, u16 rdsc, u16 rdsd, s8 *cbleft) { int err; u8 val[SI4713_RDSBUFF_NRESP]; const u8 args[SI4713_RDSBUFF_NARGS] = { mode & SI4713_RDSBUFF_MODE_MASK, msb(rdsb), lsb(rdsb), msb(rdsc), lsb(rdsc), msb(rdsd), lsb(rdsd), }; err = si4713_send_command(sdev, SI4713_CMD_TX_RDS_BUFF, args, ARRAY_SIZE(args), val, ARRAY_SIZE(val), DEFAULT_TIMEOUT); if (!err) { v4l2_dbg(1, debug, &sdev->sd, "%s: status=0x%02x\n", __func__, val[0]); *cbleft = (s8)val[2] - val[3]; v4l2_dbg(1, debug, &sdev->sd, "%s: response: interrupts" " 0x%02x cb avail: %d cb used %d fifo avail" " %d fifo used %d\n", __func__, val[1], val[2], val[3], val[4], val[5]); } return err; }
static int si4713_checkrev(struct si4713_device *sdev) { struct i2c_client *client = v4l2_get_subdevdata(&sdev->sd); int rval; u8 resp[SI4713_GETREV_NRESP]; mutex_lock(&sdev->mutex); rval = si4713_send_command(sdev, SI4713_CMD_GET_REV, NULL, 0, resp, ARRAY_SIZE(resp), DEFAULT_TIMEOUT); if (rval < 0) goto unlock; if (resp[1] == SI4713_PRODUCT_NUMBER) { v4l2_info(&sdev->sd, "chip found @ 0x%02x (%s)\n", client->addr << 1, client->adapter->name); } else { v4l2_err(&sdev->sd, "Invalid product number\n"); rval = -EINVAL; } unlock: mutex_unlock(&sdev->mutex); return rval; }
/* * si4713_wait_stc - Waits STC interrupt and clears status bits. Useful * for TX_TUNE_POWER, TX_TUNE_FREQ and TX_TUNE_MEAS * @sdev: si4713_device structure for the device we are communicating * @usecs: timeout to wait for STC interrupt signal */ static int si4713_wait_stc(struct si4713_device *sdev, const int usecs) { int err; u8 resp[SI4713_GET_STATUS_NRESP]; /* Wait response from STC interrupt */ if (!wait_for_completion_timeout(&sdev->work, usecs_to_jiffies(usecs) + 1)) v4l2_warn(&sdev->sd, "%s: device took too much time to answer (%d usec).\n", __func__, usecs); /* Clear status bits */ err = si4713_send_command(sdev, SI4713_CMD_GET_INT_STATUS, NULL, 0, resp, ARRAY_SIZE(resp), DEFAULT_TIMEOUT); if (err < 0) goto exit; v4l2_dbg(1, debug, &sdev->sd, "%s: status bits: 0x%02x\n", __func__, resp[0]); if (!(resp[0] & SI4713_STC_INT)) err = -EIO; exit: return err; }
/* * si4713_tx_rds_ps - Loads the program service buffer. * @sdev: si4713_device structure for the device we are communicating * @psid: program service id to be loaded. * @pschar: assumed 4 size char array to be loaded into the program service */ static int si4713_tx_rds_ps(struct si4713_device *sdev, u8 psid, unsigned char *pschar) { int err; u8 val[SI4713_RDSPS_NRESP]; const u8 args[SI4713_RDSPS_NARGS] = { psid & SI4713_RDSPS_PSID_MASK, pschar[0], pschar[1], pschar[2], pschar[3], }; err = si4713_send_command(sdev, SI4713_CMD_TX_RDS_PS, args, ARRAY_SIZE(args), val, ARRAY_SIZE(val), DEFAULT_TIMEOUT); if (err < 0) return err; v4l2_dbg(1, debug, &sdev->sd, "%s: status=0x%02x\n", __func__, val[0]); return err; }
/* * si4713_powerup - Powers the device up * @sdev: si4713_device structure for the device we are communicating */ static int si4713_powerup(struct si4713_device *sdev) { int err; u8 resp[SI4713_PWUP_NRESP]; /* * .First byte = Enabled interrupts and boot function * .Second byte = Input operation mode */ const u8 args[SI4713_PWUP_NARGS] = { SI4713_PWUP_CTSIEN | SI4713_PWUP_GPO2OEN | SI4713_PWUP_FUNC_TX, SI4713_PWUP_OPMOD_ANALOG, }; if (sdev->power_state) return 0; err = regulator_bulk_enable(ARRAY_SIZE(sdev->supplies), sdev->supplies); if (err) { v4l2_err(&sdev->sd, "Failed to enable supplies: %d\n", err); return err; } if (gpio_is_valid(sdev->gpio_reset)) { udelay(50); gpio_set_value(sdev->gpio_reset, 1); } err = si4713_send_command(sdev, SI4713_CMD_POWER_UP, args, ARRAY_SIZE(args), resp, ARRAY_SIZE(resp), TIMEOUT_POWER_UP); if (!err) { v4l2_dbg(1, debug, &sdev->sd, "Powerup response: 0x%02x\n", resp[0]); v4l2_dbg(1, debug, &sdev->sd, "Device in power up mode\n"); sdev->power_state = POWER_ON; err = si4713_write_property(sdev, SI4713_GPO_IEN, SI4713_STC_INT | SI4713_CTS); } else { if (gpio_is_valid(sdev->gpio_reset)) gpio_set_value(sdev->gpio_reset, 0); err = regulator_bulk_disable(ARRAY_SIZE(sdev->supplies), sdev->supplies); if (err) v4l2_err(&sdev->sd, "Failed to disable supplies: %d\n", err); } return err; }
/* * si4713_write_property - modifies a si4713 property * @sdev: si4713_device structure for the device we are communicating * @prop: property identification number * @val: new value for that property */ static int si4713_write_property(struct si4713_device *sdev, u16 prop, u16 val) { int rval; u8 resp[SI4713_SET_PROP_NRESP]; /* * .First byte = 0 * .Second byte = property's MSB * .Third byte = property's LSB * .Fourth byte = value's MSB * .Fifth byte = value's LSB */ const u8 args[SI4713_SET_PROP_NARGS] = { 0x00, msb(prop), lsb(prop), msb(val), lsb(val), }; rval = si4713_send_command(sdev, SI4713_CMD_SET_PROPERTY, args, ARRAY_SIZE(args), resp, ARRAY_SIZE(resp), DEFAULT_TIMEOUT); if (rval < 0) return rval; v4l2_dbg(1, debug, &sdev->sd, "%s: property=0x%02x value=0x%02x status=0x%02x\n", __func__, prop, val, resp[0]); /* * As there is no command response for SET_PROPERTY, * wait Tcomp time to finish before proceed, in order * to have property properly set. */ msleep(TIMEOUT_SET_PROPERTY); return rval; }
static int si4713_powerdown(struct si4713_device *sdev) { int err; u8 resp[SI4713_PWDN_NRESP]; if (!sdev->power_state) return 0; err = si4713_send_command(sdev, SI4713_CMD_POWER_DOWN, NULL, 0, resp, ARRAY_SIZE(resp), DEFAULT_TIMEOUT); if (!err) { v4l2_dbg(1, debug, &sdev->sd, "Power down response: 0x%02x\n", resp[0]); v4l2_dbg(1, debug, &sdev->sd, "Device in reset mode\n"); sdev->platform_data->set_power(0); sdev->power_state = POWER_OFF; } return err; }
static int si4713_powerup(struct si4713_device *sdev) { int err; u8 resp[SI4713_PWUP_NRESP]; /* * .First byte = Enabled interrupts and boot function * .Second byte = Input operation mode */ const u8 args[SI4713_PWUP_NARGS] = { SI4713_PWUP_CTSIEN | SI4713_PWUP_GPO2OEN | SI4713_PWUP_FUNC_TX, SI4713_PWUP_OPMOD_ANALOG, }; if (sdev->power_state) return 0; sdev->platform_data->set_power(1); err = si4713_send_command(sdev, SI4713_CMD_POWER_UP, args, ARRAY_SIZE(args), resp, ARRAY_SIZE(resp), TIMEOUT_POWER_UP); if (!err) { v4l2_dbg(1, debug, &sdev->sd, "Powerup response: 0x%02x\n", resp[0]); v4l2_dbg(1, debug, &sdev->sd, "Device in power up mode\n"); sdev->power_state = POWER_ON; err = si4713_write_property(sdev, SI4713_GPO_IEN, SI4713_STC_INT | SI4713_CTS); } else { sdev->platform_data->set_power(0); } return err; }
/* * si4713_wait_stc - Waits STC interrupt and clears status bits. Useful * for TX_TUNE_POWER, TX_TUNE_FREQ and TX_TUNE_MEAS * @sdev: si4713_device structure for the device we are communicating * @usecs: timeout to wait for STC interrupt signal */ static int si4713_wait_stc(struct si4713_device *sdev, const int usecs) { struct i2c_client *client = v4l2_get_subdevdata(&sdev->sd); u8 resp[SI4713_GET_STATUS_NRESP]; unsigned long start_jiffies = jiffies; int err; if (client->irq && !wait_for_completion_timeout(&sdev->work, usecs_to_jiffies(usecs) + 1)) v4l2_warn(&sdev->sd, "(%s) Device took too much time to answer.\n", __func__); for (;;) { /* Clear status bits */ err = si4713_send_command(sdev, SI4713_CMD_GET_INT_STATUS, NULL, 0, resp, ARRAY_SIZE(resp), DEFAULT_TIMEOUT); /* The USB device returns errors when it waits for the * STC bit to be set. Hence polling */ if (err >= 0) { v4l2_dbg(1, debug, &sdev->sd, "%s: status bits: 0x%02x\n", __func__, resp[0]); if (resp[0] & SI4713_STC_INT) return 0; } if (jiffies_to_usecs(jiffies - start_jiffies) > usecs) return err < 0 ? err : -EIO; /* We sleep here for 3-4 ms in order to avoid flooding the device * with USB requests. The si4713 USB driver was developed * by reverse engineering the Windows USB driver. The windows * driver also has a ~2.5 ms delay between responses. */ usleep_range(3000, 4000); } }
/* * si4713_tx_tune_measure - Enters receive mode and measures the received noise * level in units of dBuV on the selected frequency. * The Frequency must be between 76 and 108 MHz in 10 kHz * units and steps of 50 kHz. The command also sets the * antenna tuning capacitance. A value of 0 means * autotuning, and a value of 1 to 191 indicates manual * override. * @sdev: si4713_device structure for the device we are communicating * @frequency: desired frequency (76 - 108 MHz, unit 10 KHz, step 50 kHz) * @antcap: value of antenna tuning capacitor (0 - 191) */ static int si4713_tx_tune_measure(struct si4713_device *sdev, u16 frequency, u8 antcap) { int err; u8 val[SI4713_TXMEA_NRESP]; /* * .First byte = 0 * .Second byte = frequency's MSB * .Third byte = frequency's LSB * .Fourth byte = antcap */ const u8 args[SI4713_TXMEA_NARGS] = { 0x00, msb(frequency), lsb(frequency), antcap, }; sdev->tune_rnl = DEFAULT_TUNE_RNL; if (antcap > SI4713_MAX_ANTCAP) return -EDOM; err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_MEASURE, args, ARRAY_SIZE(args), val, ARRAY_SIZE(val), DEFAULT_TIMEOUT); if (err < 0) return err; v4l2_dbg(1, debug, &sdev->sd, "%s: frequency=0x%02x antcap=0x%02x status=0x%02x\n", __func__, frequency, antcap, val[0]); return si4713_wait_stc(sdev, TIMEOUT_TX_TUNE); }
/* * si4713_powerup - Powers the device up * @sdev: si4713_device structure for the device we are communicating */ static int si4713_powerup(struct si4713_device *sdev) { struct i2c_client *client = v4l2_get_subdevdata(&sdev->sd); int err; u8 resp[SI4713_PWUP_NRESP]; /* * .First byte = Enabled interrupts and boot function * .Second byte = Input operation mode */ u8 args[SI4713_PWUP_NARGS] = { SI4713_PWUP_GPO2OEN | SI4713_PWUP_FUNC_TX, SI4713_PWUP_OPMOD_ANALOG, }; if (sdev->power_state) return 0; if (sdev->vdd) { err = regulator_enable(sdev->vdd); if (err) { v4l2_err(&sdev->sd, "Failed to enable vdd: %d\n", err); return err; } } if (sdev->vio) { err = regulator_enable(sdev->vio); if (err) { v4l2_err(&sdev->sd, "Failed to enable vio: %d\n", err); return err; } } if (sdev->gpio_reset) { udelay(50); gpiod_set_value(sdev->gpio_reset, 1); } if (client->irq) args[0] |= SI4713_PWUP_CTSIEN; err = si4713_send_command(sdev, SI4713_CMD_POWER_UP, args, ARRAY_SIZE(args), resp, ARRAY_SIZE(resp), TIMEOUT_POWER_UP); if (!err) { v4l2_dbg(1, debug, &sdev->sd, "Powerup response: 0x%02x\n", resp[0]); v4l2_dbg(1, debug, &sdev->sd, "Device in power up mode\n"); sdev->power_state = POWER_ON; if (client->irq) err = si4713_write_property(sdev, SI4713_GPO_IEN, SI4713_STC_INT | SI4713_CTS); return err; } gpiod_set_value(sdev->gpio_reset, 0); if (sdev->vdd) { err = regulator_disable(sdev->vdd); if (err) v4l2_err(&sdev->sd, "Failed to disable vdd: %d\n", err); } if (sdev->vio) { err = regulator_disable(sdev->vio); if (err) v4l2_err(&sdev->sd, "Failed to disable vio: %d\n", err); } return err; }