18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * drivers/mfd/si476x-i2c.c -- Core device driver for si476x MFD
48c2ecf20Sopenharmony_ci * device
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2012 Innovative Converged Devices(ICD)
78c2ecf20Sopenharmony_ci * Copyright (C) 2013 Andrey Smirnov
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
158c2ecf20Sopenharmony_ci#include <linux/delay.h>
168c2ecf20Sopenharmony_ci#include <linux/gpio.h>
178c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
188c2ecf20Sopenharmony_ci#include <linux/i2c.h>
198c2ecf20Sopenharmony_ci#include <linux/err.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <linux/mfd/si476x-core.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define SI476X_MAX_IO_ERRORS		10
248c2ecf20Sopenharmony_ci#define SI476X_DRIVER_RDS_FIFO_DEPTH	128
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/**
278c2ecf20Sopenharmony_ci * si476x_core_config_pinmux() - pin function configuration function
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci * @core: Core device structure
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * Configure the functions of the pins of the radio chip.
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci * The function returns zero in case of succes or negative error code
348c2ecf20Sopenharmony_ci * otherwise.
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_cistatic int si476x_core_config_pinmux(struct si476x_core *core)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	int err;
398c2ecf20Sopenharmony_ci	dev_dbg(&core->client->dev, "Configuring pinmux\n");
408c2ecf20Sopenharmony_ci	err = si476x_core_cmd_dig_audio_pin_cfg(core,
418c2ecf20Sopenharmony_ci						core->pinmux.dclk,
428c2ecf20Sopenharmony_ci						core->pinmux.dfs,
438c2ecf20Sopenharmony_ci						core->pinmux.dout,
448c2ecf20Sopenharmony_ci						core->pinmux.xout);
458c2ecf20Sopenharmony_ci	if (err < 0) {
468c2ecf20Sopenharmony_ci		dev_err(&core->client->dev,
478c2ecf20Sopenharmony_ci			"Failed to configure digital audio pins(err = %d)\n",
488c2ecf20Sopenharmony_ci			err);
498c2ecf20Sopenharmony_ci		return err;
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	err = si476x_core_cmd_zif_pin_cfg(core,
538c2ecf20Sopenharmony_ci					  core->pinmux.iqclk,
548c2ecf20Sopenharmony_ci					  core->pinmux.iqfs,
558c2ecf20Sopenharmony_ci					  core->pinmux.iout,
568c2ecf20Sopenharmony_ci					  core->pinmux.qout);
578c2ecf20Sopenharmony_ci	if (err < 0) {
588c2ecf20Sopenharmony_ci		dev_err(&core->client->dev,
598c2ecf20Sopenharmony_ci			"Failed to configure ZIF pins(err = %d)\n",
608c2ecf20Sopenharmony_ci			err);
618c2ecf20Sopenharmony_ci		return err;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	err = si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(core,
658c2ecf20Sopenharmony_ci						      core->pinmux.icin,
668c2ecf20Sopenharmony_ci						      core->pinmux.icip,
678c2ecf20Sopenharmony_ci						      core->pinmux.icon,
688c2ecf20Sopenharmony_ci						      core->pinmux.icop);
698c2ecf20Sopenharmony_ci	if (err < 0) {
708c2ecf20Sopenharmony_ci		dev_err(&core->client->dev,
718c2ecf20Sopenharmony_ci			"Failed to configure IC-Link/GPO pins(err = %d)\n",
728c2ecf20Sopenharmony_ci			err);
738c2ecf20Sopenharmony_ci		return err;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	err = si476x_core_cmd_ana_audio_pin_cfg(core,
778c2ecf20Sopenharmony_ci						core->pinmux.lrout);
788c2ecf20Sopenharmony_ci	if (err < 0) {
798c2ecf20Sopenharmony_ci		dev_err(&core->client->dev,
808c2ecf20Sopenharmony_ci			"Failed to configure analog audio pins(err = %d)\n",
818c2ecf20Sopenharmony_ci			err);
828c2ecf20Sopenharmony_ci		return err;
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	err = si476x_core_cmd_intb_pin_cfg(core,
868c2ecf20Sopenharmony_ci					   core->pinmux.intb,
878c2ecf20Sopenharmony_ci					   core->pinmux.a1);
888c2ecf20Sopenharmony_ci	if (err < 0) {
898c2ecf20Sopenharmony_ci		dev_err(&core->client->dev,
908c2ecf20Sopenharmony_ci			"Failed to configure interrupt pins(err = %d)\n",
918c2ecf20Sopenharmony_ci			err);
928c2ecf20Sopenharmony_ci		return err;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	return 0;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic inline void si476x_core_schedule_polling_work(struct si476x_core *core)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	schedule_delayed_work(&core->status_monitor,
1018c2ecf20Sopenharmony_ci			      usecs_to_jiffies(SI476X_STATUS_POLL_US));
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/**
1058c2ecf20Sopenharmony_ci * si476x_core_start() - early chip startup function
1068c2ecf20Sopenharmony_ci * @core: Core device structure
1078c2ecf20Sopenharmony_ci * @soft: When set, this flag forces "soft" startup, where "soft"
1088c2ecf20Sopenharmony_ci * power down is the one done by sending appropriate command instead
1098c2ecf20Sopenharmony_ci * of using reset pin of the tuner
1108c2ecf20Sopenharmony_ci *
1118c2ecf20Sopenharmony_ci * Perform required startup sequence to correctly power
1128c2ecf20Sopenharmony_ci * up the chip and perform initial configuration. It does the
1138c2ecf20Sopenharmony_ci * following sequence of actions:
1148c2ecf20Sopenharmony_ci *       1. Claims and enables the power supplies VD and VIO1 required
1158c2ecf20Sopenharmony_ci *          for I2C interface of the chip operation.
1168c2ecf20Sopenharmony_ci *       2. Waits for 100us, pulls the reset line up, enables irq,
1178c2ecf20Sopenharmony_ci *          waits for another 100us as it is specified by the
1188c2ecf20Sopenharmony_ci *          datasheet.
1198c2ecf20Sopenharmony_ci *       3. Sends 'POWER_UP' command to the device with all provided
1208c2ecf20Sopenharmony_ci *          information about power-up parameters.
1218c2ecf20Sopenharmony_ci *       4. Configures, pin multiplexor, disables digital audio and
1228c2ecf20Sopenharmony_ci *          configures interrupt sources.
1238c2ecf20Sopenharmony_ci *
1248c2ecf20Sopenharmony_ci * The function returns zero in case of succes or negative error code
1258c2ecf20Sopenharmony_ci * otherwise.
1268c2ecf20Sopenharmony_ci */
1278c2ecf20Sopenharmony_ciint si476x_core_start(struct si476x_core *core, bool soft)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	struct i2c_client *client = core->client;
1308c2ecf20Sopenharmony_ci	int err;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	if (!soft) {
1338c2ecf20Sopenharmony_ci		if (gpio_is_valid(core->gpio_reset))
1348c2ecf20Sopenharmony_ci			gpio_set_value_cansleep(core->gpio_reset, 1);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci		if (client->irq)
1378c2ecf20Sopenharmony_ci			enable_irq(client->irq);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci		udelay(100);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci		if (!client->irq) {
1428c2ecf20Sopenharmony_ci			atomic_set(&core->is_alive, 1);
1438c2ecf20Sopenharmony_ci			si476x_core_schedule_polling_work(core);
1448c2ecf20Sopenharmony_ci		}
1458c2ecf20Sopenharmony_ci	} else {
1468c2ecf20Sopenharmony_ci		if (client->irq)
1478c2ecf20Sopenharmony_ci			enable_irq(client->irq);
1488c2ecf20Sopenharmony_ci		else {
1498c2ecf20Sopenharmony_ci			atomic_set(&core->is_alive, 1);
1508c2ecf20Sopenharmony_ci			si476x_core_schedule_polling_work(core);
1518c2ecf20Sopenharmony_ci		}
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	err = si476x_core_cmd_power_up(core,
1558c2ecf20Sopenharmony_ci				       &core->power_up_parameters);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (err < 0) {
1588c2ecf20Sopenharmony_ci		dev_err(&core->client->dev,
1598c2ecf20Sopenharmony_ci			"Power up failure(err = %d)\n",
1608c2ecf20Sopenharmony_ci			err);
1618c2ecf20Sopenharmony_ci		goto disable_irq;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (client->irq)
1658c2ecf20Sopenharmony_ci		atomic_set(&core->is_alive, 1);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	err = si476x_core_config_pinmux(core);
1688c2ecf20Sopenharmony_ci	if (err < 0) {
1698c2ecf20Sopenharmony_ci		dev_err(&core->client->dev,
1708c2ecf20Sopenharmony_ci			"Failed to configure pinmux(err = %d)\n",
1718c2ecf20Sopenharmony_ci			err);
1728c2ecf20Sopenharmony_ci		goto disable_irq;
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (client->irq) {
1768c2ecf20Sopenharmony_ci		err = regmap_write(core->regmap,
1778c2ecf20Sopenharmony_ci				   SI476X_PROP_INT_CTL_ENABLE,
1788c2ecf20Sopenharmony_ci				   SI476X_RDSIEN |
1798c2ecf20Sopenharmony_ci				   SI476X_STCIEN |
1808c2ecf20Sopenharmony_ci				   SI476X_CTSIEN);
1818c2ecf20Sopenharmony_ci		if (err < 0) {
1828c2ecf20Sopenharmony_ci			dev_err(&core->client->dev,
1838c2ecf20Sopenharmony_ci				"Failed to configure interrupt sources"
1848c2ecf20Sopenharmony_ci				"(err = %d)\n", err);
1858c2ecf20Sopenharmony_ci			goto disable_irq;
1868c2ecf20Sopenharmony_ci		}
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	return 0;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cidisable_irq:
1928c2ecf20Sopenharmony_ci	if (err == -ENODEV)
1938c2ecf20Sopenharmony_ci		atomic_set(&core->is_alive, 0);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	if (client->irq)
1968c2ecf20Sopenharmony_ci		disable_irq(client->irq);
1978c2ecf20Sopenharmony_ci	else
1988c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&core->status_monitor);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	if (gpio_is_valid(core->gpio_reset))
2018c2ecf20Sopenharmony_ci		gpio_set_value_cansleep(core->gpio_reset, 0);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	return err;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_start);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci/**
2088c2ecf20Sopenharmony_ci * si476x_core_stop() - chip power-down function
2098c2ecf20Sopenharmony_ci * @core: Core device structure
2108c2ecf20Sopenharmony_ci * @soft: When set, function sends a POWER_DOWN command instead of
2118c2ecf20Sopenharmony_ci * bringing reset line low
2128c2ecf20Sopenharmony_ci *
2138c2ecf20Sopenharmony_ci * Power down the chip by performing following actions:
2148c2ecf20Sopenharmony_ci * 1. Disable IRQ or stop the polling worker
2158c2ecf20Sopenharmony_ci * 2. Send the POWER_DOWN command if the power down is soft or bring
2168c2ecf20Sopenharmony_ci *    reset line low if not.
2178c2ecf20Sopenharmony_ci *
2188c2ecf20Sopenharmony_ci * The function returns zero in case of succes or negative error code
2198c2ecf20Sopenharmony_ci * otherwise.
2208c2ecf20Sopenharmony_ci */
2218c2ecf20Sopenharmony_ciint si476x_core_stop(struct si476x_core *core, bool soft)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	int err = 0;
2248c2ecf20Sopenharmony_ci	atomic_set(&core->is_alive, 0);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	if (soft) {
2278c2ecf20Sopenharmony_ci		/* TODO: This probably shoud be a configurable option,
2288c2ecf20Sopenharmony_ci		 * so it is possible to have the chips keep their
2298c2ecf20Sopenharmony_ci		 * oscillators running
2308c2ecf20Sopenharmony_ci		 */
2318c2ecf20Sopenharmony_ci		struct si476x_power_down_args args = {
2328c2ecf20Sopenharmony_ci			.xosc = false,
2338c2ecf20Sopenharmony_ci		};
2348c2ecf20Sopenharmony_ci		err = si476x_core_cmd_power_down(core, &args);
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	/* We couldn't disable those before
2388c2ecf20Sopenharmony_ci	 * 'si476x_core_cmd_power_down' since we expect to get CTS
2398c2ecf20Sopenharmony_ci	 * interrupt */
2408c2ecf20Sopenharmony_ci	if (core->client->irq)
2418c2ecf20Sopenharmony_ci		disable_irq(core->client->irq);
2428c2ecf20Sopenharmony_ci	else
2438c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&core->status_monitor);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (!soft) {
2468c2ecf20Sopenharmony_ci		if (gpio_is_valid(core->gpio_reset))
2478c2ecf20Sopenharmony_ci			gpio_set_value_cansleep(core->gpio_reset, 0);
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci	return err;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_stop);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci/**
2548c2ecf20Sopenharmony_ci * si476x_core_set_power_state() - set the level at which the power is
2558c2ecf20Sopenharmony_ci * supplied for the chip.
2568c2ecf20Sopenharmony_ci * @core: Core device structure
2578c2ecf20Sopenharmony_ci * @next_state: enum si476x_power_state describing power state to
2588c2ecf20Sopenharmony_ci *              switch to.
2598c2ecf20Sopenharmony_ci *
2608c2ecf20Sopenharmony_ci * Switch on all the required power supplies
2618c2ecf20Sopenharmony_ci *
2628c2ecf20Sopenharmony_ci * This function returns 0 in case of suvccess and negative error code
2638c2ecf20Sopenharmony_ci * otherwise.
2648c2ecf20Sopenharmony_ci */
2658c2ecf20Sopenharmony_ciint si476x_core_set_power_state(struct si476x_core *core,
2668c2ecf20Sopenharmony_ci				enum si476x_power_state next_state)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	/*
2698c2ecf20Sopenharmony_ci	   It is not clear form the datasheet if it is possible to
2708c2ecf20Sopenharmony_ci	   work with device if not all power domains are operational.
2718c2ecf20Sopenharmony_ci	   So for now the power-up policy is "power-up all the things!"
2728c2ecf20Sopenharmony_ci	 */
2738c2ecf20Sopenharmony_ci	int err = 0;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (core->power_state == SI476X_POWER_INCONSISTENT) {
2768c2ecf20Sopenharmony_ci		dev_err(&core->client->dev,
2778c2ecf20Sopenharmony_ci			"The device in inconsistent power state\n");
2788c2ecf20Sopenharmony_ci		return -EINVAL;
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	if (next_state != core->power_state) {
2828c2ecf20Sopenharmony_ci		switch (next_state) {
2838c2ecf20Sopenharmony_ci		case SI476X_POWER_UP_FULL:
2848c2ecf20Sopenharmony_ci			err = regulator_bulk_enable(ARRAY_SIZE(core->supplies),
2858c2ecf20Sopenharmony_ci						    core->supplies);
2868c2ecf20Sopenharmony_ci			if (err < 0) {
2878c2ecf20Sopenharmony_ci				core->power_state = SI476X_POWER_INCONSISTENT;
2888c2ecf20Sopenharmony_ci				break;
2898c2ecf20Sopenharmony_ci			}
2908c2ecf20Sopenharmony_ci			/*
2918c2ecf20Sopenharmony_ci			 * Startup timing diagram recommends to have a
2928c2ecf20Sopenharmony_ci			 * 100 us delay between enabling of the power
2938c2ecf20Sopenharmony_ci			 * supplies and turning the tuner on.
2948c2ecf20Sopenharmony_ci			 */
2958c2ecf20Sopenharmony_ci			udelay(100);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci			err = si476x_core_start(core, false);
2988c2ecf20Sopenharmony_ci			if (err < 0)
2998c2ecf20Sopenharmony_ci				goto disable_regulators;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci			core->power_state = next_state;
3028c2ecf20Sopenharmony_ci			break;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci		case SI476X_POWER_DOWN:
3058c2ecf20Sopenharmony_ci			core->power_state = next_state;
3068c2ecf20Sopenharmony_ci			err = si476x_core_stop(core, false);
3078c2ecf20Sopenharmony_ci			if (err < 0)
3088c2ecf20Sopenharmony_ci				core->power_state = SI476X_POWER_INCONSISTENT;
3098c2ecf20Sopenharmony_cidisable_regulators:
3108c2ecf20Sopenharmony_ci			err = regulator_bulk_disable(ARRAY_SIZE(core->supplies),
3118c2ecf20Sopenharmony_ci						     core->supplies);
3128c2ecf20Sopenharmony_ci			if (err < 0)
3138c2ecf20Sopenharmony_ci				core->power_state = SI476X_POWER_INCONSISTENT;
3148c2ecf20Sopenharmony_ci			break;
3158c2ecf20Sopenharmony_ci		default:
3168c2ecf20Sopenharmony_ci			BUG();
3178c2ecf20Sopenharmony_ci		}
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	return err;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_set_power_state);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci/**
3258c2ecf20Sopenharmony_ci * si476x_core_report_drainer_stop() - mark the completion of the RDS
3268c2ecf20Sopenharmony_ci * buffer drain porcess by the worker.
3278c2ecf20Sopenharmony_ci *
3288c2ecf20Sopenharmony_ci * @core: Core device structure
3298c2ecf20Sopenharmony_ci */
3308c2ecf20Sopenharmony_cistatic inline void si476x_core_report_drainer_stop(struct si476x_core *core)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	mutex_lock(&core->rds_drainer_status_lock);
3338c2ecf20Sopenharmony_ci	core->rds_drainer_is_working = false;
3348c2ecf20Sopenharmony_ci	mutex_unlock(&core->rds_drainer_status_lock);
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci/**
3388c2ecf20Sopenharmony_ci * si476x_core_start_rds_drainer_once() - start RDS drainer worker if
3398c2ecf20Sopenharmony_ci * ther is none working, do nothing otherwise
3408c2ecf20Sopenharmony_ci *
3418c2ecf20Sopenharmony_ci * @core: Datastructure corresponding to the chip.
3428c2ecf20Sopenharmony_ci */
3438c2ecf20Sopenharmony_cistatic inline void si476x_core_start_rds_drainer_once(struct si476x_core *core)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	mutex_lock(&core->rds_drainer_status_lock);
3468c2ecf20Sopenharmony_ci	if (!core->rds_drainer_is_working) {
3478c2ecf20Sopenharmony_ci		core->rds_drainer_is_working = true;
3488c2ecf20Sopenharmony_ci		schedule_work(&core->rds_fifo_drainer);
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci	mutex_unlock(&core->rds_drainer_status_lock);
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci/**
3538c2ecf20Sopenharmony_ci * si476x_drain_rds_fifo() - RDS buffer drainer.
3548c2ecf20Sopenharmony_ci * @work: struct work_struct being ppassed to the function by the
3558c2ecf20Sopenharmony_ci * kernel.
3568c2ecf20Sopenharmony_ci *
3578c2ecf20Sopenharmony_ci * Drain the contents of the RDS FIFO of
3588c2ecf20Sopenharmony_ci */
3598c2ecf20Sopenharmony_cistatic void si476x_core_drain_rds_fifo(struct work_struct *work)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	int err;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	struct si476x_core *core = container_of(work, struct si476x_core,
3648c2ecf20Sopenharmony_ci						rds_fifo_drainer);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	struct si476x_rds_status_report report;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	si476x_core_lock(core);
3698c2ecf20Sopenharmony_ci	err = si476x_core_cmd_fm_rds_status(core, true, false, false, &report);
3708c2ecf20Sopenharmony_ci	if (!err) {
3718c2ecf20Sopenharmony_ci		int i = report.rdsfifoused;
3728c2ecf20Sopenharmony_ci		dev_dbg(&core->client->dev,
3738c2ecf20Sopenharmony_ci			"%d elements in RDS FIFO. Draining.\n", i);
3748c2ecf20Sopenharmony_ci		for (; i > 0; --i) {
3758c2ecf20Sopenharmony_ci			err = si476x_core_cmd_fm_rds_status(core, false, false,
3768c2ecf20Sopenharmony_ci							    (i == 1), &report);
3778c2ecf20Sopenharmony_ci			if (err < 0)
3788c2ecf20Sopenharmony_ci				goto unlock;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci			kfifo_in(&core->rds_fifo, report.rds,
3818c2ecf20Sopenharmony_ci				 sizeof(report.rds));
3828c2ecf20Sopenharmony_ci			dev_dbg(&core->client->dev, "RDS data:\n %*ph\n",
3838c2ecf20Sopenharmony_ci				(int)sizeof(report.rds), report.rds);
3848c2ecf20Sopenharmony_ci		}
3858c2ecf20Sopenharmony_ci		dev_dbg(&core->client->dev, "Drrrrained!\n");
3868c2ecf20Sopenharmony_ci		wake_up_interruptible(&core->rds_read_queue);
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ciunlock:
3908c2ecf20Sopenharmony_ci	si476x_core_unlock(core);
3918c2ecf20Sopenharmony_ci	si476x_core_report_drainer_stop(core);
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci/**
3958c2ecf20Sopenharmony_ci * si476x_core_pronounce_dead()
3968c2ecf20Sopenharmony_ci *
3978c2ecf20Sopenharmony_ci * @core: Core device structure
3988c2ecf20Sopenharmony_ci *
3998c2ecf20Sopenharmony_ci * Mark the device as being dead and wake up all potentially waiting
4008c2ecf20Sopenharmony_ci * threads of execution.
4018c2ecf20Sopenharmony_ci *
4028c2ecf20Sopenharmony_ci */
4038c2ecf20Sopenharmony_cistatic void si476x_core_pronounce_dead(struct si476x_core *core)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	dev_info(&core->client->dev, "Core device is dead.\n");
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	atomic_set(&core->is_alive, 0);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	/* Wake up al possible waiting processes */
4108c2ecf20Sopenharmony_ci	wake_up_interruptible(&core->rds_read_queue);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	atomic_set(&core->cts, 1);
4138c2ecf20Sopenharmony_ci	wake_up(&core->command);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	atomic_set(&core->stc, 1);
4168c2ecf20Sopenharmony_ci	wake_up(&core->tuning);
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci/**
4208c2ecf20Sopenharmony_ci * si476x_core_i2c_xfer()
4218c2ecf20Sopenharmony_ci *
4228c2ecf20Sopenharmony_ci * @core: Core device structure
4238c2ecf20Sopenharmony_ci * @type: Transfer type
4248c2ecf20Sopenharmony_ci * @buf: Transfer buffer for/with data
4258c2ecf20Sopenharmony_ci * @count: Transfer buffer size
4268c2ecf20Sopenharmony_ci *
4278c2ecf20Sopenharmony_ci * Perfrom and I2C transfer(either read or write) and keep a counter
4288c2ecf20Sopenharmony_ci * of I/O errors. If the error counter rises above the threshold
4298c2ecf20Sopenharmony_ci * pronounce device dead.
4308c2ecf20Sopenharmony_ci *
4318c2ecf20Sopenharmony_ci * The function returns zero on succes or negative error code on
4328c2ecf20Sopenharmony_ci * failure.
4338c2ecf20Sopenharmony_ci */
4348c2ecf20Sopenharmony_ciint si476x_core_i2c_xfer(struct si476x_core *core,
4358c2ecf20Sopenharmony_ci		    enum si476x_i2c_type type,
4368c2ecf20Sopenharmony_ci		    char *buf, int count)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	static int io_errors_count;
4398c2ecf20Sopenharmony_ci	int err;
4408c2ecf20Sopenharmony_ci	if (type == SI476X_I2C_SEND)
4418c2ecf20Sopenharmony_ci		err = i2c_master_send(core->client, buf, count);
4428c2ecf20Sopenharmony_ci	else
4438c2ecf20Sopenharmony_ci		err = i2c_master_recv(core->client, buf, count);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	if (err < 0) {
4468c2ecf20Sopenharmony_ci		if (io_errors_count++ > SI476X_MAX_IO_ERRORS)
4478c2ecf20Sopenharmony_ci			si476x_core_pronounce_dead(core);
4488c2ecf20Sopenharmony_ci	} else {
4498c2ecf20Sopenharmony_ci		io_errors_count = 0;
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	return err;
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_i2c_xfer);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci/**
4578c2ecf20Sopenharmony_ci * si476x_get_status()
4588c2ecf20Sopenharmony_ci * @core: Core device structure
4598c2ecf20Sopenharmony_ci *
4608c2ecf20Sopenharmony_ci * Get the status byte of the core device by berforming one byte I2C
4618c2ecf20Sopenharmony_ci * read.
4628c2ecf20Sopenharmony_ci *
4638c2ecf20Sopenharmony_ci * The function returns a status value or a negative error code on
4648c2ecf20Sopenharmony_ci * error.
4658c2ecf20Sopenharmony_ci */
4668c2ecf20Sopenharmony_cistatic int si476x_core_get_status(struct si476x_core *core)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	u8 response;
4698c2ecf20Sopenharmony_ci	int err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV,
4708c2ecf20Sopenharmony_ci				  &response, sizeof(response));
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	return (err < 0) ? err : response;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci/**
4768c2ecf20Sopenharmony_ci * si476x_get_and_signal_status() - IRQ dispatcher
4778c2ecf20Sopenharmony_ci * @core: Core device structure
4788c2ecf20Sopenharmony_ci *
4798c2ecf20Sopenharmony_ci * Dispatch the arrived interrupt request based on the value of the
4808c2ecf20Sopenharmony_ci * status byte reported by the tuner.
4818c2ecf20Sopenharmony_ci *
4828c2ecf20Sopenharmony_ci */
4838c2ecf20Sopenharmony_cistatic void si476x_core_get_and_signal_status(struct si476x_core *core)
4848c2ecf20Sopenharmony_ci{
4858c2ecf20Sopenharmony_ci	int status = si476x_core_get_status(core);
4868c2ecf20Sopenharmony_ci	if (status < 0) {
4878c2ecf20Sopenharmony_ci		dev_err(&core->client->dev, "Failed to get status\n");
4888c2ecf20Sopenharmony_ci		return;
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (status & SI476X_CTS) {
4928c2ecf20Sopenharmony_ci		/* Unfortunately completions could not be used for
4938c2ecf20Sopenharmony_ci		 * signalling CTS since this flag cannot be cleared
4948c2ecf20Sopenharmony_ci		 * in status byte, and therefore once it becomes true
4958c2ecf20Sopenharmony_ci		 * multiple calls to 'complete' would cause the
4968c2ecf20Sopenharmony_ci		 * commands following the current one to be completed
4978c2ecf20Sopenharmony_ci		 * before they actually are */
4988c2ecf20Sopenharmony_ci		dev_dbg(&core->client->dev, "[interrupt] CTSINT\n");
4998c2ecf20Sopenharmony_ci		atomic_set(&core->cts, 1);
5008c2ecf20Sopenharmony_ci		wake_up(&core->command);
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	if (status & SI476X_FM_RDS_INT) {
5048c2ecf20Sopenharmony_ci		dev_dbg(&core->client->dev, "[interrupt] RDSINT\n");
5058c2ecf20Sopenharmony_ci		si476x_core_start_rds_drainer_once(core);
5068c2ecf20Sopenharmony_ci	}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	if (status & SI476X_STC_INT) {
5098c2ecf20Sopenharmony_ci		dev_dbg(&core->client->dev, "[interrupt] STCINT\n");
5108c2ecf20Sopenharmony_ci		atomic_set(&core->stc, 1);
5118c2ecf20Sopenharmony_ci		wake_up(&core->tuning);
5128c2ecf20Sopenharmony_ci	}
5138c2ecf20Sopenharmony_ci}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_cistatic void si476x_core_poll_loop(struct work_struct *work)
5168c2ecf20Sopenharmony_ci{
5178c2ecf20Sopenharmony_ci	struct si476x_core *core = SI476X_WORK_TO_CORE(work);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	si476x_core_get_and_signal_status(core);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (atomic_read(&core->is_alive))
5228c2ecf20Sopenharmony_ci		si476x_core_schedule_polling_work(core);
5238c2ecf20Sopenharmony_ci}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_cistatic irqreturn_t si476x_core_interrupt(int irq, void *dev)
5268c2ecf20Sopenharmony_ci{
5278c2ecf20Sopenharmony_ci	struct si476x_core *core = dev;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	si476x_core_get_and_signal_status(core);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci/**
5358c2ecf20Sopenharmony_ci * si476x_firmware_version_to_revision()
5368c2ecf20Sopenharmony_ci * @core: Core device structure
5378c2ecf20Sopenharmony_ci * @func: Selects the boot function of the device:
5388c2ecf20Sopenharmony_ci *         *_BOOTLOADER  - Boot loader
5398c2ecf20Sopenharmony_ci *         *_FM_RECEIVER - FM receiver
5408c2ecf20Sopenharmony_ci *         *_AM_RECEIVER - AM receiver
5418c2ecf20Sopenharmony_ci *         *_WB_RECEIVER - Weatherband receiver
5428c2ecf20Sopenharmony_ci * @major:  Firmware major number
5438c2ecf20Sopenharmony_ci * @minor1: Firmware first minor number
5448c2ecf20Sopenharmony_ci * @minor2: Firmware second minor number
5458c2ecf20Sopenharmony_ci *
5468c2ecf20Sopenharmony_ci * Convert a chip's firmware version number into an offset that later
5478c2ecf20Sopenharmony_ci * will be used to as offset in "vtable" of tuner functions
5488c2ecf20Sopenharmony_ci *
5498c2ecf20Sopenharmony_ci * This function returns a positive offset in case of success and a -1
5508c2ecf20Sopenharmony_ci * in case of failure.
5518c2ecf20Sopenharmony_ci */
5528c2ecf20Sopenharmony_cistatic int si476x_core_fwver_to_revision(struct si476x_core *core,
5538c2ecf20Sopenharmony_ci					 int func, int major,
5548c2ecf20Sopenharmony_ci					 int minor1, int minor2)
5558c2ecf20Sopenharmony_ci{
5568c2ecf20Sopenharmony_ci	switch (func) {
5578c2ecf20Sopenharmony_ci	case SI476X_FUNC_FM_RECEIVER:
5588c2ecf20Sopenharmony_ci		switch (major) {
5598c2ecf20Sopenharmony_ci		case 5:
5608c2ecf20Sopenharmony_ci			return SI476X_REVISION_A10;
5618c2ecf20Sopenharmony_ci		case 8:
5628c2ecf20Sopenharmony_ci			return SI476X_REVISION_A20;
5638c2ecf20Sopenharmony_ci		case 10:
5648c2ecf20Sopenharmony_ci			return SI476X_REVISION_A30;
5658c2ecf20Sopenharmony_ci		default:
5668c2ecf20Sopenharmony_ci			goto unknown_revision;
5678c2ecf20Sopenharmony_ci		}
5688c2ecf20Sopenharmony_ci	case SI476X_FUNC_AM_RECEIVER:
5698c2ecf20Sopenharmony_ci		switch (major) {
5708c2ecf20Sopenharmony_ci		case 5:
5718c2ecf20Sopenharmony_ci			return SI476X_REVISION_A10;
5728c2ecf20Sopenharmony_ci		case 7:
5738c2ecf20Sopenharmony_ci			return SI476X_REVISION_A20;
5748c2ecf20Sopenharmony_ci		case 9:
5758c2ecf20Sopenharmony_ci			return SI476X_REVISION_A30;
5768c2ecf20Sopenharmony_ci		default:
5778c2ecf20Sopenharmony_ci			goto unknown_revision;
5788c2ecf20Sopenharmony_ci		}
5798c2ecf20Sopenharmony_ci	case SI476X_FUNC_WB_RECEIVER:
5808c2ecf20Sopenharmony_ci		switch (major) {
5818c2ecf20Sopenharmony_ci		case 3:
5828c2ecf20Sopenharmony_ci			return SI476X_REVISION_A10;
5838c2ecf20Sopenharmony_ci		case 5:
5848c2ecf20Sopenharmony_ci			return SI476X_REVISION_A20;
5858c2ecf20Sopenharmony_ci		case 7:
5868c2ecf20Sopenharmony_ci			return SI476X_REVISION_A30;
5878c2ecf20Sopenharmony_ci		default:
5888c2ecf20Sopenharmony_ci			goto unknown_revision;
5898c2ecf20Sopenharmony_ci		}
5908c2ecf20Sopenharmony_ci	case SI476X_FUNC_BOOTLOADER:
5918c2ecf20Sopenharmony_ci	default:		/* FALLTHROUGH */
5928c2ecf20Sopenharmony_ci		BUG();
5938c2ecf20Sopenharmony_ci		return -1;
5948c2ecf20Sopenharmony_ci	}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ciunknown_revision:
5978c2ecf20Sopenharmony_ci	dev_err(&core->client->dev,
5988c2ecf20Sopenharmony_ci		"Unsupported version of the firmware: %d.%d.%d, "
5998c2ecf20Sopenharmony_ci		"reverting to A10 compatible functions\n",
6008c2ecf20Sopenharmony_ci		major, minor1, minor2);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	return SI476X_REVISION_A10;
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci/**
6068c2ecf20Sopenharmony_ci * si476x_get_revision_info()
6078c2ecf20Sopenharmony_ci * @core: Core device structure
6088c2ecf20Sopenharmony_ci *
6098c2ecf20Sopenharmony_ci * Get the firmware version number of the device. It is done in
6108c2ecf20Sopenharmony_ci * following three steps:
6118c2ecf20Sopenharmony_ci *    1. Power-up the device
6128c2ecf20Sopenharmony_ci *    2. Send the 'FUNC_INFO' command
6138c2ecf20Sopenharmony_ci *    3. Powering the device down.
6148c2ecf20Sopenharmony_ci *
6158c2ecf20Sopenharmony_ci * The function return zero on success and a negative error code on
6168c2ecf20Sopenharmony_ci * failure.
6178c2ecf20Sopenharmony_ci */
6188c2ecf20Sopenharmony_cistatic int si476x_core_get_revision_info(struct si476x_core *core)
6198c2ecf20Sopenharmony_ci{
6208c2ecf20Sopenharmony_ci	int rval;
6218c2ecf20Sopenharmony_ci	struct si476x_func_info info;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	si476x_core_lock(core);
6248c2ecf20Sopenharmony_ci	rval = si476x_core_set_power_state(core, SI476X_POWER_UP_FULL);
6258c2ecf20Sopenharmony_ci	if (rval < 0)
6268c2ecf20Sopenharmony_ci		goto exit;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	rval = si476x_core_cmd_func_info(core, &info);
6298c2ecf20Sopenharmony_ci	if (rval < 0)
6308c2ecf20Sopenharmony_ci		goto power_down;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	core->revision = si476x_core_fwver_to_revision(core, info.func,
6338c2ecf20Sopenharmony_ci						       info.firmware.major,
6348c2ecf20Sopenharmony_ci						       info.firmware.minor[0],
6358c2ecf20Sopenharmony_ci						       info.firmware.minor[1]);
6368c2ecf20Sopenharmony_cipower_down:
6378c2ecf20Sopenharmony_ci	si476x_core_set_power_state(core, SI476X_POWER_DOWN);
6388c2ecf20Sopenharmony_ciexit:
6398c2ecf20Sopenharmony_ci	si476x_core_unlock(core);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	return rval;
6428c2ecf20Sopenharmony_ci}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_cibool si476x_core_has_am(struct si476x_core *core)
6458c2ecf20Sopenharmony_ci{
6468c2ecf20Sopenharmony_ci	return core->chip_id == SI476X_CHIP_SI4761 ||
6478c2ecf20Sopenharmony_ci		core->chip_id == SI476X_CHIP_SI4764;
6488c2ecf20Sopenharmony_ci}
6498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_has_am);
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_cibool si476x_core_has_diversity(struct si476x_core *core)
6528c2ecf20Sopenharmony_ci{
6538c2ecf20Sopenharmony_ci	return core->chip_id == SI476X_CHIP_SI4764;
6548c2ecf20Sopenharmony_ci}
6558c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_has_diversity);
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_cibool si476x_core_is_a_secondary_tuner(struct si476x_core *core)
6588c2ecf20Sopenharmony_ci{
6598c2ecf20Sopenharmony_ci	return si476x_core_has_diversity(core) &&
6608c2ecf20Sopenharmony_ci		(core->diversity_mode == SI476X_PHDIV_SECONDARY_ANTENNA ||
6618c2ecf20Sopenharmony_ci		 core->diversity_mode == SI476X_PHDIV_SECONDARY_COMBINING);
6628c2ecf20Sopenharmony_ci}
6638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_is_a_secondary_tuner);
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_cibool si476x_core_is_a_primary_tuner(struct si476x_core *core)
6668c2ecf20Sopenharmony_ci{
6678c2ecf20Sopenharmony_ci	return si476x_core_has_diversity(core) &&
6688c2ecf20Sopenharmony_ci		(core->diversity_mode == SI476X_PHDIV_PRIMARY_ANTENNA ||
6698c2ecf20Sopenharmony_ci		 core->diversity_mode == SI476X_PHDIV_PRIMARY_COMBINING);
6708c2ecf20Sopenharmony_ci}
6718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_is_a_primary_tuner);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_cibool si476x_core_is_in_am_receiver_mode(struct si476x_core *core)
6748c2ecf20Sopenharmony_ci{
6758c2ecf20Sopenharmony_ci	return si476x_core_has_am(core) &&
6768c2ecf20Sopenharmony_ci		(core->power_up_parameters.func == SI476X_FUNC_AM_RECEIVER);
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_is_in_am_receiver_mode);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_cibool si476x_core_is_powered_up(struct si476x_core *core)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	return core->power_state == SI476X_POWER_UP_FULL;
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(si476x_core_is_powered_up);
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_cistatic int si476x_core_probe(struct i2c_client *client,
6878c2ecf20Sopenharmony_ci			     const struct i2c_device_id *id)
6888c2ecf20Sopenharmony_ci{
6898c2ecf20Sopenharmony_ci	int rval;
6908c2ecf20Sopenharmony_ci	struct si476x_core          *core;
6918c2ecf20Sopenharmony_ci	struct si476x_platform_data *pdata;
6928c2ecf20Sopenharmony_ci	struct mfd_cell *cell;
6938c2ecf20Sopenharmony_ci	int              cell_num;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	core = devm_kzalloc(&client->dev, sizeof(*core), GFP_KERNEL);
6968c2ecf20Sopenharmony_ci	if (!core)
6978c2ecf20Sopenharmony_ci		return -ENOMEM;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	core->client = client;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	core->regmap = devm_regmap_init_si476x(core);
7028c2ecf20Sopenharmony_ci	if (IS_ERR(core->regmap)) {
7038c2ecf20Sopenharmony_ci		rval = PTR_ERR(core->regmap);
7048c2ecf20Sopenharmony_ci		dev_err(&client->dev,
7058c2ecf20Sopenharmony_ci			"Failed to allocate register map: %d\n",
7068c2ecf20Sopenharmony_ci			rval);
7078c2ecf20Sopenharmony_ci		return rval;
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, core);
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	atomic_set(&core->is_alive, 0);
7138c2ecf20Sopenharmony_ci	core->power_state = SI476X_POWER_DOWN;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	pdata = dev_get_platdata(&client->dev);
7168c2ecf20Sopenharmony_ci	if (pdata) {
7178c2ecf20Sopenharmony_ci		memcpy(&core->power_up_parameters,
7188c2ecf20Sopenharmony_ci		       &pdata->power_up_parameters,
7198c2ecf20Sopenharmony_ci		       sizeof(core->power_up_parameters));
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci		core->gpio_reset = -1;
7228c2ecf20Sopenharmony_ci		if (gpio_is_valid(pdata->gpio_reset)) {
7238c2ecf20Sopenharmony_ci			rval = gpio_request(pdata->gpio_reset, "si476x reset");
7248c2ecf20Sopenharmony_ci			if (rval) {
7258c2ecf20Sopenharmony_ci				dev_err(&client->dev,
7268c2ecf20Sopenharmony_ci					"Failed to request gpio: %d\n", rval);
7278c2ecf20Sopenharmony_ci				return rval;
7288c2ecf20Sopenharmony_ci			}
7298c2ecf20Sopenharmony_ci			core->gpio_reset = pdata->gpio_reset;
7308c2ecf20Sopenharmony_ci			gpio_direction_output(core->gpio_reset, 0);
7318c2ecf20Sopenharmony_ci		}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci		core->diversity_mode = pdata->diversity_mode;
7348c2ecf20Sopenharmony_ci		memcpy(&core->pinmux, &pdata->pinmux,
7358c2ecf20Sopenharmony_ci		       sizeof(struct si476x_pinmux));
7368c2ecf20Sopenharmony_ci	} else {
7378c2ecf20Sopenharmony_ci		dev_err(&client->dev, "No platform data provided\n");
7388c2ecf20Sopenharmony_ci		return -EINVAL;
7398c2ecf20Sopenharmony_ci	}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	core->supplies[0].supply = "vd";
7428c2ecf20Sopenharmony_ci	core->supplies[1].supply = "va";
7438c2ecf20Sopenharmony_ci	core->supplies[2].supply = "vio1";
7448c2ecf20Sopenharmony_ci	core->supplies[3].supply = "vio2";
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	rval = devm_regulator_bulk_get(&client->dev,
7478c2ecf20Sopenharmony_ci				       ARRAY_SIZE(core->supplies),
7488c2ecf20Sopenharmony_ci				       core->supplies);
7498c2ecf20Sopenharmony_ci	if (rval) {
7508c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Failed to get all of the regulators\n");
7518c2ecf20Sopenharmony_ci		goto free_gpio;
7528c2ecf20Sopenharmony_ci	}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	mutex_init(&core->cmd_lock);
7558c2ecf20Sopenharmony_ci	init_waitqueue_head(&core->command);
7568c2ecf20Sopenharmony_ci	init_waitqueue_head(&core->tuning);
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	rval = kfifo_alloc(&core->rds_fifo,
7598c2ecf20Sopenharmony_ci			   SI476X_DRIVER_RDS_FIFO_DEPTH *
7608c2ecf20Sopenharmony_ci			   sizeof(struct v4l2_rds_data),
7618c2ecf20Sopenharmony_ci			   GFP_KERNEL);
7628c2ecf20Sopenharmony_ci	if (rval) {
7638c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Could not allocate the FIFO\n");
7648c2ecf20Sopenharmony_ci		goto free_gpio;
7658c2ecf20Sopenharmony_ci	}
7668c2ecf20Sopenharmony_ci	mutex_init(&core->rds_drainer_status_lock);
7678c2ecf20Sopenharmony_ci	init_waitqueue_head(&core->rds_read_queue);
7688c2ecf20Sopenharmony_ci	INIT_WORK(&core->rds_fifo_drainer, si476x_core_drain_rds_fifo);
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	if (client->irq) {
7718c2ecf20Sopenharmony_ci		rval = devm_request_threaded_irq(&client->dev,
7728c2ecf20Sopenharmony_ci						 client->irq, NULL,
7738c2ecf20Sopenharmony_ci						 si476x_core_interrupt,
7748c2ecf20Sopenharmony_ci						 IRQF_TRIGGER_FALLING |
7758c2ecf20Sopenharmony_ci						 IRQF_ONESHOT,
7768c2ecf20Sopenharmony_ci						 client->name, core);
7778c2ecf20Sopenharmony_ci		if (rval < 0) {
7788c2ecf20Sopenharmony_ci			dev_err(&client->dev, "Could not request IRQ %d\n",
7798c2ecf20Sopenharmony_ci				client->irq);
7808c2ecf20Sopenharmony_ci			goto free_kfifo;
7818c2ecf20Sopenharmony_ci		}
7828c2ecf20Sopenharmony_ci		disable_irq(client->irq);
7838c2ecf20Sopenharmony_ci		dev_dbg(&client->dev, "IRQ requested.\n");
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci		core->rds_fifo_depth = 20;
7868c2ecf20Sopenharmony_ci	} else {
7878c2ecf20Sopenharmony_ci		INIT_DELAYED_WORK(&core->status_monitor,
7888c2ecf20Sopenharmony_ci				  si476x_core_poll_loop);
7898c2ecf20Sopenharmony_ci		dev_info(&client->dev,
7908c2ecf20Sopenharmony_ci			 "No IRQ number specified, will use polling\n");
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci		core->rds_fifo_depth = 5;
7938c2ecf20Sopenharmony_ci	}
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	core->chip_id = id->driver_data;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	rval = si476x_core_get_revision_info(core);
7988c2ecf20Sopenharmony_ci	if (rval < 0) {
7998c2ecf20Sopenharmony_ci		rval = -ENODEV;
8008c2ecf20Sopenharmony_ci		goto free_kfifo;
8018c2ecf20Sopenharmony_ci	}
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	cell_num = 0;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	cell = &core->cells[SI476X_RADIO_CELL];
8068c2ecf20Sopenharmony_ci	cell->name = "si476x-radio";
8078c2ecf20Sopenharmony_ci	cell_num++;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_SOC_SI476X
8108c2ecf20Sopenharmony_ci	if ((core->chip_id == SI476X_CHIP_SI4761 ||
8118c2ecf20Sopenharmony_ci	     core->chip_id == SI476X_CHIP_SI4764)	&&
8128c2ecf20Sopenharmony_ci	    core->pinmux.dclk == SI476X_DCLK_DAUDIO     &&
8138c2ecf20Sopenharmony_ci	    core->pinmux.dfs  == SI476X_DFS_DAUDIO      &&
8148c2ecf20Sopenharmony_ci	    core->pinmux.dout == SI476X_DOUT_I2S_OUTPUT &&
8158c2ecf20Sopenharmony_ci	    core->pinmux.xout == SI476X_XOUT_TRISTATE) {
8168c2ecf20Sopenharmony_ci		cell = &core->cells[SI476X_CODEC_CELL];
8178c2ecf20Sopenharmony_ci		cell->name          = "si476x-codec";
8188c2ecf20Sopenharmony_ci		cell_num++;
8198c2ecf20Sopenharmony_ci	}
8208c2ecf20Sopenharmony_ci#endif
8218c2ecf20Sopenharmony_ci	rval = mfd_add_devices(&client->dev,
8228c2ecf20Sopenharmony_ci			       (client->adapter->nr << 8) + client->addr,
8238c2ecf20Sopenharmony_ci			       core->cells, cell_num,
8248c2ecf20Sopenharmony_ci			       NULL, 0, NULL);
8258c2ecf20Sopenharmony_ci	if (!rval)
8268c2ecf20Sopenharmony_ci		return 0;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_cifree_kfifo:
8298c2ecf20Sopenharmony_ci	kfifo_free(&core->rds_fifo);
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_cifree_gpio:
8328c2ecf20Sopenharmony_ci	if (gpio_is_valid(core->gpio_reset))
8338c2ecf20Sopenharmony_ci		gpio_free(core->gpio_reset);
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	return rval;
8368c2ecf20Sopenharmony_ci}
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_cistatic int si476x_core_remove(struct i2c_client *client)
8398c2ecf20Sopenharmony_ci{
8408c2ecf20Sopenharmony_ci	struct si476x_core *core = i2c_get_clientdata(client);
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	si476x_core_pronounce_dead(core);
8438c2ecf20Sopenharmony_ci	mfd_remove_devices(&client->dev);
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	if (client->irq)
8468c2ecf20Sopenharmony_ci		disable_irq(client->irq);
8478c2ecf20Sopenharmony_ci	else
8488c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&core->status_monitor);
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	kfifo_free(&core->rds_fifo);
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	if (gpio_is_valid(core->gpio_reset))
8538c2ecf20Sopenharmony_ci		gpio_free(core->gpio_reset);
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	return 0;
8568c2ecf20Sopenharmony_ci}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_cistatic const struct i2c_device_id si476x_id[] = {
8608c2ecf20Sopenharmony_ci	{ "si4761", SI476X_CHIP_SI4761 },
8618c2ecf20Sopenharmony_ci	{ "si4764", SI476X_CHIP_SI4764 },
8628c2ecf20Sopenharmony_ci	{ "si4768", SI476X_CHIP_SI4768 },
8638c2ecf20Sopenharmony_ci	{ },
8648c2ecf20Sopenharmony_ci};
8658c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, si476x_id);
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_cistatic struct i2c_driver si476x_core_driver = {
8688c2ecf20Sopenharmony_ci	.driver		= {
8698c2ecf20Sopenharmony_ci		.name	= "si476x-core",
8708c2ecf20Sopenharmony_ci	},
8718c2ecf20Sopenharmony_ci	.probe		= si476x_core_probe,
8728c2ecf20Sopenharmony_ci	.remove         = si476x_core_remove,
8738c2ecf20Sopenharmony_ci	.id_table       = si476x_id,
8748c2ecf20Sopenharmony_ci};
8758c2ecf20Sopenharmony_cimodule_i2c_driver(si476x_core_driver);
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
8798c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Si4761/64/68 AM/FM MFD core device driver");
8808c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
881