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