162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/mfd/si476x-i2c.c -- Core device driver for si476x MFD 462306a36Sopenharmony_ci * device 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2012 Innovative Converged Devices(ICD) 762306a36Sopenharmony_ci * Copyright (C) 2013 Andrey Smirnov 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Author: Andrey Smirnov <andrew.smirnov@gmail.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/gpio.h> 1762306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1862306a36Sopenharmony_ci#include <linux/i2c.h> 1962306a36Sopenharmony_ci#include <linux/err.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/mfd/si476x-core.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define SI476X_MAX_IO_ERRORS 10 2462306a36Sopenharmony_ci#define SI476X_DRIVER_RDS_FIFO_DEPTH 128 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/** 2762306a36Sopenharmony_ci * si476x_core_config_pinmux() - pin function configuration function 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * @core: Core device structure 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * Configure the functions of the pins of the radio chip. 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * The function returns zero in case of succes or negative error code 3462306a36Sopenharmony_ci * otherwise. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_cistatic int si476x_core_config_pinmux(struct si476x_core *core) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci int err; 3962306a36Sopenharmony_ci dev_dbg(&core->client->dev, "Configuring pinmux\n"); 4062306a36Sopenharmony_ci err = si476x_core_cmd_dig_audio_pin_cfg(core, 4162306a36Sopenharmony_ci core->pinmux.dclk, 4262306a36Sopenharmony_ci core->pinmux.dfs, 4362306a36Sopenharmony_ci core->pinmux.dout, 4462306a36Sopenharmony_ci core->pinmux.xout); 4562306a36Sopenharmony_ci if (err < 0) { 4662306a36Sopenharmony_ci dev_err(&core->client->dev, 4762306a36Sopenharmony_ci "Failed to configure digital audio pins(err = %d)\n", 4862306a36Sopenharmony_ci err); 4962306a36Sopenharmony_ci return err; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci err = si476x_core_cmd_zif_pin_cfg(core, 5362306a36Sopenharmony_ci core->pinmux.iqclk, 5462306a36Sopenharmony_ci core->pinmux.iqfs, 5562306a36Sopenharmony_ci core->pinmux.iout, 5662306a36Sopenharmony_ci core->pinmux.qout); 5762306a36Sopenharmony_ci if (err < 0) { 5862306a36Sopenharmony_ci dev_err(&core->client->dev, 5962306a36Sopenharmony_ci "Failed to configure ZIF pins(err = %d)\n", 6062306a36Sopenharmony_ci err); 6162306a36Sopenharmony_ci return err; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci err = si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(core, 6562306a36Sopenharmony_ci core->pinmux.icin, 6662306a36Sopenharmony_ci core->pinmux.icip, 6762306a36Sopenharmony_ci core->pinmux.icon, 6862306a36Sopenharmony_ci core->pinmux.icop); 6962306a36Sopenharmony_ci if (err < 0) { 7062306a36Sopenharmony_ci dev_err(&core->client->dev, 7162306a36Sopenharmony_ci "Failed to configure IC-Link/GPO pins(err = %d)\n", 7262306a36Sopenharmony_ci err); 7362306a36Sopenharmony_ci return err; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci err = si476x_core_cmd_ana_audio_pin_cfg(core, 7762306a36Sopenharmony_ci core->pinmux.lrout); 7862306a36Sopenharmony_ci if (err < 0) { 7962306a36Sopenharmony_ci dev_err(&core->client->dev, 8062306a36Sopenharmony_ci "Failed to configure analog audio pins(err = %d)\n", 8162306a36Sopenharmony_ci err); 8262306a36Sopenharmony_ci return err; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci err = si476x_core_cmd_intb_pin_cfg(core, 8662306a36Sopenharmony_ci core->pinmux.intb, 8762306a36Sopenharmony_ci core->pinmux.a1); 8862306a36Sopenharmony_ci if (err < 0) { 8962306a36Sopenharmony_ci dev_err(&core->client->dev, 9062306a36Sopenharmony_ci "Failed to configure interrupt pins(err = %d)\n", 9162306a36Sopenharmony_ci err); 9262306a36Sopenharmony_ci return err; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic inline void si476x_core_schedule_polling_work(struct si476x_core *core) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci schedule_delayed_work(&core->status_monitor, 10162306a36Sopenharmony_ci usecs_to_jiffies(SI476X_STATUS_POLL_US)); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/** 10562306a36Sopenharmony_ci * si476x_core_start() - early chip startup function 10662306a36Sopenharmony_ci * @core: Core device structure 10762306a36Sopenharmony_ci * @soft: When set, this flag forces "soft" startup, where "soft" 10862306a36Sopenharmony_ci * power down is the one done by sending appropriate command instead 10962306a36Sopenharmony_ci * of using reset pin of the tuner 11062306a36Sopenharmony_ci * 11162306a36Sopenharmony_ci * Perform required startup sequence to correctly power 11262306a36Sopenharmony_ci * up the chip and perform initial configuration. It does the 11362306a36Sopenharmony_ci * following sequence of actions: 11462306a36Sopenharmony_ci * 1. Claims and enables the power supplies VD and VIO1 required 11562306a36Sopenharmony_ci * for I2C interface of the chip operation. 11662306a36Sopenharmony_ci * 2. Waits for 100us, pulls the reset line up, enables irq, 11762306a36Sopenharmony_ci * waits for another 100us as it is specified by the 11862306a36Sopenharmony_ci * datasheet. 11962306a36Sopenharmony_ci * 3. Sends 'POWER_UP' command to the device with all provided 12062306a36Sopenharmony_ci * information about power-up parameters. 12162306a36Sopenharmony_ci * 4. Configures, pin multiplexor, disables digital audio and 12262306a36Sopenharmony_ci * configures interrupt sources. 12362306a36Sopenharmony_ci * 12462306a36Sopenharmony_ci * The function returns zero in case of succes or negative error code 12562306a36Sopenharmony_ci * otherwise. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_ciint si476x_core_start(struct si476x_core *core, bool soft) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct i2c_client *client = core->client; 13062306a36Sopenharmony_ci int err; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (!soft) { 13362306a36Sopenharmony_ci if (gpio_is_valid(core->gpio_reset)) 13462306a36Sopenharmony_ci gpio_set_value_cansleep(core->gpio_reset, 1); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (client->irq) 13762306a36Sopenharmony_ci enable_irq(client->irq); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci udelay(100); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (!client->irq) { 14262306a36Sopenharmony_ci atomic_set(&core->is_alive, 1); 14362306a36Sopenharmony_ci si476x_core_schedule_polling_work(core); 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci } else { 14662306a36Sopenharmony_ci if (client->irq) 14762306a36Sopenharmony_ci enable_irq(client->irq); 14862306a36Sopenharmony_ci else { 14962306a36Sopenharmony_ci atomic_set(&core->is_alive, 1); 15062306a36Sopenharmony_ci si476x_core_schedule_polling_work(core); 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci err = si476x_core_cmd_power_up(core, 15562306a36Sopenharmony_ci &core->power_up_parameters); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (err < 0) { 15862306a36Sopenharmony_ci dev_err(&core->client->dev, 15962306a36Sopenharmony_ci "Power up failure(err = %d)\n", 16062306a36Sopenharmony_ci err); 16162306a36Sopenharmony_ci goto disable_irq; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (client->irq) 16562306a36Sopenharmony_ci atomic_set(&core->is_alive, 1); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci err = si476x_core_config_pinmux(core); 16862306a36Sopenharmony_ci if (err < 0) { 16962306a36Sopenharmony_ci dev_err(&core->client->dev, 17062306a36Sopenharmony_ci "Failed to configure pinmux(err = %d)\n", 17162306a36Sopenharmony_ci err); 17262306a36Sopenharmony_ci goto disable_irq; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (client->irq) { 17662306a36Sopenharmony_ci err = regmap_write(core->regmap, 17762306a36Sopenharmony_ci SI476X_PROP_INT_CTL_ENABLE, 17862306a36Sopenharmony_ci SI476X_RDSIEN | 17962306a36Sopenharmony_ci SI476X_STCIEN | 18062306a36Sopenharmony_ci SI476X_CTSIEN); 18162306a36Sopenharmony_ci if (err < 0) { 18262306a36Sopenharmony_ci dev_err(&core->client->dev, 18362306a36Sopenharmony_ci "Failed to configure interrupt sources" 18462306a36Sopenharmony_ci "(err = %d)\n", err); 18562306a36Sopenharmony_ci goto disable_irq; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cidisable_irq: 19262306a36Sopenharmony_ci if (err == -ENODEV) 19362306a36Sopenharmony_ci atomic_set(&core->is_alive, 0); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (client->irq) 19662306a36Sopenharmony_ci disable_irq(client->irq); 19762306a36Sopenharmony_ci else 19862306a36Sopenharmony_ci cancel_delayed_work_sync(&core->status_monitor); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (gpio_is_valid(core->gpio_reset)) 20162306a36Sopenharmony_ci gpio_set_value_cansleep(core->gpio_reset, 0); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return err; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_start); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/** 20862306a36Sopenharmony_ci * si476x_core_stop() - chip power-down function 20962306a36Sopenharmony_ci * @core: Core device structure 21062306a36Sopenharmony_ci * @soft: When set, function sends a POWER_DOWN command instead of 21162306a36Sopenharmony_ci * bringing reset line low 21262306a36Sopenharmony_ci * 21362306a36Sopenharmony_ci * Power down the chip by performing following actions: 21462306a36Sopenharmony_ci * 1. Disable IRQ or stop the polling worker 21562306a36Sopenharmony_ci * 2. Send the POWER_DOWN command if the power down is soft or bring 21662306a36Sopenharmony_ci * reset line low if not. 21762306a36Sopenharmony_ci * 21862306a36Sopenharmony_ci * The function returns zero in case of succes or negative error code 21962306a36Sopenharmony_ci * otherwise. 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_ciint si476x_core_stop(struct si476x_core *core, bool soft) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci int err = 0; 22462306a36Sopenharmony_ci atomic_set(&core->is_alive, 0); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (soft) { 22762306a36Sopenharmony_ci /* TODO: This probably shoud be a configurable option, 22862306a36Sopenharmony_ci * so it is possible to have the chips keep their 22962306a36Sopenharmony_ci * oscillators running 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci struct si476x_power_down_args args = { 23262306a36Sopenharmony_ci .xosc = false, 23362306a36Sopenharmony_ci }; 23462306a36Sopenharmony_ci err = si476x_core_cmd_power_down(core, &args); 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* We couldn't disable those before 23862306a36Sopenharmony_ci * 'si476x_core_cmd_power_down' since we expect to get CTS 23962306a36Sopenharmony_ci * interrupt */ 24062306a36Sopenharmony_ci if (core->client->irq) 24162306a36Sopenharmony_ci disable_irq(core->client->irq); 24262306a36Sopenharmony_ci else 24362306a36Sopenharmony_ci cancel_delayed_work_sync(&core->status_monitor); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (!soft) { 24662306a36Sopenharmony_ci if (gpio_is_valid(core->gpio_reset)) 24762306a36Sopenharmony_ci gpio_set_value_cansleep(core->gpio_reset, 0); 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci return err; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_stop); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci/** 25462306a36Sopenharmony_ci * si476x_core_set_power_state() - set the level at which the power is 25562306a36Sopenharmony_ci * supplied for the chip. 25662306a36Sopenharmony_ci * @core: Core device structure 25762306a36Sopenharmony_ci * @next_state: enum si476x_power_state describing power state to 25862306a36Sopenharmony_ci * switch to. 25962306a36Sopenharmony_ci * 26062306a36Sopenharmony_ci * Switch on all the required power supplies 26162306a36Sopenharmony_ci * 26262306a36Sopenharmony_ci * This function returns 0 in case of suvccess and negative error code 26362306a36Sopenharmony_ci * otherwise. 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_ciint si476x_core_set_power_state(struct si476x_core *core, 26662306a36Sopenharmony_ci enum si476x_power_state next_state) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci /* 26962306a36Sopenharmony_ci It is not clear form the datasheet if it is possible to 27062306a36Sopenharmony_ci work with device if not all power domains are operational. 27162306a36Sopenharmony_ci So for now the power-up policy is "power-up all the things!" 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_ci int err = 0; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (core->power_state == SI476X_POWER_INCONSISTENT) { 27662306a36Sopenharmony_ci dev_err(&core->client->dev, 27762306a36Sopenharmony_ci "The device in inconsistent power state\n"); 27862306a36Sopenharmony_ci return -EINVAL; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (next_state != core->power_state) { 28262306a36Sopenharmony_ci switch (next_state) { 28362306a36Sopenharmony_ci case SI476X_POWER_UP_FULL: 28462306a36Sopenharmony_ci err = regulator_bulk_enable(ARRAY_SIZE(core->supplies), 28562306a36Sopenharmony_ci core->supplies); 28662306a36Sopenharmony_ci if (err < 0) { 28762306a36Sopenharmony_ci core->power_state = SI476X_POWER_INCONSISTENT; 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci /* 29162306a36Sopenharmony_ci * Startup timing diagram recommends to have a 29262306a36Sopenharmony_ci * 100 us delay between enabling of the power 29362306a36Sopenharmony_ci * supplies and turning the tuner on. 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci udelay(100); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci err = si476x_core_start(core, false); 29862306a36Sopenharmony_ci if (err < 0) 29962306a36Sopenharmony_ci goto disable_regulators; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci core->power_state = next_state; 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci case SI476X_POWER_DOWN: 30562306a36Sopenharmony_ci core->power_state = next_state; 30662306a36Sopenharmony_ci err = si476x_core_stop(core, false); 30762306a36Sopenharmony_ci if (err < 0) 30862306a36Sopenharmony_ci core->power_state = SI476X_POWER_INCONSISTENT; 30962306a36Sopenharmony_cidisable_regulators: 31062306a36Sopenharmony_ci err = regulator_bulk_disable(ARRAY_SIZE(core->supplies), 31162306a36Sopenharmony_ci core->supplies); 31262306a36Sopenharmony_ci if (err < 0) 31362306a36Sopenharmony_ci core->power_state = SI476X_POWER_INCONSISTENT; 31462306a36Sopenharmony_ci break; 31562306a36Sopenharmony_ci default: 31662306a36Sopenharmony_ci BUG(); 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return err; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_set_power_state); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci/** 32562306a36Sopenharmony_ci * si476x_core_report_drainer_stop() - mark the completion of the RDS 32662306a36Sopenharmony_ci * buffer drain porcess by the worker. 32762306a36Sopenharmony_ci * 32862306a36Sopenharmony_ci * @core: Core device structure 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_cistatic inline void si476x_core_report_drainer_stop(struct si476x_core *core) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci mutex_lock(&core->rds_drainer_status_lock); 33362306a36Sopenharmony_ci core->rds_drainer_is_working = false; 33462306a36Sopenharmony_ci mutex_unlock(&core->rds_drainer_status_lock); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci/** 33862306a36Sopenharmony_ci * si476x_core_start_rds_drainer_once() - start RDS drainer worker if 33962306a36Sopenharmony_ci * ther is none working, do nothing otherwise 34062306a36Sopenharmony_ci * 34162306a36Sopenharmony_ci * @core: Datastructure corresponding to the chip. 34262306a36Sopenharmony_ci */ 34362306a36Sopenharmony_cistatic inline void si476x_core_start_rds_drainer_once(struct si476x_core *core) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci mutex_lock(&core->rds_drainer_status_lock); 34662306a36Sopenharmony_ci if (!core->rds_drainer_is_working) { 34762306a36Sopenharmony_ci core->rds_drainer_is_working = true; 34862306a36Sopenharmony_ci schedule_work(&core->rds_fifo_drainer); 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci mutex_unlock(&core->rds_drainer_status_lock); 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci/** 35362306a36Sopenharmony_ci * si476x_core_drain_rds_fifo() - RDS buffer drainer. 35462306a36Sopenharmony_ci * @work: struct work_struct being ppassed to the function by the 35562306a36Sopenharmony_ci * kernel. 35662306a36Sopenharmony_ci * 35762306a36Sopenharmony_ci * Drain the contents of the RDS FIFO of 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_cistatic void si476x_core_drain_rds_fifo(struct work_struct *work) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci int err; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci struct si476x_core *core = container_of(work, struct si476x_core, 36462306a36Sopenharmony_ci rds_fifo_drainer); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci struct si476x_rds_status_report report; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci si476x_core_lock(core); 36962306a36Sopenharmony_ci err = si476x_core_cmd_fm_rds_status(core, true, false, false, &report); 37062306a36Sopenharmony_ci if (!err) { 37162306a36Sopenharmony_ci int i = report.rdsfifoused; 37262306a36Sopenharmony_ci dev_dbg(&core->client->dev, 37362306a36Sopenharmony_ci "%d elements in RDS FIFO. Draining.\n", i); 37462306a36Sopenharmony_ci for (; i > 0; --i) { 37562306a36Sopenharmony_ci err = si476x_core_cmd_fm_rds_status(core, false, false, 37662306a36Sopenharmony_ci (i == 1), &report); 37762306a36Sopenharmony_ci if (err < 0) 37862306a36Sopenharmony_ci goto unlock; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci kfifo_in(&core->rds_fifo, report.rds, 38162306a36Sopenharmony_ci sizeof(report.rds)); 38262306a36Sopenharmony_ci dev_dbg(&core->client->dev, "RDS data:\n %*ph\n", 38362306a36Sopenharmony_ci (int)sizeof(report.rds), report.rds); 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci dev_dbg(&core->client->dev, "Drrrrained!\n"); 38662306a36Sopenharmony_ci wake_up_interruptible(&core->rds_read_queue); 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ciunlock: 39062306a36Sopenharmony_ci si476x_core_unlock(core); 39162306a36Sopenharmony_ci si476x_core_report_drainer_stop(core); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci/** 39562306a36Sopenharmony_ci * si476x_core_pronounce_dead() 39662306a36Sopenharmony_ci * 39762306a36Sopenharmony_ci * @core: Core device structure 39862306a36Sopenharmony_ci * 39962306a36Sopenharmony_ci * Mark the device as being dead and wake up all potentially waiting 40062306a36Sopenharmony_ci * threads of execution. 40162306a36Sopenharmony_ci * 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_cistatic void si476x_core_pronounce_dead(struct si476x_core *core) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci dev_info(&core->client->dev, "Core device is dead.\n"); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci atomic_set(&core->is_alive, 0); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* Wake up al possible waiting processes */ 41062306a36Sopenharmony_ci wake_up_interruptible(&core->rds_read_queue); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci atomic_set(&core->cts, 1); 41362306a36Sopenharmony_ci wake_up(&core->command); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci atomic_set(&core->stc, 1); 41662306a36Sopenharmony_ci wake_up(&core->tuning); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci/** 42062306a36Sopenharmony_ci * si476x_core_i2c_xfer() 42162306a36Sopenharmony_ci * 42262306a36Sopenharmony_ci * @core: Core device structure 42362306a36Sopenharmony_ci * @type: Transfer type 42462306a36Sopenharmony_ci * @buf: Transfer buffer for/with data 42562306a36Sopenharmony_ci * @count: Transfer buffer size 42662306a36Sopenharmony_ci * 42762306a36Sopenharmony_ci * Perfrom and I2C transfer(either read or write) and keep a counter 42862306a36Sopenharmony_ci * of I/O errors. If the error counter rises above the threshold 42962306a36Sopenharmony_ci * pronounce device dead. 43062306a36Sopenharmony_ci * 43162306a36Sopenharmony_ci * The function returns zero on succes or negative error code on 43262306a36Sopenharmony_ci * failure. 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_ciint si476x_core_i2c_xfer(struct si476x_core *core, 43562306a36Sopenharmony_ci enum si476x_i2c_type type, 43662306a36Sopenharmony_ci char *buf, int count) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci static int io_errors_count; 43962306a36Sopenharmony_ci int err; 44062306a36Sopenharmony_ci if (type == SI476X_I2C_SEND) 44162306a36Sopenharmony_ci err = i2c_master_send(core->client, buf, count); 44262306a36Sopenharmony_ci else 44362306a36Sopenharmony_ci err = i2c_master_recv(core->client, buf, count); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (err < 0) { 44662306a36Sopenharmony_ci if (io_errors_count++ > SI476X_MAX_IO_ERRORS) 44762306a36Sopenharmony_ci si476x_core_pronounce_dead(core); 44862306a36Sopenharmony_ci } else { 44962306a36Sopenharmony_ci io_errors_count = 0; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci return err; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_i2c_xfer); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci/** 45762306a36Sopenharmony_ci * si476x_core_get_status() 45862306a36Sopenharmony_ci * @core: Core device structure 45962306a36Sopenharmony_ci * 46062306a36Sopenharmony_ci * Get the status byte of the core device by berforming one byte I2C 46162306a36Sopenharmony_ci * read. 46262306a36Sopenharmony_ci * 46362306a36Sopenharmony_ci * The function returns a status value or a negative error code on 46462306a36Sopenharmony_ci * error. 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_cistatic int si476x_core_get_status(struct si476x_core *core) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci u8 response; 46962306a36Sopenharmony_ci int err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, 47062306a36Sopenharmony_ci &response, sizeof(response)); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return (err < 0) ? err : response; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci/** 47662306a36Sopenharmony_ci * si476x_core_get_and_signal_status() - IRQ dispatcher 47762306a36Sopenharmony_ci * @core: Core device structure 47862306a36Sopenharmony_ci * 47962306a36Sopenharmony_ci * Dispatch the arrived interrupt request based on the value of the 48062306a36Sopenharmony_ci * status byte reported by the tuner. 48162306a36Sopenharmony_ci * 48262306a36Sopenharmony_ci */ 48362306a36Sopenharmony_cistatic void si476x_core_get_and_signal_status(struct si476x_core *core) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci int status = si476x_core_get_status(core); 48662306a36Sopenharmony_ci if (status < 0) { 48762306a36Sopenharmony_ci dev_err(&core->client->dev, "Failed to get status\n"); 48862306a36Sopenharmony_ci return; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (status & SI476X_CTS) { 49262306a36Sopenharmony_ci /* Unfortunately completions could not be used for 49362306a36Sopenharmony_ci * signalling CTS since this flag cannot be cleared 49462306a36Sopenharmony_ci * in status byte, and therefore once it becomes true 49562306a36Sopenharmony_ci * multiple calls to 'complete' would cause the 49662306a36Sopenharmony_ci * commands following the current one to be completed 49762306a36Sopenharmony_ci * before they actually are */ 49862306a36Sopenharmony_ci dev_dbg(&core->client->dev, "[interrupt] CTSINT\n"); 49962306a36Sopenharmony_ci atomic_set(&core->cts, 1); 50062306a36Sopenharmony_ci wake_up(&core->command); 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (status & SI476X_FM_RDS_INT) { 50462306a36Sopenharmony_ci dev_dbg(&core->client->dev, "[interrupt] RDSINT\n"); 50562306a36Sopenharmony_ci si476x_core_start_rds_drainer_once(core); 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (status & SI476X_STC_INT) { 50962306a36Sopenharmony_ci dev_dbg(&core->client->dev, "[interrupt] STCINT\n"); 51062306a36Sopenharmony_ci atomic_set(&core->stc, 1); 51162306a36Sopenharmony_ci wake_up(&core->tuning); 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic void si476x_core_poll_loop(struct work_struct *work) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci struct si476x_core *core = SI476X_WORK_TO_CORE(work); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci si476x_core_get_and_signal_status(core); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (atomic_read(&core->is_alive)) 52262306a36Sopenharmony_ci si476x_core_schedule_polling_work(core); 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic irqreturn_t si476x_core_interrupt(int irq, void *dev) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct si476x_core *core = dev; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci si476x_core_get_and_signal_status(core); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci return IRQ_HANDLED; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci/** 53562306a36Sopenharmony_ci * si476x_core_fwver_to_revision() 53662306a36Sopenharmony_ci * @core: Core device structure 53762306a36Sopenharmony_ci * @func: Selects the boot function of the device: 53862306a36Sopenharmony_ci * *_BOOTLOADER - Boot loader 53962306a36Sopenharmony_ci * *_FM_RECEIVER - FM receiver 54062306a36Sopenharmony_ci * *_AM_RECEIVER - AM receiver 54162306a36Sopenharmony_ci * *_WB_RECEIVER - Weatherband receiver 54262306a36Sopenharmony_ci * @major: Firmware major number 54362306a36Sopenharmony_ci * @minor1: Firmware first minor number 54462306a36Sopenharmony_ci * @minor2: Firmware second minor number 54562306a36Sopenharmony_ci * 54662306a36Sopenharmony_ci * Convert a chip's firmware version number into an offset that later 54762306a36Sopenharmony_ci * will be used to as offset in "vtable" of tuner functions 54862306a36Sopenharmony_ci * 54962306a36Sopenharmony_ci * This function returns a positive offset in case of success and a -1 55062306a36Sopenharmony_ci * in case of failure. 55162306a36Sopenharmony_ci */ 55262306a36Sopenharmony_cistatic int si476x_core_fwver_to_revision(struct si476x_core *core, 55362306a36Sopenharmony_ci int func, int major, 55462306a36Sopenharmony_ci int minor1, int minor2) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci switch (func) { 55762306a36Sopenharmony_ci case SI476X_FUNC_FM_RECEIVER: 55862306a36Sopenharmony_ci switch (major) { 55962306a36Sopenharmony_ci case 5: 56062306a36Sopenharmony_ci return SI476X_REVISION_A10; 56162306a36Sopenharmony_ci case 8: 56262306a36Sopenharmony_ci return SI476X_REVISION_A20; 56362306a36Sopenharmony_ci case 10: 56462306a36Sopenharmony_ci return SI476X_REVISION_A30; 56562306a36Sopenharmony_ci default: 56662306a36Sopenharmony_ci goto unknown_revision; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci case SI476X_FUNC_AM_RECEIVER: 56962306a36Sopenharmony_ci switch (major) { 57062306a36Sopenharmony_ci case 5: 57162306a36Sopenharmony_ci return SI476X_REVISION_A10; 57262306a36Sopenharmony_ci case 7: 57362306a36Sopenharmony_ci return SI476X_REVISION_A20; 57462306a36Sopenharmony_ci case 9: 57562306a36Sopenharmony_ci return SI476X_REVISION_A30; 57662306a36Sopenharmony_ci default: 57762306a36Sopenharmony_ci goto unknown_revision; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci case SI476X_FUNC_WB_RECEIVER: 58062306a36Sopenharmony_ci switch (major) { 58162306a36Sopenharmony_ci case 3: 58262306a36Sopenharmony_ci return SI476X_REVISION_A10; 58362306a36Sopenharmony_ci case 5: 58462306a36Sopenharmony_ci return SI476X_REVISION_A20; 58562306a36Sopenharmony_ci case 7: 58662306a36Sopenharmony_ci return SI476X_REVISION_A30; 58762306a36Sopenharmony_ci default: 58862306a36Sopenharmony_ci goto unknown_revision; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci case SI476X_FUNC_BOOTLOADER: 59162306a36Sopenharmony_ci default: /* FALLTHROUGH */ 59262306a36Sopenharmony_ci BUG(); 59362306a36Sopenharmony_ci return -1; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ciunknown_revision: 59762306a36Sopenharmony_ci dev_err(&core->client->dev, 59862306a36Sopenharmony_ci "Unsupported version of the firmware: %d.%d.%d, " 59962306a36Sopenharmony_ci "reverting to A10 compatible functions\n", 60062306a36Sopenharmony_ci major, minor1, minor2); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci return SI476X_REVISION_A10; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci/** 60662306a36Sopenharmony_ci * si476x_core_get_revision_info() 60762306a36Sopenharmony_ci * @core: Core device structure 60862306a36Sopenharmony_ci * 60962306a36Sopenharmony_ci * Get the firmware version number of the device. It is done in 61062306a36Sopenharmony_ci * following three steps: 61162306a36Sopenharmony_ci * 1. Power-up the device 61262306a36Sopenharmony_ci * 2. Send the 'FUNC_INFO' command 61362306a36Sopenharmony_ci * 3. Powering the device down. 61462306a36Sopenharmony_ci * 61562306a36Sopenharmony_ci * The function return zero on success and a negative error code on 61662306a36Sopenharmony_ci * failure. 61762306a36Sopenharmony_ci */ 61862306a36Sopenharmony_cistatic int si476x_core_get_revision_info(struct si476x_core *core) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci int rval; 62162306a36Sopenharmony_ci struct si476x_func_info info; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci si476x_core_lock(core); 62462306a36Sopenharmony_ci rval = si476x_core_set_power_state(core, SI476X_POWER_UP_FULL); 62562306a36Sopenharmony_ci if (rval < 0) 62662306a36Sopenharmony_ci goto exit; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci rval = si476x_core_cmd_func_info(core, &info); 62962306a36Sopenharmony_ci if (rval < 0) 63062306a36Sopenharmony_ci goto power_down; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci core->revision = si476x_core_fwver_to_revision(core, info.func, 63362306a36Sopenharmony_ci info.firmware.major, 63462306a36Sopenharmony_ci info.firmware.minor[0], 63562306a36Sopenharmony_ci info.firmware.minor[1]); 63662306a36Sopenharmony_cipower_down: 63762306a36Sopenharmony_ci si476x_core_set_power_state(core, SI476X_POWER_DOWN); 63862306a36Sopenharmony_ciexit: 63962306a36Sopenharmony_ci si476x_core_unlock(core); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci return rval; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cibool si476x_core_has_am(struct si476x_core *core) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci return core->chip_id == SI476X_CHIP_SI4761 || 64762306a36Sopenharmony_ci core->chip_id == SI476X_CHIP_SI4764; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_has_am); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cibool si476x_core_has_diversity(struct si476x_core *core) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci return core->chip_id == SI476X_CHIP_SI4764; 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_has_diversity); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cibool si476x_core_is_a_secondary_tuner(struct si476x_core *core) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci return si476x_core_has_diversity(core) && 66062306a36Sopenharmony_ci (core->diversity_mode == SI476X_PHDIV_SECONDARY_ANTENNA || 66162306a36Sopenharmony_ci core->diversity_mode == SI476X_PHDIV_SECONDARY_COMBINING); 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_is_a_secondary_tuner); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cibool si476x_core_is_a_primary_tuner(struct si476x_core *core) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci return si476x_core_has_diversity(core) && 66862306a36Sopenharmony_ci (core->diversity_mode == SI476X_PHDIV_PRIMARY_ANTENNA || 66962306a36Sopenharmony_ci core->diversity_mode == SI476X_PHDIV_PRIMARY_COMBINING); 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_is_a_primary_tuner); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cibool si476x_core_is_in_am_receiver_mode(struct si476x_core *core) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci return si476x_core_has_am(core) && 67662306a36Sopenharmony_ci (core->power_up_parameters.func == SI476X_FUNC_AM_RECEIVER); 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_is_in_am_receiver_mode); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cibool si476x_core_is_powered_up(struct si476x_core *core) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci return core->power_state == SI476X_POWER_UP_FULL; 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_is_powered_up); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cistatic int si476x_core_probe(struct i2c_client *client) 68762306a36Sopenharmony_ci{ 68862306a36Sopenharmony_ci const struct i2c_device_id *id = i2c_client_get_device_id(client); 68962306a36Sopenharmony_ci int rval; 69062306a36Sopenharmony_ci struct si476x_core *core; 69162306a36Sopenharmony_ci struct si476x_platform_data *pdata; 69262306a36Sopenharmony_ci struct mfd_cell *cell; 69362306a36Sopenharmony_ci int cell_num; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci core = devm_kzalloc(&client->dev, sizeof(*core), GFP_KERNEL); 69662306a36Sopenharmony_ci if (!core) 69762306a36Sopenharmony_ci return -ENOMEM; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci core->client = client; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci core->regmap = devm_regmap_init_si476x(core); 70262306a36Sopenharmony_ci if (IS_ERR(core->regmap)) { 70362306a36Sopenharmony_ci rval = PTR_ERR(core->regmap); 70462306a36Sopenharmony_ci dev_err(&client->dev, 70562306a36Sopenharmony_ci "Failed to allocate register map: %d\n", 70662306a36Sopenharmony_ci rval); 70762306a36Sopenharmony_ci return rval; 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci i2c_set_clientdata(client, core); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci atomic_set(&core->is_alive, 0); 71362306a36Sopenharmony_ci core->power_state = SI476X_POWER_DOWN; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci pdata = dev_get_platdata(&client->dev); 71662306a36Sopenharmony_ci if (pdata) { 71762306a36Sopenharmony_ci memcpy(&core->power_up_parameters, 71862306a36Sopenharmony_ci &pdata->power_up_parameters, 71962306a36Sopenharmony_ci sizeof(core->power_up_parameters)); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci core->gpio_reset = -1; 72262306a36Sopenharmony_ci if (gpio_is_valid(pdata->gpio_reset)) { 72362306a36Sopenharmony_ci rval = gpio_request(pdata->gpio_reset, "si476x reset"); 72462306a36Sopenharmony_ci if (rval) { 72562306a36Sopenharmony_ci dev_err(&client->dev, 72662306a36Sopenharmony_ci "Failed to request gpio: %d\n", rval); 72762306a36Sopenharmony_ci return rval; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci core->gpio_reset = pdata->gpio_reset; 73062306a36Sopenharmony_ci gpio_direction_output(core->gpio_reset, 0); 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci core->diversity_mode = pdata->diversity_mode; 73462306a36Sopenharmony_ci memcpy(&core->pinmux, &pdata->pinmux, 73562306a36Sopenharmony_ci sizeof(struct si476x_pinmux)); 73662306a36Sopenharmony_ci } else { 73762306a36Sopenharmony_ci dev_err(&client->dev, "No platform data provided\n"); 73862306a36Sopenharmony_ci return -EINVAL; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci core->supplies[0].supply = "vd"; 74262306a36Sopenharmony_ci core->supplies[1].supply = "va"; 74362306a36Sopenharmony_ci core->supplies[2].supply = "vio1"; 74462306a36Sopenharmony_ci core->supplies[3].supply = "vio2"; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci rval = devm_regulator_bulk_get(&client->dev, 74762306a36Sopenharmony_ci ARRAY_SIZE(core->supplies), 74862306a36Sopenharmony_ci core->supplies); 74962306a36Sopenharmony_ci if (rval) { 75062306a36Sopenharmony_ci dev_err(&client->dev, "Failed to get all of the regulators\n"); 75162306a36Sopenharmony_ci goto free_gpio; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci mutex_init(&core->cmd_lock); 75562306a36Sopenharmony_ci init_waitqueue_head(&core->command); 75662306a36Sopenharmony_ci init_waitqueue_head(&core->tuning); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci rval = kfifo_alloc(&core->rds_fifo, 75962306a36Sopenharmony_ci SI476X_DRIVER_RDS_FIFO_DEPTH * 76062306a36Sopenharmony_ci sizeof(struct v4l2_rds_data), 76162306a36Sopenharmony_ci GFP_KERNEL); 76262306a36Sopenharmony_ci if (rval) { 76362306a36Sopenharmony_ci dev_err(&client->dev, "Could not allocate the FIFO\n"); 76462306a36Sopenharmony_ci goto free_gpio; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci mutex_init(&core->rds_drainer_status_lock); 76762306a36Sopenharmony_ci init_waitqueue_head(&core->rds_read_queue); 76862306a36Sopenharmony_ci INIT_WORK(&core->rds_fifo_drainer, si476x_core_drain_rds_fifo); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci if (client->irq) { 77162306a36Sopenharmony_ci rval = devm_request_threaded_irq(&client->dev, 77262306a36Sopenharmony_ci client->irq, NULL, 77362306a36Sopenharmony_ci si476x_core_interrupt, 77462306a36Sopenharmony_ci IRQF_TRIGGER_FALLING | 77562306a36Sopenharmony_ci IRQF_ONESHOT, 77662306a36Sopenharmony_ci client->name, core); 77762306a36Sopenharmony_ci if (rval < 0) { 77862306a36Sopenharmony_ci dev_err(&client->dev, "Could not request IRQ %d\n", 77962306a36Sopenharmony_ci client->irq); 78062306a36Sopenharmony_ci goto free_kfifo; 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci disable_irq(client->irq); 78362306a36Sopenharmony_ci dev_dbg(&client->dev, "IRQ requested.\n"); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci core->rds_fifo_depth = 20; 78662306a36Sopenharmony_ci } else { 78762306a36Sopenharmony_ci INIT_DELAYED_WORK(&core->status_monitor, 78862306a36Sopenharmony_ci si476x_core_poll_loop); 78962306a36Sopenharmony_ci dev_info(&client->dev, 79062306a36Sopenharmony_ci "No IRQ number specified, will use polling\n"); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci core->rds_fifo_depth = 5; 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci core->chip_id = id->driver_data; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci rval = si476x_core_get_revision_info(core); 79862306a36Sopenharmony_ci if (rval < 0) { 79962306a36Sopenharmony_ci rval = -ENODEV; 80062306a36Sopenharmony_ci goto free_kfifo; 80162306a36Sopenharmony_ci } 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci cell_num = 0; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci cell = &core->cells[SI476X_RADIO_CELL]; 80662306a36Sopenharmony_ci cell->name = "si476x-radio"; 80762306a36Sopenharmony_ci cell_num++; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci#ifdef CONFIG_SND_SOC_SI476X 81062306a36Sopenharmony_ci if ((core->chip_id == SI476X_CHIP_SI4761 || 81162306a36Sopenharmony_ci core->chip_id == SI476X_CHIP_SI4764) && 81262306a36Sopenharmony_ci core->pinmux.dclk == SI476X_DCLK_DAUDIO && 81362306a36Sopenharmony_ci core->pinmux.dfs == SI476X_DFS_DAUDIO && 81462306a36Sopenharmony_ci core->pinmux.dout == SI476X_DOUT_I2S_OUTPUT && 81562306a36Sopenharmony_ci core->pinmux.xout == SI476X_XOUT_TRISTATE) { 81662306a36Sopenharmony_ci cell = &core->cells[SI476X_CODEC_CELL]; 81762306a36Sopenharmony_ci cell->name = "si476x-codec"; 81862306a36Sopenharmony_ci cell_num++; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci#endif 82162306a36Sopenharmony_ci rval = mfd_add_devices(&client->dev, 82262306a36Sopenharmony_ci (client->adapter->nr << 8) + client->addr, 82362306a36Sopenharmony_ci core->cells, cell_num, 82462306a36Sopenharmony_ci NULL, 0, NULL); 82562306a36Sopenharmony_ci if (!rval) 82662306a36Sopenharmony_ci return 0; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cifree_kfifo: 82962306a36Sopenharmony_ci kfifo_free(&core->rds_fifo); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_cifree_gpio: 83262306a36Sopenharmony_ci if (gpio_is_valid(core->gpio_reset)) 83362306a36Sopenharmony_ci gpio_free(core->gpio_reset); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci return rval; 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_cistatic void si476x_core_remove(struct i2c_client *client) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci struct si476x_core *core = i2c_get_clientdata(client); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci si476x_core_pronounce_dead(core); 84362306a36Sopenharmony_ci mfd_remove_devices(&client->dev); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (client->irq) 84662306a36Sopenharmony_ci disable_irq(client->irq); 84762306a36Sopenharmony_ci else 84862306a36Sopenharmony_ci cancel_delayed_work_sync(&core->status_monitor); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci kfifo_free(&core->rds_fifo); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci if (gpio_is_valid(core->gpio_reset)) 85362306a36Sopenharmony_ci gpio_free(core->gpio_reset); 85462306a36Sopenharmony_ci} 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cistatic const struct i2c_device_id si476x_id[] = { 85862306a36Sopenharmony_ci { "si4761", SI476X_CHIP_SI4761 }, 85962306a36Sopenharmony_ci { "si4764", SI476X_CHIP_SI4764 }, 86062306a36Sopenharmony_ci { "si4768", SI476X_CHIP_SI4768 }, 86162306a36Sopenharmony_ci { }, 86262306a36Sopenharmony_ci}; 86362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, si476x_id); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic struct i2c_driver si476x_core_driver = { 86662306a36Sopenharmony_ci .driver = { 86762306a36Sopenharmony_ci .name = "si476x-core", 86862306a36Sopenharmony_ci }, 86962306a36Sopenharmony_ci .probe = si476x_core_probe, 87062306a36Sopenharmony_ci .remove = si476x_core_remove, 87162306a36Sopenharmony_ci .id_table = si476x_id, 87262306a36Sopenharmony_ci}; 87362306a36Sopenharmony_cimodule_i2c_driver(si476x_core_driver); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ciMODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>"); 87762306a36Sopenharmony_ciMODULE_DESCRIPTION("Si4761/64/68 AM/FM MFD core device driver"); 87862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 879