xref: /openbmc/linux/drivers/mfd/si476x-i2c.c (revision 9816d859)
18e8e69d6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2730a30abSAndrey Smirnov /*
3730a30abSAndrey Smirnov  * drivers/mfd/si476x-i2c.c -- Core device driver for si476x MFD
4730a30abSAndrey Smirnov  * device
5730a30abSAndrey Smirnov  *
6730a30abSAndrey Smirnov  * Copyright (C) 2012 Innovative Converged Devices(ICD)
7730a30abSAndrey Smirnov  * Copyright (C) 2013 Andrey Smirnov
8730a30abSAndrey Smirnov  *
9730a30abSAndrey Smirnov  * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
10730a30abSAndrey Smirnov  */
11730a30abSAndrey Smirnov #include <linux/module.h>
12730a30abSAndrey Smirnov 
13730a30abSAndrey Smirnov #include <linux/slab.h>
14730a30abSAndrey Smirnov #include <linux/interrupt.h>
15730a30abSAndrey Smirnov #include <linux/delay.h>
16730a30abSAndrey Smirnov #include <linux/gpio.h>
17730a30abSAndrey Smirnov #include <linux/regulator/consumer.h>
18730a30abSAndrey Smirnov #include <linux/i2c.h>
19730a30abSAndrey Smirnov #include <linux/err.h>
20730a30abSAndrey Smirnov 
21730a30abSAndrey Smirnov #include <linux/mfd/si476x-core.h>
22730a30abSAndrey Smirnov 
23730a30abSAndrey Smirnov #define SI476X_MAX_IO_ERRORS		10
24730a30abSAndrey Smirnov #define SI476X_DRIVER_RDS_FIFO_DEPTH	128
25730a30abSAndrey Smirnov 
26730a30abSAndrey Smirnov /**
27730a30abSAndrey Smirnov  * si476x_core_config_pinmux() - pin function configuration function
28730a30abSAndrey Smirnov  *
29730a30abSAndrey Smirnov  * @core: Core device structure
30730a30abSAndrey Smirnov  *
31730a30abSAndrey Smirnov  * Configure the functions of the pins of the radio chip.
32730a30abSAndrey Smirnov  *
33730a30abSAndrey Smirnov  * The function returns zero in case of succes or negative error code
34730a30abSAndrey Smirnov  * otherwise.
35730a30abSAndrey Smirnov  */
si476x_core_config_pinmux(struct si476x_core * core)36730a30abSAndrey Smirnov static int si476x_core_config_pinmux(struct si476x_core *core)
37730a30abSAndrey Smirnov {
38730a30abSAndrey Smirnov 	int err;
39730a30abSAndrey Smirnov 	dev_dbg(&core->client->dev, "Configuring pinmux\n");
40730a30abSAndrey Smirnov 	err = si476x_core_cmd_dig_audio_pin_cfg(core,
41730a30abSAndrey Smirnov 						core->pinmux.dclk,
42730a30abSAndrey Smirnov 						core->pinmux.dfs,
43730a30abSAndrey Smirnov 						core->pinmux.dout,
44730a30abSAndrey Smirnov 						core->pinmux.xout);
45730a30abSAndrey Smirnov 	if (err < 0) {
46730a30abSAndrey Smirnov 		dev_err(&core->client->dev,
47730a30abSAndrey Smirnov 			"Failed to configure digital audio pins(err = %d)\n",
48730a30abSAndrey Smirnov 			err);
49730a30abSAndrey Smirnov 		return err;
50730a30abSAndrey Smirnov 	}
51730a30abSAndrey Smirnov 
52730a30abSAndrey Smirnov 	err = si476x_core_cmd_zif_pin_cfg(core,
53730a30abSAndrey Smirnov 					  core->pinmux.iqclk,
54730a30abSAndrey Smirnov 					  core->pinmux.iqfs,
55730a30abSAndrey Smirnov 					  core->pinmux.iout,
56730a30abSAndrey Smirnov 					  core->pinmux.qout);
57730a30abSAndrey Smirnov 	if (err < 0) {
58730a30abSAndrey Smirnov 		dev_err(&core->client->dev,
59730a30abSAndrey Smirnov 			"Failed to configure ZIF pins(err = %d)\n",
60730a30abSAndrey Smirnov 			err);
61730a30abSAndrey Smirnov 		return err;
62730a30abSAndrey Smirnov 	}
63730a30abSAndrey Smirnov 
64730a30abSAndrey Smirnov 	err = si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(core,
65730a30abSAndrey Smirnov 						      core->pinmux.icin,
66730a30abSAndrey Smirnov 						      core->pinmux.icip,
67730a30abSAndrey Smirnov 						      core->pinmux.icon,
68730a30abSAndrey Smirnov 						      core->pinmux.icop);
69730a30abSAndrey Smirnov 	if (err < 0) {
70730a30abSAndrey Smirnov 		dev_err(&core->client->dev,
71730a30abSAndrey Smirnov 			"Failed to configure IC-Link/GPO pins(err = %d)\n",
72730a30abSAndrey Smirnov 			err);
73730a30abSAndrey Smirnov 		return err;
74730a30abSAndrey Smirnov 	}
75730a30abSAndrey Smirnov 
76730a30abSAndrey Smirnov 	err = si476x_core_cmd_ana_audio_pin_cfg(core,
77730a30abSAndrey Smirnov 						core->pinmux.lrout);
78730a30abSAndrey Smirnov 	if (err < 0) {
79730a30abSAndrey Smirnov 		dev_err(&core->client->dev,
80730a30abSAndrey Smirnov 			"Failed to configure analog audio pins(err = %d)\n",
81730a30abSAndrey Smirnov 			err);
82730a30abSAndrey Smirnov 		return err;
83730a30abSAndrey Smirnov 	}
84730a30abSAndrey Smirnov 
85730a30abSAndrey Smirnov 	err = si476x_core_cmd_intb_pin_cfg(core,
86730a30abSAndrey Smirnov 					   core->pinmux.intb,
87730a30abSAndrey Smirnov 					   core->pinmux.a1);
88730a30abSAndrey Smirnov 	if (err < 0) {
89730a30abSAndrey Smirnov 		dev_err(&core->client->dev,
90730a30abSAndrey Smirnov 			"Failed to configure interrupt pins(err = %d)\n",
91730a30abSAndrey Smirnov 			err);
92730a30abSAndrey Smirnov 		return err;
93730a30abSAndrey Smirnov 	}
94730a30abSAndrey Smirnov 
95730a30abSAndrey Smirnov 	return 0;
96730a30abSAndrey Smirnov }
97730a30abSAndrey Smirnov 
si476x_core_schedule_polling_work(struct si476x_core * core)98730a30abSAndrey Smirnov static inline void si476x_core_schedule_polling_work(struct si476x_core *core)
99730a30abSAndrey Smirnov {
100730a30abSAndrey Smirnov 	schedule_delayed_work(&core->status_monitor,
101730a30abSAndrey Smirnov 			      usecs_to_jiffies(SI476X_STATUS_POLL_US));
102730a30abSAndrey Smirnov }
103730a30abSAndrey Smirnov 
104730a30abSAndrey Smirnov /**
105730a30abSAndrey Smirnov  * si476x_core_start() - early chip startup function
106730a30abSAndrey Smirnov  * @core: Core device structure
107730a30abSAndrey Smirnov  * @soft: When set, this flag forces "soft" startup, where "soft"
108730a30abSAndrey Smirnov  * power down is the one done by sending appropriate command instead
109730a30abSAndrey Smirnov  * of using reset pin of the tuner
110730a30abSAndrey Smirnov  *
111730a30abSAndrey Smirnov  * Perform required startup sequence to correctly power
112730a30abSAndrey Smirnov  * up the chip and perform initial configuration. It does the
113730a30abSAndrey Smirnov  * following sequence of actions:
114730a30abSAndrey Smirnov  *       1. Claims and enables the power supplies VD and VIO1 required
115730a30abSAndrey Smirnov  *          for I2C interface of the chip operation.
116730a30abSAndrey Smirnov  *       2. Waits for 100us, pulls the reset line up, enables irq,
117730a30abSAndrey Smirnov  *          waits for another 100us as it is specified by the
118730a30abSAndrey Smirnov  *          datasheet.
119730a30abSAndrey Smirnov  *       3. Sends 'POWER_UP' command to the device with all provided
120730a30abSAndrey Smirnov  *          information about power-up parameters.
121730a30abSAndrey Smirnov  *       4. Configures, pin multiplexor, disables digital audio and
122730a30abSAndrey Smirnov  *          configures interrupt sources.
123730a30abSAndrey Smirnov  *
124730a30abSAndrey Smirnov  * The function returns zero in case of succes or negative error code
125730a30abSAndrey Smirnov  * otherwise.
126730a30abSAndrey Smirnov  */
si476x_core_start(struct si476x_core * core,bool soft)127730a30abSAndrey Smirnov int si476x_core_start(struct si476x_core *core, bool soft)
128730a30abSAndrey Smirnov {
129730a30abSAndrey Smirnov 	struct i2c_client *client = core->client;
130730a30abSAndrey Smirnov 	int err;
131730a30abSAndrey Smirnov 
132730a30abSAndrey Smirnov 	if (!soft) {
133730a30abSAndrey Smirnov 		if (gpio_is_valid(core->gpio_reset))
134730a30abSAndrey Smirnov 			gpio_set_value_cansleep(core->gpio_reset, 1);
135730a30abSAndrey Smirnov 
136730a30abSAndrey Smirnov 		if (client->irq)
137730a30abSAndrey Smirnov 			enable_irq(client->irq);
138730a30abSAndrey Smirnov 
139730a30abSAndrey Smirnov 		udelay(100);
140730a30abSAndrey Smirnov 
141730a30abSAndrey Smirnov 		if (!client->irq) {
142730a30abSAndrey Smirnov 			atomic_set(&core->is_alive, 1);
143730a30abSAndrey Smirnov 			si476x_core_schedule_polling_work(core);
144730a30abSAndrey Smirnov 		}
145730a30abSAndrey Smirnov 	} else {
146730a30abSAndrey Smirnov 		if (client->irq)
147730a30abSAndrey Smirnov 			enable_irq(client->irq);
148730a30abSAndrey Smirnov 		else {
149730a30abSAndrey Smirnov 			atomic_set(&core->is_alive, 1);
150730a30abSAndrey Smirnov 			si476x_core_schedule_polling_work(core);
151730a30abSAndrey Smirnov 		}
152730a30abSAndrey Smirnov 	}
153730a30abSAndrey Smirnov 
154730a30abSAndrey Smirnov 	err = si476x_core_cmd_power_up(core,
155730a30abSAndrey Smirnov 				       &core->power_up_parameters);
156730a30abSAndrey Smirnov 
157730a30abSAndrey Smirnov 	if (err < 0) {
158730a30abSAndrey Smirnov 		dev_err(&core->client->dev,
159730a30abSAndrey Smirnov 			"Power up failure(err = %d)\n",
160730a30abSAndrey Smirnov 			err);
161730a30abSAndrey Smirnov 		goto disable_irq;
162730a30abSAndrey Smirnov 	}
163730a30abSAndrey Smirnov 
164730a30abSAndrey Smirnov 	if (client->irq)
165730a30abSAndrey Smirnov 		atomic_set(&core->is_alive, 1);
166730a30abSAndrey Smirnov 
167730a30abSAndrey Smirnov 	err = si476x_core_config_pinmux(core);
168730a30abSAndrey Smirnov 	if (err < 0) {
169730a30abSAndrey Smirnov 		dev_err(&core->client->dev,
170730a30abSAndrey Smirnov 			"Failed to configure pinmux(err = %d)\n",
171730a30abSAndrey Smirnov 			err);
172730a30abSAndrey Smirnov 		goto disable_irq;
173730a30abSAndrey Smirnov 	}
174730a30abSAndrey Smirnov 
175730a30abSAndrey Smirnov 	if (client->irq) {
176730a30abSAndrey Smirnov 		err = regmap_write(core->regmap,
177730a30abSAndrey Smirnov 				   SI476X_PROP_INT_CTL_ENABLE,
178730a30abSAndrey Smirnov 				   SI476X_RDSIEN |
179730a30abSAndrey Smirnov 				   SI476X_STCIEN |
180730a30abSAndrey Smirnov 				   SI476X_CTSIEN);
181730a30abSAndrey Smirnov 		if (err < 0) {
182730a30abSAndrey Smirnov 			dev_err(&core->client->dev,
183730a30abSAndrey Smirnov 				"Failed to configure interrupt sources"
184730a30abSAndrey Smirnov 				"(err = %d)\n", err);
185730a30abSAndrey Smirnov 			goto disable_irq;
186730a30abSAndrey Smirnov 		}
187730a30abSAndrey Smirnov 	}
188730a30abSAndrey Smirnov 
189730a30abSAndrey Smirnov 	return 0;
190730a30abSAndrey Smirnov 
191730a30abSAndrey Smirnov disable_irq:
192730a30abSAndrey Smirnov 	if (err == -ENODEV)
193730a30abSAndrey Smirnov 		atomic_set(&core->is_alive, 0);
194730a30abSAndrey Smirnov 
195730a30abSAndrey Smirnov 	if (client->irq)
196730a30abSAndrey Smirnov 		disable_irq(client->irq);
197730a30abSAndrey Smirnov 	else
198730a30abSAndrey Smirnov 		cancel_delayed_work_sync(&core->status_monitor);
199730a30abSAndrey Smirnov 
200730a30abSAndrey Smirnov 	if (gpio_is_valid(core->gpio_reset))
201730a30abSAndrey Smirnov 		gpio_set_value_cansleep(core->gpio_reset, 0);
202730a30abSAndrey Smirnov 
203730a30abSAndrey Smirnov 	return err;
204730a30abSAndrey Smirnov }
205730a30abSAndrey Smirnov EXPORT_SYMBOL_GPL(si476x_core_start);
206730a30abSAndrey Smirnov 
207730a30abSAndrey Smirnov /**
208730a30abSAndrey Smirnov  * si476x_core_stop() - chip power-down function
209730a30abSAndrey Smirnov  * @core: Core device structure
210730a30abSAndrey Smirnov  * @soft: When set, function sends a POWER_DOWN command instead of
211730a30abSAndrey Smirnov  * bringing reset line low
212730a30abSAndrey Smirnov  *
213730a30abSAndrey Smirnov  * Power down the chip by performing following actions:
214730a30abSAndrey Smirnov  * 1. Disable IRQ or stop the polling worker
215730a30abSAndrey Smirnov  * 2. Send the POWER_DOWN command if the power down is soft or bring
216730a30abSAndrey Smirnov  *    reset line low if not.
217730a30abSAndrey Smirnov  *
218730a30abSAndrey Smirnov  * The function returns zero in case of succes or negative error code
219730a30abSAndrey Smirnov  * otherwise.
220730a30abSAndrey Smirnov  */
si476x_core_stop(struct si476x_core * core,bool soft)221730a30abSAndrey Smirnov int si476x_core_stop(struct si476x_core *core, bool soft)
222730a30abSAndrey Smirnov {
223730a30abSAndrey Smirnov 	int err = 0;
224730a30abSAndrey Smirnov 	atomic_set(&core->is_alive, 0);
225730a30abSAndrey Smirnov 
226730a30abSAndrey Smirnov 	if (soft) {
227730a30abSAndrey Smirnov 		/* TODO: This probably shoud be a configurable option,
228730a30abSAndrey Smirnov 		 * so it is possible to have the chips keep their
229730a30abSAndrey Smirnov 		 * oscillators running
230730a30abSAndrey Smirnov 		 */
231730a30abSAndrey Smirnov 		struct si476x_power_down_args args = {
232730a30abSAndrey Smirnov 			.xosc = false,
233730a30abSAndrey Smirnov 		};
234730a30abSAndrey Smirnov 		err = si476x_core_cmd_power_down(core, &args);
235730a30abSAndrey Smirnov 	}
236730a30abSAndrey Smirnov 
237730a30abSAndrey Smirnov 	/* We couldn't disable those before
238730a30abSAndrey Smirnov 	 * 'si476x_core_cmd_power_down' since we expect to get CTS
239730a30abSAndrey Smirnov 	 * interrupt */
240730a30abSAndrey Smirnov 	if (core->client->irq)
241730a30abSAndrey Smirnov 		disable_irq(core->client->irq);
242730a30abSAndrey Smirnov 	else
243730a30abSAndrey Smirnov 		cancel_delayed_work_sync(&core->status_monitor);
244730a30abSAndrey Smirnov 
245730a30abSAndrey Smirnov 	if (!soft) {
246730a30abSAndrey Smirnov 		if (gpio_is_valid(core->gpio_reset))
247730a30abSAndrey Smirnov 			gpio_set_value_cansleep(core->gpio_reset, 0);
248730a30abSAndrey Smirnov 	}
249730a30abSAndrey Smirnov 	return err;
250730a30abSAndrey Smirnov }
251730a30abSAndrey Smirnov EXPORT_SYMBOL_GPL(si476x_core_stop);
252730a30abSAndrey Smirnov 
253730a30abSAndrey Smirnov /**
254730a30abSAndrey Smirnov  * si476x_core_set_power_state() - set the level at which the power is
255730a30abSAndrey Smirnov  * supplied for the chip.
256730a30abSAndrey Smirnov  * @core: Core device structure
257730a30abSAndrey Smirnov  * @next_state: enum si476x_power_state describing power state to
258730a30abSAndrey Smirnov  *              switch to.
259730a30abSAndrey Smirnov  *
260730a30abSAndrey Smirnov  * Switch on all the required power supplies
261730a30abSAndrey Smirnov  *
262730a30abSAndrey Smirnov  * This function returns 0 in case of suvccess and negative error code
263730a30abSAndrey Smirnov  * otherwise.
264730a30abSAndrey Smirnov  */
si476x_core_set_power_state(struct si476x_core * core,enum si476x_power_state next_state)265730a30abSAndrey Smirnov int si476x_core_set_power_state(struct si476x_core *core,
266730a30abSAndrey Smirnov 				enum si476x_power_state next_state)
267730a30abSAndrey Smirnov {
268730a30abSAndrey Smirnov 	/*
269730a30abSAndrey Smirnov 	   It is not clear form the datasheet if it is possible to
270730a30abSAndrey Smirnov 	   work with device if not all power domains are operational.
271730a30abSAndrey Smirnov 	   So for now the power-up policy is "power-up all the things!"
272730a30abSAndrey Smirnov 	 */
273730a30abSAndrey Smirnov 	int err = 0;
274730a30abSAndrey Smirnov 
275730a30abSAndrey Smirnov 	if (core->power_state == SI476X_POWER_INCONSISTENT) {
276730a30abSAndrey Smirnov 		dev_err(&core->client->dev,
277730a30abSAndrey Smirnov 			"The device in inconsistent power state\n");
278730a30abSAndrey Smirnov 		return -EINVAL;
279730a30abSAndrey Smirnov 	}
280730a30abSAndrey Smirnov 
281730a30abSAndrey Smirnov 	if (next_state != core->power_state) {
282730a30abSAndrey Smirnov 		switch (next_state) {
283730a30abSAndrey Smirnov 		case SI476X_POWER_UP_FULL:
284730a30abSAndrey Smirnov 			err = regulator_bulk_enable(ARRAY_SIZE(core->supplies),
285730a30abSAndrey Smirnov 						    core->supplies);
286730a30abSAndrey Smirnov 			if (err < 0) {
287730a30abSAndrey Smirnov 				core->power_state = SI476X_POWER_INCONSISTENT;
288730a30abSAndrey Smirnov 				break;
289730a30abSAndrey Smirnov 			}
290730a30abSAndrey Smirnov 			/*
291730a30abSAndrey Smirnov 			 * Startup timing diagram recommends to have a
292730a30abSAndrey Smirnov 			 * 100 us delay between enabling of the power
293730a30abSAndrey Smirnov 			 * supplies and turning the tuner on.
294730a30abSAndrey Smirnov 			 */
295730a30abSAndrey Smirnov 			udelay(100);
296730a30abSAndrey Smirnov 
297730a30abSAndrey Smirnov 			err = si476x_core_start(core, false);
298730a30abSAndrey Smirnov 			if (err < 0)
299730a30abSAndrey Smirnov 				goto disable_regulators;
300730a30abSAndrey Smirnov 
301730a30abSAndrey Smirnov 			core->power_state = next_state;
302730a30abSAndrey Smirnov 			break;
303730a30abSAndrey Smirnov 
304730a30abSAndrey Smirnov 		case SI476X_POWER_DOWN:
305730a30abSAndrey Smirnov 			core->power_state = next_state;
306730a30abSAndrey Smirnov 			err = si476x_core_stop(core, false);
307730a30abSAndrey Smirnov 			if (err < 0)
308730a30abSAndrey Smirnov 				core->power_state = SI476X_POWER_INCONSISTENT;
309730a30abSAndrey Smirnov disable_regulators:
310730a30abSAndrey Smirnov 			err = regulator_bulk_disable(ARRAY_SIZE(core->supplies),
311730a30abSAndrey Smirnov 						     core->supplies);
312730a30abSAndrey Smirnov 			if (err < 0)
313730a30abSAndrey Smirnov 				core->power_state = SI476X_POWER_INCONSISTENT;
314730a30abSAndrey Smirnov 			break;
315730a30abSAndrey Smirnov 		default:
316730a30abSAndrey Smirnov 			BUG();
317730a30abSAndrey Smirnov 		}
318730a30abSAndrey Smirnov 	}
319730a30abSAndrey Smirnov 
320730a30abSAndrey Smirnov 	return err;
321730a30abSAndrey Smirnov }
322730a30abSAndrey Smirnov EXPORT_SYMBOL_GPL(si476x_core_set_power_state);
323730a30abSAndrey Smirnov 
324730a30abSAndrey Smirnov /**
325730a30abSAndrey Smirnov  * si476x_core_report_drainer_stop() - mark the completion of the RDS
326730a30abSAndrey Smirnov  * buffer drain porcess by the worker.
327730a30abSAndrey Smirnov  *
328730a30abSAndrey Smirnov  * @core: Core device structure
329730a30abSAndrey Smirnov  */
si476x_core_report_drainer_stop(struct si476x_core * core)330730a30abSAndrey Smirnov static inline void si476x_core_report_drainer_stop(struct si476x_core *core)
331730a30abSAndrey Smirnov {
332730a30abSAndrey Smirnov 	mutex_lock(&core->rds_drainer_status_lock);
333730a30abSAndrey Smirnov 	core->rds_drainer_is_working = false;
334730a30abSAndrey Smirnov 	mutex_unlock(&core->rds_drainer_status_lock);
335730a30abSAndrey Smirnov }
336730a30abSAndrey Smirnov 
337730a30abSAndrey Smirnov /**
338730a30abSAndrey Smirnov  * si476x_core_start_rds_drainer_once() - start RDS drainer worker if
339730a30abSAndrey Smirnov  * ther is none working, do nothing otherwise
340730a30abSAndrey Smirnov  *
341730a30abSAndrey Smirnov  * @core: Datastructure corresponding to the chip.
342730a30abSAndrey Smirnov  */
si476x_core_start_rds_drainer_once(struct si476x_core * core)343730a30abSAndrey Smirnov static inline void si476x_core_start_rds_drainer_once(struct si476x_core *core)
344730a30abSAndrey Smirnov {
345730a30abSAndrey Smirnov 	mutex_lock(&core->rds_drainer_status_lock);
346730a30abSAndrey Smirnov 	if (!core->rds_drainer_is_working) {
347730a30abSAndrey Smirnov 		core->rds_drainer_is_working = true;
348730a30abSAndrey Smirnov 		schedule_work(&core->rds_fifo_drainer);
349730a30abSAndrey Smirnov 	}
350730a30abSAndrey Smirnov 	mutex_unlock(&core->rds_drainer_status_lock);
351730a30abSAndrey Smirnov }
352730a30abSAndrey Smirnov /**
353769b7608SLee Jones  * si476x_core_drain_rds_fifo() - RDS buffer drainer.
354730a30abSAndrey Smirnov  * @work: struct work_struct being ppassed to the function by the
355730a30abSAndrey Smirnov  * kernel.
356730a30abSAndrey Smirnov  *
357730a30abSAndrey Smirnov  * Drain the contents of the RDS FIFO of
358730a30abSAndrey Smirnov  */
si476x_core_drain_rds_fifo(struct work_struct * work)359730a30abSAndrey Smirnov static void si476x_core_drain_rds_fifo(struct work_struct *work)
360730a30abSAndrey Smirnov {
361730a30abSAndrey Smirnov 	int err;
362730a30abSAndrey Smirnov 
363730a30abSAndrey Smirnov 	struct si476x_core *core = container_of(work, struct si476x_core,
364730a30abSAndrey Smirnov 						rds_fifo_drainer);
365730a30abSAndrey Smirnov 
366730a30abSAndrey Smirnov 	struct si476x_rds_status_report report;
367730a30abSAndrey Smirnov 
368730a30abSAndrey Smirnov 	si476x_core_lock(core);
369730a30abSAndrey Smirnov 	err = si476x_core_cmd_fm_rds_status(core, true, false, false, &report);
370730a30abSAndrey Smirnov 	if (!err) {
371730a30abSAndrey Smirnov 		int i = report.rdsfifoused;
372730a30abSAndrey Smirnov 		dev_dbg(&core->client->dev,
373730a30abSAndrey Smirnov 			"%d elements in RDS FIFO. Draining.\n", i);
374730a30abSAndrey Smirnov 		for (; i > 0; --i) {
375730a30abSAndrey Smirnov 			err = si476x_core_cmd_fm_rds_status(core, false, false,
376730a30abSAndrey Smirnov 							    (i == 1), &report);
377730a30abSAndrey Smirnov 			if (err < 0)
378730a30abSAndrey Smirnov 				goto unlock;
379730a30abSAndrey Smirnov 
380730a30abSAndrey Smirnov 			kfifo_in(&core->rds_fifo, report.rds,
381730a30abSAndrey Smirnov 				 sizeof(report.rds));
382730a30abSAndrey Smirnov 			dev_dbg(&core->client->dev, "RDS data:\n %*ph\n",
3834adedc57SHans Verkuil 				(int)sizeof(report.rds), report.rds);
384730a30abSAndrey Smirnov 		}
385730a30abSAndrey Smirnov 		dev_dbg(&core->client->dev, "Drrrrained!\n");
386730a30abSAndrey Smirnov 		wake_up_interruptible(&core->rds_read_queue);
387730a30abSAndrey Smirnov 	}
388730a30abSAndrey Smirnov 
389730a30abSAndrey Smirnov unlock:
390730a30abSAndrey Smirnov 	si476x_core_unlock(core);
391730a30abSAndrey Smirnov 	si476x_core_report_drainer_stop(core);
392730a30abSAndrey Smirnov }
393730a30abSAndrey Smirnov 
394730a30abSAndrey Smirnov /**
395730a30abSAndrey Smirnov  * si476x_core_pronounce_dead()
396730a30abSAndrey Smirnov  *
397730a30abSAndrey Smirnov  * @core: Core device structure
398730a30abSAndrey Smirnov  *
399730a30abSAndrey Smirnov  * Mark the device as being dead and wake up all potentially waiting
400730a30abSAndrey Smirnov  * threads of execution.
401730a30abSAndrey Smirnov  *
402730a30abSAndrey Smirnov  */
si476x_core_pronounce_dead(struct si476x_core * core)403730a30abSAndrey Smirnov static void si476x_core_pronounce_dead(struct si476x_core *core)
404730a30abSAndrey Smirnov {
405730a30abSAndrey Smirnov 	dev_info(&core->client->dev, "Core device is dead.\n");
406730a30abSAndrey Smirnov 
407730a30abSAndrey Smirnov 	atomic_set(&core->is_alive, 0);
408730a30abSAndrey Smirnov 
409730a30abSAndrey Smirnov 	/* Wake up al possible waiting processes */
410730a30abSAndrey Smirnov 	wake_up_interruptible(&core->rds_read_queue);
411730a30abSAndrey Smirnov 
412730a30abSAndrey Smirnov 	atomic_set(&core->cts, 1);
413730a30abSAndrey Smirnov 	wake_up(&core->command);
414730a30abSAndrey Smirnov 
415730a30abSAndrey Smirnov 	atomic_set(&core->stc, 1);
416730a30abSAndrey Smirnov 	wake_up(&core->tuning);
417730a30abSAndrey Smirnov }
418730a30abSAndrey Smirnov 
419730a30abSAndrey Smirnov /**
420730a30abSAndrey Smirnov  * si476x_core_i2c_xfer()
421730a30abSAndrey Smirnov  *
422730a30abSAndrey Smirnov  * @core: Core device structure
423730a30abSAndrey Smirnov  * @type: Transfer type
424730a30abSAndrey Smirnov  * @buf: Transfer buffer for/with data
425730a30abSAndrey Smirnov  * @count: Transfer buffer size
426730a30abSAndrey Smirnov  *
427730a30abSAndrey Smirnov  * Perfrom and I2C transfer(either read or write) and keep a counter
428730a30abSAndrey Smirnov  * of I/O errors. If the error counter rises above the threshold
429730a30abSAndrey Smirnov  * pronounce device dead.
430730a30abSAndrey Smirnov  *
431730a30abSAndrey Smirnov  * The function returns zero on succes or negative error code on
432730a30abSAndrey Smirnov  * failure.
433730a30abSAndrey Smirnov  */
si476x_core_i2c_xfer(struct si476x_core * core,enum si476x_i2c_type type,char * buf,int count)434730a30abSAndrey Smirnov int si476x_core_i2c_xfer(struct si476x_core *core,
435730a30abSAndrey Smirnov 		    enum si476x_i2c_type type,
436730a30abSAndrey Smirnov 		    char *buf, int count)
437730a30abSAndrey Smirnov {
438730a30abSAndrey Smirnov 	static int io_errors_count;
439730a30abSAndrey Smirnov 	int err;
440730a30abSAndrey Smirnov 	if (type == SI476X_I2C_SEND)
441730a30abSAndrey Smirnov 		err = i2c_master_send(core->client, buf, count);
442730a30abSAndrey Smirnov 	else
443730a30abSAndrey Smirnov 		err = i2c_master_recv(core->client, buf, count);
444730a30abSAndrey Smirnov 
445730a30abSAndrey Smirnov 	if (err < 0) {
446730a30abSAndrey Smirnov 		if (io_errors_count++ > SI476X_MAX_IO_ERRORS)
447730a30abSAndrey Smirnov 			si476x_core_pronounce_dead(core);
448730a30abSAndrey Smirnov 	} else {
449730a30abSAndrey Smirnov 		io_errors_count = 0;
450730a30abSAndrey Smirnov 	}
451730a30abSAndrey Smirnov 
452730a30abSAndrey Smirnov 	return err;
453730a30abSAndrey Smirnov }
454730a30abSAndrey Smirnov EXPORT_SYMBOL_GPL(si476x_core_i2c_xfer);
455730a30abSAndrey Smirnov 
456730a30abSAndrey Smirnov /**
457769b7608SLee Jones  * si476x_core_get_status()
458730a30abSAndrey Smirnov  * @core: Core device structure
459730a30abSAndrey Smirnov  *
460730a30abSAndrey Smirnov  * Get the status byte of the core device by berforming one byte I2C
461730a30abSAndrey Smirnov  * read.
462730a30abSAndrey Smirnov  *
463730a30abSAndrey Smirnov  * The function returns a status value or a negative error code on
464730a30abSAndrey Smirnov  * error.
465730a30abSAndrey Smirnov  */
si476x_core_get_status(struct si476x_core * core)466730a30abSAndrey Smirnov static int si476x_core_get_status(struct si476x_core *core)
467730a30abSAndrey Smirnov {
468730a30abSAndrey Smirnov 	u8 response;
469730a30abSAndrey Smirnov 	int err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV,
470730a30abSAndrey Smirnov 				  &response, sizeof(response));
471730a30abSAndrey Smirnov 
472730a30abSAndrey Smirnov 	return (err < 0) ? err : response;
473730a30abSAndrey Smirnov }
474730a30abSAndrey Smirnov 
475730a30abSAndrey Smirnov /**
476769b7608SLee Jones  * si476x_core_get_and_signal_status() - IRQ dispatcher
477730a30abSAndrey Smirnov  * @core: Core device structure
478730a30abSAndrey Smirnov  *
479730a30abSAndrey Smirnov  * Dispatch the arrived interrupt request based on the value of the
480730a30abSAndrey Smirnov  * status byte reported by the tuner.
481730a30abSAndrey Smirnov  *
482730a30abSAndrey Smirnov  */
si476x_core_get_and_signal_status(struct si476x_core * core)483730a30abSAndrey Smirnov static void si476x_core_get_and_signal_status(struct si476x_core *core)
484730a30abSAndrey Smirnov {
485730a30abSAndrey Smirnov 	int status = si476x_core_get_status(core);
486730a30abSAndrey Smirnov 	if (status < 0) {
487730a30abSAndrey Smirnov 		dev_err(&core->client->dev, "Failed to get status\n");
488730a30abSAndrey Smirnov 		return;
489730a30abSAndrey Smirnov 	}
490730a30abSAndrey Smirnov 
491730a30abSAndrey Smirnov 	if (status & SI476X_CTS) {
492730a30abSAndrey Smirnov 		/* Unfortunately completions could not be used for
493730a30abSAndrey Smirnov 		 * signalling CTS since this flag cannot be cleared
494730a30abSAndrey Smirnov 		 * in status byte, and therefore once it becomes true
495730a30abSAndrey Smirnov 		 * multiple calls to 'complete' would cause the
496730a30abSAndrey Smirnov 		 * commands following the current one to be completed
497730a30abSAndrey Smirnov 		 * before they actually are */
498730a30abSAndrey Smirnov 		dev_dbg(&core->client->dev, "[interrupt] CTSINT\n");
499730a30abSAndrey Smirnov 		atomic_set(&core->cts, 1);
500730a30abSAndrey Smirnov 		wake_up(&core->command);
501730a30abSAndrey Smirnov 	}
502730a30abSAndrey Smirnov 
503730a30abSAndrey Smirnov 	if (status & SI476X_FM_RDS_INT) {
504730a30abSAndrey Smirnov 		dev_dbg(&core->client->dev, "[interrupt] RDSINT\n");
505730a30abSAndrey Smirnov 		si476x_core_start_rds_drainer_once(core);
506730a30abSAndrey Smirnov 	}
507730a30abSAndrey Smirnov 
508730a30abSAndrey Smirnov 	if (status & SI476X_STC_INT) {
509730a30abSAndrey Smirnov 		dev_dbg(&core->client->dev, "[interrupt] STCINT\n");
510730a30abSAndrey Smirnov 		atomic_set(&core->stc, 1);
511730a30abSAndrey Smirnov 		wake_up(&core->tuning);
512730a30abSAndrey Smirnov 	}
513730a30abSAndrey Smirnov }
514730a30abSAndrey Smirnov 
si476x_core_poll_loop(struct work_struct * work)515730a30abSAndrey Smirnov static void si476x_core_poll_loop(struct work_struct *work)
516730a30abSAndrey Smirnov {
517730a30abSAndrey Smirnov 	struct si476x_core *core = SI476X_WORK_TO_CORE(work);
518730a30abSAndrey Smirnov 
519730a30abSAndrey Smirnov 	si476x_core_get_and_signal_status(core);
520730a30abSAndrey Smirnov 
521730a30abSAndrey Smirnov 	if (atomic_read(&core->is_alive))
522730a30abSAndrey Smirnov 		si476x_core_schedule_polling_work(core);
523730a30abSAndrey Smirnov }
524730a30abSAndrey Smirnov 
si476x_core_interrupt(int irq,void * dev)525730a30abSAndrey Smirnov static irqreturn_t si476x_core_interrupt(int irq, void *dev)
526730a30abSAndrey Smirnov {
527730a30abSAndrey Smirnov 	struct si476x_core *core = dev;
528730a30abSAndrey Smirnov 
529730a30abSAndrey Smirnov 	si476x_core_get_and_signal_status(core);
530730a30abSAndrey Smirnov 
531730a30abSAndrey Smirnov 	return IRQ_HANDLED;
532730a30abSAndrey Smirnov }
533730a30abSAndrey Smirnov 
534730a30abSAndrey Smirnov /**
535769b7608SLee Jones  * si476x_core_fwver_to_revision()
536730a30abSAndrey Smirnov  * @core: Core device structure
537c9b55f99SLee Jones  * @func: Selects the boot function of the device:
538c9b55f99SLee Jones  *         *_BOOTLOADER  - Boot loader
539c9b55f99SLee Jones  *         *_FM_RECEIVER - FM receiver
540c9b55f99SLee Jones  *         *_AM_RECEIVER - AM receiver
541c9b55f99SLee Jones  *         *_WB_RECEIVER - Weatherband receiver
542730a30abSAndrey Smirnov  * @major:  Firmware major number
543730a30abSAndrey Smirnov  * @minor1: Firmware first minor number
544730a30abSAndrey Smirnov  * @minor2: Firmware second minor number
545730a30abSAndrey Smirnov  *
546730a30abSAndrey Smirnov  * Convert a chip's firmware version number into an offset that later
547730a30abSAndrey Smirnov  * will be used to as offset in "vtable" of tuner functions
548730a30abSAndrey Smirnov  *
549730a30abSAndrey Smirnov  * This function returns a positive offset in case of success and a -1
550730a30abSAndrey Smirnov  * in case of failure.
551730a30abSAndrey Smirnov  */
si476x_core_fwver_to_revision(struct si476x_core * core,int func,int major,int minor1,int minor2)552730a30abSAndrey Smirnov static int si476x_core_fwver_to_revision(struct si476x_core *core,
553730a30abSAndrey Smirnov 					 int func, int major,
554730a30abSAndrey Smirnov 					 int minor1, int minor2)
555730a30abSAndrey Smirnov {
556730a30abSAndrey Smirnov 	switch (func) {
557730a30abSAndrey Smirnov 	case SI476X_FUNC_FM_RECEIVER:
558730a30abSAndrey Smirnov 		switch (major) {
559730a30abSAndrey Smirnov 		case 5:
560730a30abSAndrey Smirnov 			return SI476X_REVISION_A10;
561730a30abSAndrey Smirnov 		case 8:
562730a30abSAndrey Smirnov 			return SI476X_REVISION_A20;
563730a30abSAndrey Smirnov 		case 10:
564730a30abSAndrey Smirnov 			return SI476X_REVISION_A30;
565730a30abSAndrey Smirnov 		default:
566730a30abSAndrey Smirnov 			goto unknown_revision;
567730a30abSAndrey Smirnov 		}
568730a30abSAndrey Smirnov 	case SI476X_FUNC_AM_RECEIVER:
569730a30abSAndrey Smirnov 		switch (major) {
570730a30abSAndrey Smirnov 		case 5:
571730a30abSAndrey Smirnov 			return SI476X_REVISION_A10;
572730a30abSAndrey Smirnov 		case 7:
573730a30abSAndrey Smirnov 			return SI476X_REVISION_A20;
574730a30abSAndrey Smirnov 		case 9:
575730a30abSAndrey Smirnov 			return SI476X_REVISION_A30;
576730a30abSAndrey Smirnov 		default:
577730a30abSAndrey Smirnov 			goto unknown_revision;
578730a30abSAndrey Smirnov 		}
579730a30abSAndrey Smirnov 	case SI476X_FUNC_WB_RECEIVER:
580730a30abSAndrey Smirnov 		switch (major) {
581730a30abSAndrey Smirnov 		case 3:
582730a30abSAndrey Smirnov 			return SI476X_REVISION_A10;
583730a30abSAndrey Smirnov 		case 5:
584730a30abSAndrey Smirnov 			return SI476X_REVISION_A20;
585730a30abSAndrey Smirnov 		case 7:
586730a30abSAndrey Smirnov 			return SI476X_REVISION_A30;
587730a30abSAndrey Smirnov 		default:
588730a30abSAndrey Smirnov 			goto unknown_revision;
589730a30abSAndrey Smirnov 		}
590730a30abSAndrey Smirnov 	case SI476X_FUNC_BOOTLOADER:
591b1ded80aSLee Jones 	default:		/* FALLTHROUGH */
592730a30abSAndrey Smirnov 		BUG();
593730a30abSAndrey Smirnov 		return -1;
594730a30abSAndrey Smirnov 	}
595730a30abSAndrey Smirnov 
596730a30abSAndrey Smirnov unknown_revision:
597730a30abSAndrey Smirnov 	dev_err(&core->client->dev,
598730a30abSAndrey Smirnov 		"Unsupported version of the firmware: %d.%d.%d, "
599521d9357SColin Ian King 		"reverting to A10 compatible functions\n",
600730a30abSAndrey Smirnov 		major, minor1, minor2);
601730a30abSAndrey Smirnov 
602730a30abSAndrey Smirnov 	return SI476X_REVISION_A10;
603730a30abSAndrey Smirnov }
604730a30abSAndrey Smirnov 
605730a30abSAndrey Smirnov /**
606769b7608SLee Jones  * si476x_core_get_revision_info()
607730a30abSAndrey Smirnov  * @core: Core device structure
608730a30abSAndrey Smirnov  *
609730a30abSAndrey Smirnov  * Get the firmware version number of the device. It is done in
610730a30abSAndrey Smirnov  * following three steps:
611730a30abSAndrey Smirnov  *    1. Power-up the device
612730a30abSAndrey Smirnov  *    2. Send the 'FUNC_INFO' command
613730a30abSAndrey Smirnov  *    3. Powering the device down.
614730a30abSAndrey Smirnov  *
615730a30abSAndrey Smirnov  * The function return zero on success and a negative error code on
616730a30abSAndrey Smirnov  * failure.
617730a30abSAndrey Smirnov  */
si476x_core_get_revision_info(struct si476x_core * core)618730a30abSAndrey Smirnov static int si476x_core_get_revision_info(struct si476x_core *core)
619730a30abSAndrey Smirnov {
620730a30abSAndrey Smirnov 	int rval;
621730a30abSAndrey Smirnov 	struct si476x_func_info info;
622730a30abSAndrey Smirnov 
623730a30abSAndrey Smirnov 	si476x_core_lock(core);
624730a30abSAndrey Smirnov 	rval = si476x_core_set_power_state(core, SI476X_POWER_UP_FULL);
625730a30abSAndrey Smirnov 	if (rval < 0)
626730a30abSAndrey Smirnov 		goto exit;
627730a30abSAndrey Smirnov 
628730a30abSAndrey Smirnov 	rval = si476x_core_cmd_func_info(core, &info);
629730a30abSAndrey Smirnov 	if (rval < 0)
630730a30abSAndrey Smirnov 		goto power_down;
631730a30abSAndrey Smirnov 
632730a30abSAndrey Smirnov 	core->revision = si476x_core_fwver_to_revision(core, info.func,
633730a30abSAndrey Smirnov 						       info.firmware.major,
634730a30abSAndrey Smirnov 						       info.firmware.minor[0],
635730a30abSAndrey Smirnov 						       info.firmware.minor[1]);
636730a30abSAndrey Smirnov power_down:
637730a30abSAndrey Smirnov 	si476x_core_set_power_state(core, SI476X_POWER_DOWN);
638730a30abSAndrey Smirnov exit:
639730a30abSAndrey Smirnov 	si476x_core_unlock(core);
640730a30abSAndrey Smirnov 
641730a30abSAndrey Smirnov 	return rval;
642730a30abSAndrey Smirnov }
643730a30abSAndrey Smirnov 
si476x_core_has_am(struct si476x_core * core)644730a30abSAndrey Smirnov bool si476x_core_has_am(struct si476x_core *core)
645730a30abSAndrey Smirnov {
646730a30abSAndrey Smirnov 	return core->chip_id == SI476X_CHIP_SI4761 ||
647730a30abSAndrey Smirnov 		core->chip_id == SI476X_CHIP_SI4764;
648730a30abSAndrey Smirnov }
649730a30abSAndrey Smirnov EXPORT_SYMBOL_GPL(si476x_core_has_am);
650730a30abSAndrey Smirnov 
si476x_core_has_diversity(struct si476x_core * core)651730a30abSAndrey Smirnov bool si476x_core_has_diversity(struct si476x_core *core)
652730a30abSAndrey Smirnov {
653730a30abSAndrey Smirnov 	return core->chip_id == SI476X_CHIP_SI4764;
654730a30abSAndrey Smirnov }
655730a30abSAndrey Smirnov EXPORT_SYMBOL_GPL(si476x_core_has_diversity);
656730a30abSAndrey Smirnov 
si476x_core_is_a_secondary_tuner(struct si476x_core * core)657730a30abSAndrey Smirnov bool si476x_core_is_a_secondary_tuner(struct si476x_core *core)
658730a30abSAndrey Smirnov {
659730a30abSAndrey Smirnov 	return si476x_core_has_diversity(core) &&
660730a30abSAndrey Smirnov 		(core->diversity_mode == SI476X_PHDIV_SECONDARY_ANTENNA ||
661730a30abSAndrey Smirnov 		 core->diversity_mode == SI476X_PHDIV_SECONDARY_COMBINING);
662730a30abSAndrey Smirnov }
663730a30abSAndrey Smirnov EXPORT_SYMBOL_GPL(si476x_core_is_a_secondary_tuner);
664730a30abSAndrey Smirnov 
si476x_core_is_a_primary_tuner(struct si476x_core * core)665730a30abSAndrey Smirnov bool si476x_core_is_a_primary_tuner(struct si476x_core *core)
666730a30abSAndrey Smirnov {
667730a30abSAndrey Smirnov 	return si476x_core_has_diversity(core) &&
668730a30abSAndrey Smirnov 		(core->diversity_mode == SI476X_PHDIV_PRIMARY_ANTENNA ||
669730a30abSAndrey Smirnov 		 core->diversity_mode == SI476X_PHDIV_PRIMARY_COMBINING);
670730a30abSAndrey Smirnov }
671730a30abSAndrey Smirnov EXPORT_SYMBOL_GPL(si476x_core_is_a_primary_tuner);
672730a30abSAndrey Smirnov 
si476x_core_is_in_am_receiver_mode(struct si476x_core * core)673730a30abSAndrey Smirnov bool si476x_core_is_in_am_receiver_mode(struct si476x_core *core)
674730a30abSAndrey Smirnov {
675730a30abSAndrey Smirnov 	return si476x_core_has_am(core) &&
676730a30abSAndrey Smirnov 		(core->power_up_parameters.func == SI476X_FUNC_AM_RECEIVER);
677730a30abSAndrey Smirnov }
678730a30abSAndrey Smirnov EXPORT_SYMBOL_GPL(si476x_core_is_in_am_receiver_mode);
679730a30abSAndrey Smirnov 
si476x_core_is_powered_up(struct si476x_core * core)680730a30abSAndrey Smirnov bool si476x_core_is_powered_up(struct si476x_core *core)
681730a30abSAndrey Smirnov {
682730a30abSAndrey Smirnov 	return core->power_state == SI476X_POWER_UP_FULL;
683730a30abSAndrey Smirnov }
684730a30abSAndrey Smirnov EXPORT_SYMBOL_GPL(si476x_core_is_powered_up);
685730a30abSAndrey Smirnov 
si476x_core_probe(struct i2c_client * client)686ca08a4f3SUwe Kleine-König static int si476x_core_probe(struct i2c_client *client)
687730a30abSAndrey Smirnov {
688ca08a4f3SUwe Kleine-König 	const struct i2c_device_id *id = i2c_client_get_device_id(client);
689730a30abSAndrey Smirnov 	int rval;
690730a30abSAndrey Smirnov 	struct si476x_core          *core;
691730a30abSAndrey Smirnov 	struct si476x_platform_data *pdata;
692730a30abSAndrey Smirnov 	struct mfd_cell *cell;
693730a30abSAndrey Smirnov 	int              cell_num;
694730a30abSAndrey Smirnov 
695730a30abSAndrey Smirnov 	core = devm_kzalloc(&client->dev, sizeof(*core), GFP_KERNEL);
696a9241b09SMarkus Elfring 	if (!core)
697730a30abSAndrey Smirnov 		return -ENOMEM;
698a9241b09SMarkus Elfring 
699730a30abSAndrey Smirnov 	core->client = client;
700730a30abSAndrey Smirnov 
701730a30abSAndrey Smirnov 	core->regmap = devm_regmap_init_si476x(core);
702730a30abSAndrey Smirnov 	if (IS_ERR(core->regmap)) {
703730a30abSAndrey Smirnov 		rval = PTR_ERR(core->regmap);
704730a30abSAndrey Smirnov 		dev_err(&client->dev,
705730a30abSAndrey Smirnov 			"Failed to allocate register map: %d\n",
706730a30abSAndrey Smirnov 			rval);
707730a30abSAndrey Smirnov 		return rval;
708730a30abSAndrey Smirnov 	}
709730a30abSAndrey Smirnov 
710730a30abSAndrey Smirnov 	i2c_set_clientdata(client, core);
711730a30abSAndrey Smirnov 
712730a30abSAndrey Smirnov 	atomic_set(&core->is_alive, 0);
713730a30abSAndrey Smirnov 	core->power_state = SI476X_POWER_DOWN;
714730a30abSAndrey Smirnov 
715334a41ceSJingoo Han 	pdata = dev_get_platdata(&client->dev);
716730a30abSAndrey Smirnov 	if (pdata) {
717730a30abSAndrey Smirnov 		memcpy(&core->power_up_parameters,
718730a30abSAndrey Smirnov 		       &pdata->power_up_parameters,
719730a30abSAndrey Smirnov 		       sizeof(core->power_up_parameters));
720730a30abSAndrey Smirnov 
721730a30abSAndrey Smirnov 		core->gpio_reset = -1;
722730a30abSAndrey Smirnov 		if (gpio_is_valid(pdata->gpio_reset)) {
723730a30abSAndrey Smirnov 			rval = gpio_request(pdata->gpio_reset, "si476x reset");
724730a30abSAndrey Smirnov 			if (rval) {
725730a30abSAndrey Smirnov 				dev_err(&client->dev,
726730a30abSAndrey Smirnov 					"Failed to request gpio: %d\n", rval);
727730a30abSAndrey Smirnov 				return rval;
728730a30abSAndrey Smirnov 			}
729730a30abSAndrey Smirnov 			core->gpio_reset = pdata->gpio_reset;
730730a30abSAndrey Smirnov 			gpio_direction_output(core->gpio_reset, 0);
731730a30abSAndrey Smirnov 		}
732730a30abSAndrey Smirnov 
733730a30abSAndrey Smirnov 		core->diversity_mode = pdata->diversity_mode;
734730a30abSAndrey Smirnov 		memcpy(&core->pinmux, &pdata->pinmux,
735730a30abSAndrey Smirnov 		       sizeof(struct si476x_pinmux));
736730a30abSAndrey Smirnov 	} else {
737730a30abSAndrey Smirnov 		dev_err(&client->dev, "No platform data provided\n");
738730a30abSAndrey Smirnov 		return -EINVAL;
739730a30abSAndrey Smirnov 	}
740730a30abSAndrey Smirnov 
741730a30abSAndrey Smirnov 	core->supplies[0].supply = "vd";
742730a30abSAndrey Smirnov 	core->supplies[1].supply = "va";
743730a30abSAndrey Smirnov 	core->supplies[2].supply = "vio1";
744730a30abSAndrey Smirnov 	core->supplies[3].supply = "vio2";
745730a30abSAndrey Smirnov 
746730a30abSAndrey Smirnov 	rval = devm_regulator_bulk_get(&client->dev,
747730a30abSAndrey Smirnov 				       ARRAY_SIZE(core->supplies),
748730a30abSAndrey Smirnov 				       core->supplies);
749730a30abSAndrey Smirnov 	if (rval) {
750d4c55da2SColin Ian King 		dev_err(&client->dev, "Failed to get all of the regulators\n");
751730a30abSAndrey Smirnov 		goto free_gpio;
752730a30abSAndrey Smirnov 	}
753730a30abSAndrey Smirnov 
754730a30abSAndrey Smirnov 	mutex_init(&core->cmd_lock);
755730a30abSAndrey Smirnov 	init_waitqueue_head(&core->command);
756730a30abSAndrey Smirnov 	init_waitqueue_head(&core->tuning);
757730a30abSAndrey Smirnov 
758730a30abSAndrey Smirnov 	rval = kfifo_alloc(&core->rds_fifo,
759730a30abSAndrey Smirnov 			   SI476X_DRIVER_RDS_FIFO_DEPTH *
760730a30abSAndrey Smirnov 			   sizeof(struct v4l2_rds_data),
761730a30abSAndrey Smirnov 			   GFP_KERNEL);
762730a30abSAndrey Smirnov 	if (rval) {
763f42cf8d6SMasanari Iida 		dev_err(&client->dev, "Could not allocate the FIFO\n");
764730a30abSAndrey Smirnov 		goto free_gpio;
765730a30abSAndrey Smirnov 	}
766730a30abSAndrey Smirnov 	mutex_init(&core->rds_drainer_status_lock);
767730a30abSAndrey Smirnov 	init_waitqueue_head(&core->rds_read_queue);
768730a30abSAndrey Smirnov 	INIT_WORK(&core->rds_fifo_drainer, si476x_core_drain_rds_fifo);
769730a30abSAndrey Smirnov 
770730a30abSAndrey Smirnov 	if (client->irq) {
771730a30abSAndrey Smirnov 		rval = devm_request_threaded_irq(&client->dev,
772730a30abSAndrey Smirnov 						 client->irq, NULL,
773730a30abSAndrey Smirnov 						 si476x_core_interrupt,
774ef76cc5bSFabio Estevam 						 IRQF_TRIGGER_FALLING |
775ef76cc5bSFabio Estevam 						 IRQF_ONESHOT,
776730a30abSAndrey Smirnov 						 client->name, core);
777730a30abSAndrey Smirnov 		if (rval < 0) {
778730a30abSAndrey Smirnov 			dev_err(&client->dev, "Could not request IRQ %d\n",
779730a30abSAndrey Smirnov 				client->irq);
780730a30abSAndrey Smirnov 			goto free_kfifo;
781730a30abSAndrey Smirnov 		}
782730a30abSAndrey Smirnov 		disable_irq(client->irq);
783730a30abSAndrey Smirnov 		dev_dbg(&client->dev, "IRQ requested.\n");
784730a30abSAndrey Smirnov 
785730a30abSAndrey Smirnov 		core->rds_fifo_depth = 20;
786730a30abSAndrey Smirnov 	} else {
787730a30abSAndrey Smirnov 		INIT_DELAYED_WORK(&core->status_monitor,
788730a30abSAndrey Smirnov 				  si476x_core_poll_loop);
789730a30abSAndrey Smirnov 		dev_info(&client->dev,
790730a30abSAndrey Smirnov 			 "No IRQ number specified, will use polling\n");
791730a30abSAndrey Smirnov 
792730a30abSAndrey Smirnov 		core->rds_fifo_depth = 5;
793730a30abSAndrey Smirnov 	}
794730a30abSAndrey Smirnov 
795730a30abSAndrey Smirnov 	core->chip_id = id->driver_data;
796730a30abSAndrey Smirnov 
797730a30abSAndrey Smirnov 	rval = si476x_core_get_revision_info(core);
798730a30abSAndrey Smirnov 	if (rval < 0) {
799730a30abSAndrey Smirnov 		rval = -ENODEV;
800730a30abSAndrey Smirnov 		goto free_kfifo;
801730a30abSAndrey Smirnov 	}
802730a30abSAndrey Smirnov 
803730a30abSAndrey Smirnov 	cell_num = 0;
804730a30abSAndrey Smirnov 
805730a30abSAndrey Smirnov 	cell = &core->cells[SI476X_RADIO_CELL];
806730a30abSAndrey Smirnov 	cell->name = "si476x-radio";
807730a30abSAndrey Smirnov 	cell_num++;
808730a30abSAndrey Smirnov 
809730a30abSAndrey Smirnov #ifdef CONFIG_SND_SOC_SI476X
810730a30abSAndrey Smirnov 	if ((core->chip_id == SI476X_CHIP_SI4761 ||
811730a30abSAndrey Smirnov 	     core->chip_id == SI476X_CHIP_SI4764)	&&
812730a30abSAndrey Smirnov 	    core->pinmux.dclk == SI476X_DCLK_DAUDIO     &&
813730a30abSAndrey Smirnov 	    core->pinmux.dfs  == SI476X_DFS_DAUDIO      &&
814730a30abSAndrey Smirnov 	    core->pinmux.dout == SI476X_DOUT_I2S_OUTPUT &&
815730a30abSAndrey Smirnov 	    core->pinmux.xout == SI476X_XOUT_TRISTATE) {
816730a30abSAndrey Smirnov 		cell = &core->cells[SI476X_CODEC_CELL];
817730a30abSAndrey Smirnov 		cell->name          = "si476x-codec";
818730a30abSAndrey Smirnov 		cell_num++;
819730a30abSAndrey Smirnov 	}
820730a30abSAndrey Smirnov #endif
821730a30abSAndrey Smirnov 	rval = mfd_add_devices(&client->dev,
822730a30abSAndrey Smirnov 			       (client->adapter->nr << 8) + client->addr,
823730a30abSAndrey Smirnov 			       core->cells, cell_num,
824730a30abSAndrey Smirnov 			       NULL, 0, NULL);
825730a30abSAndrey Smirnov 	if (!rval)
826730a30abSAndrey Smirnov 		return 0;
827730a30abSAndrey Smirnov 
828730a30abSAndrey Smirnov free_kfifo:
829730a30abSAndrey Smirnov 	kfifo_free(&core->rds_fifo);
830730a30abSAndrey Smirnov 
831730a30abSAndrey Smirnov free_gpio:
832730a30abSAndrey Smirnov 	if (gpio_is_valid(core->gpio_reset))
833730a30abSAndrey Smirnov 		gpio_free(core->gpio_reset);
834730a30abSAndrey Smirnov 
835730a30abSAndrey Smirnov 	return rval;
836730a30abSAndrey Smirnov }
837730a30abSAndrey Smirnov 
si476x_core_remove(struct i2c_client * client)838ed5c2f5fSUwe Kleine-König static void si476x_core_remove(struct i2c_client *client)
839730a30abSAndrey Smirnov {
840730a30abSAndrey Smirnov 	struct si476x_core *core = i2c_get_clientdata(client);
841730a30abSAndrey Smirnov 
842730a30abSAndrey Smirnov 	si476x_core_pronounce_dead(core);
843730a30abSAndrey Smirnov 	mfd_remove_devices(&client->dev);
844730a30abSAndrey Smirnov 
845730a30abSAndrey Smirnov 	if (client->irq)
846730a30abSAndrey Smirnov 		disable_irq(client->irq);
847730a30abSAndrey Smirnov 	else
848730a30abSAndrey Smirnov 		cancel_delayed_work_sync(&core->status_monitor);
849730a30abSAndrey Smirnov 
850730a30abSAndrey Smirnov 	kfifo_free(&core->rds_fifo);
851730a30abSAndrey Smirnov 
852730a30abSAndrey Smirnov 	if (gpio_is_valid(core->gpio_reset))
853730a30abSAndrey Smirnov 		gpio_free(core->gpio_reset);
854730a30abSAndrey Smirnov }
855730a30abSAndrey Smirnov 
856730a30abSAndrey Smirnov 
857730a30abSAndrey Smirnov static const struct i2c_device_id si476x_id[] = {
858730a30abSAndrey Smirnov 	{ "si4761", SI476X_CHIP_SI4761 },
859730a30abSAndrey Smirnov 	{ "si4764", SI476X_CHIP_SI4764 },
860730a30abSAndrey Smirnov 	{ "si4768", SI476X_CHIP_SI4768 },
861730a30abSAndrey Smirnov 	{ },
862730a30abSAndrey Smirnov };
863730a30abSAndrey Smirnov MODULE_DEVICE_TABLE(i2c, si476x_id);
864730a30abSAndrey Smirnov 
865730a30abSAndrey Smirnov static struct i2c_driver si476x_core_driver = {
866730a30abSAndrey Smirnov 	.driver		= {
867730a30abSAndrey Smirnov 		.name	= "si476x-core",
868730a30abSAndrey Smirnov 	},
869*9816d859SUwe Kleine-König 	.probe		= si476x_core_probe,
870730a30abSAndrey Smirnov 	.remove         = si476x_core_remove,
871730a30abSAndrey Smirnov 	.id_table       = si476x_id,
872730a30abSAndrey Smirnov };
873730a30abSAndrey Smirnov module_i2c_driver(si476x_core_driver);
874730a30abSAndrey Smirnov 
875730a30abSAndrey Smirnov 
876730a30abSAndrey Smirnov MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
877730a30abSAndrey Smirnov MODULE_DESCRIPTION("Si4761/64/68 AM/FM MFD core device driver");
878730a30abSAndrey Smirnov MODULE_LICENSE("GPL");
879