Merge branch 'sched-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-block.git] / drivers / mfd / si476x-cmd.c
CommitLineData
8e8e69d6 1// SPDX-License-Identifier: GPL-2.0-only
ed4a8fe8
AS
2/*
3 * drivers/mfd/si476x-cmd.c -- Subroutines implementing command
4 * protocol of si476x series of chips
5 *
6 * Copyright (C) 2012 Innovative Converged Devices(ICD)
7 * Copyright (C) 2013 Andrey Smirnov
8 *
9 * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
ed4a8fe8
AS
10 */
11
12#include <linux/module.h>
13#include <linux/completion.h>
14#include <linux/delay.h>
15#include <linux/atomic.h>
16#include <linux/i2c.h>
17#include <linux/device.h>
18#include <linux/gpio.h>
19#include <linux/videodev2.h>
20
21#include <linux/mfd/si476x-core.h>
22
151978bf
GU
23#include <asm/unaligned.h>
24
ed4a8fe8
AS
25#define msb(x) ((u8)((u16) x >> 8))
26#define lsb(x) ((u8)((u16) x & 0x00FF))
27
28
29
30#define CMD_POWER_UP 0x01
31#define CMD_POWER_UP_A10_NRESP 1
32#define CMD_POWER_UP_A10_NARGS 5
33
34#define CMD_POWER_UP_A20_NRESP 1
35#define CMD_POWER_UP_A20_NARGS 5
36
37#define POWER_UP_DELAY_MS 110
38
39#define CMD_POWER_DOWN 0x11
40#define CMD_POWER_DOWN_A10_NRESP 1
41
42#define CMD_POWER_DOWN_A20_NRESP 1
43#define CMD_POWER_DOWN_A20_NARGS 1
44
45#define CMD_FUNC_INFO 0x12
46#define CMD_FUNC_INFO_NRESP 7
47
48#define CMD_SET_PROPERTY 0x13
49#define CMD_SET_PROPERTY_NARGS 5
50#define CMD_SET_PROPERTY_NRESP 1
51
52#define CMD_GET_PROPERTY 0x14
53#define CMD_GET_PROPERTY_NARGS 3
54#define CMD_GET_PROPERTY_NRESP 4
55
56#define CMD_AGC_STATUS 0x17
57#define CMD_AGC_STATUS_NRESP_A10 2
58#define CMD_AGC_STATUS_NRESP_A20 6
59
60#define PIN_CFG_BYTE(x) (0x7F & (x))
61#define CMD_DIG_AUDIO_PIN_CFG 0x18
62#define CMD_DIG_AUDIO_PIN_CFG_NARGS 4
63#define CMD_DIG_AUDIO_PIN_CFG_NRESP 5
64
65#define CMD_ZIF_PIN_CFG 0x19
66#define CMD_ZIF_PIN_CFG_NARGS 4
67#define CMD_ZIF_PIN_CFG_NRESP 5
68
69#define CMD_IC_LINK_GPO_CTL_PIN_CFG 0x1A
70#define CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS 4
71#define CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP 5
72
73#define CMD_ANA_AUDIO_PIN_CFG 0x1B
74#define CMD_ANA_AUDIO_PIN_CFG_NARGS 1
75#define CMD_ANA_AUDIO_PIN_CFG_NRESP 2
76
77#define CMD_INTB_PIN_CFG 0x1C
78#define CMD_INTB_PIN_CFG_NARGS 2
79#define CMD_INTB_PIN_CFG_A10_NRESP 6
80#define CMD_INTB_PIN_CFG_A20_NRESP 3
81
82#define CMD_FM_TUNE_FREQ 0x30
83#define CMD_FM_TUNE_FREQ_A10_NARGS 5
84#define CMD_FM_TUNE_FREQ_A20_NARGS 3
85#define CMD_FM_TUNE_FREQ_NRESP 1
86
87#define CMD_FM_RSQ_STATUS 0x32
88
89#define CMD_FM_RSQ_STATUS_A10_NARGS 1
90#define CMD_FM_RSQ_STATUS_A10_NRESP 17
91#define CMD_FM_RSQ_STATUS_A30_NARGS 1
92#define CMD_FM_RSQ_STATUS_A30_NRESP 23
93
94
95#define CMD_FM_SEEK_START 0x31
96#define CMD_FM_SEEK_START_NARGS 1
97#define CMD_FM_SEEK_START_NRESP 1
98
99#define CMD_FM_RDS_STATUS 0x36
100#define CMD_FM_RDS_STATUS_NARGS 1
101#define CMD_FM_RDS_STATUS_NRESP 16
102
103#define CMD_FM_RDS_BLOCKCOUNT 0x37
104#define CMD_FM_RDS_BLOCKCOUNT_NARGS 1
105#define CMD_FM_RDS_BLOCKCOUNT_NRESP 8
106
107#define CMD_FM_PHASE_DIVERSITY 0x38
108#define CMD_FM_PHASE_DIVERSITY_NARGS 1
109#define CMD_FM_PHASE_DIVERSITY_NRESP 1
110
111#define CMD_FM_PHASE_DIV_STATUS 0x39
112#define CMD_FM_PHASE_DIV_STATUS_NRESP 2
113
114#define CMD_AM_TUNE_FREQ 0x40
115#define CMD_AM_TUNE_FREQ_NARGS 3
116#define CMD_AM_TUNE_FREQ_NRESP 1
117
118#define CMD_AM_RSQ_STATUS 0x42
119#define CMD_AM_RSQ_STATUS_NARGS 1
120#define CMD_AM_RSQ_STATUS_NRESP 13
121
122#define CMD_AM_SEEK_START 0x41
123#define CMD_AM_SEEK_START_NARGS 1
124#define CMD_AM_SEEK_START_NRESP 1
125
126
127#define CMD_AM_ACF_STATUS 0x45
128#define CMD_AM_ACF_STATUS_NRESP 6
129#define CMD_AM_ACF_STATUS_NARGS 1
130
131#define CMD_FM_ACF_STATUS 0x35
132#define CMD_FM_ACF_STATUS_NRESP 8
133#define CMD_FM_ACF_STATUS_NARGS 1
134
135#define CMD_MAX_ARGS_COUNT (10)
136
137
138enum si476x_acf_status_report_bits {
139 SI476X_ACF_BLEND_INT = (1 << 4),
140 SI476X_ACF_HIBLEND_INT = (1 << 3),
141 SI476X_ACF_HICUT_INT = (1 << 2),
142 SI476X_ACF_CHBW_INT = (1 << 1),
143 SI476X_ACF_SOFTMUTE_INT = (1 << 0),
144
145 SI476X_ACF_SMUTE = (1 << 0),
b0222afa 146 SI476X_ACF_SMATTN = 0x1f,
ed4a8fe8
AS
147 SI476X_ACF_PILOT = (1 << 7),
148 SI476X_ACF_STBLEND = ~SI476X_ACF_PILOT,
149};
150
151enum si476x_agc_status_report_bits {
152 SI476X_AGC_MXHI = (1 << 5),
153 SI476X_AGC_MXLO = (1 << 4),
154 SI476X_AGC_LNAHI = (1 << 3),
155 SI476X_AGC_LNALO = (1 << 2),
156};
157
158enum si476x_errors {
159 SI476X_ERR_BAD_COMMAND = 0x10,
160 SI476X_ERR_BAD_ARG1 = 0x11,
161 SI476X_ERR_BAD_ARG2 = 0x12,
162 SI476X_ERR_BAD_ARG3 = 0x13,
163 SI476X_ERR_BAD_ARG4 = 0x14,
164 SI476X_ERR_BUSY = 0x18,
165 SI476X_ERR_BAD_INTERNAL_MEMORY = 0x20,
166 SI476X_ERR_BAD_PATCH = 0x30,
167 SI476X_ERR_BAD_BOOT_MODE = 0x31,
168 SI476X_ERR_BAD_PROPERTY = 0x40,
169};
170
171static int si476x_core_parse_and_nag_about_error(struct si476x_core *core)
172{
173 int err;
174 char *cause;
175 u8 buffer[2];
176
177 if (core->revision != SI476X_REVISION_A10) {
178 err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV,
179 buffer, sizeof(buffer));
180 if (err == sizeof(buffer)) {
181 switch (buffer[1]) {
182 case SI476X_ERR_BAD_COMMAND:
183 cause = "Bad command";
184 err = -EINVAL;
185 break;
186 case SI476X_ERR_BAD_ARG1:
187 cause = "Bad argument #1";
188 err = -EINVAL;
189 break;
190 case SI476X_ERR_BAD_ARG2:
191 cause = "Bad argument #2";
192 err = -EINVAL;
193 break;
194 case SI476X_ERR_BAD_ARG3:
195 cause = "Bad argument #3";
196 err = -EINVAL;
197 break;
198 case SI476X_ERR_BAD_ARG4:
199 cause = "Bad argument #4";
200 err = -EINVAL;
201 break;
202 case SI476X_ERR_BUSY:
203 cause = "Chip is busy";
204 err = -EBUSY;
205 break;
206 case SI476X_ERR_BAD_INTERNAL_MEMORY:
207 cause = "Bad internal memory";
208 err = -EIO;
209 break;
210 case SI476X_ERR_BAD_PATCH:
211 cause = "Bad patch";
212 err = -EINVAL;
213 break;
214 case SI476X_ERR_BAD_BOOT_MODE:
215 cause = "Bad boot mode";
216 err = -EINVAL;
217 break;
218 case SI476X_ERR_BAD_PROPERTY:
219 cause = "Bad property";
220 err = -EINVAL;
221 break;
222 default:
223 cause = "Unknown";
224 err = -EIO;
225 }
226
227 dev_err(&core->client->dev,
228 "[Chip error status]: %s\n", cause);
229 } else {
230 dev_err(&core->client->dev,
231 "Failed to fetch error code\n");
232 err = (err >= 0) ? -EIO : err;
233 }
234 } else {
235 err = -EIO;
236 }
237
238 return err;
239}
240
241/**
242 * si476x_core_send_command() - sends a command to si476x and waits its
243 * response
244 * @core: si476x_device structure for the device we are
245 * communicating with
246 * @command: command id
247 * @args: command arguments we are sending
248 * @argn: actual size of @args
249 * @response: buffer to place the expected response from the device
250 * @respn: actual size of @response
251 * @usecs: amount of time to wait before reading the response (in
252 * usecs)
253 *
254 * Function returns 0 on succsess and negative error code on
255 * failure
256 */
257static int si476x_core_send_command(struct si476x_core *core,
258 const u8 command,
259 const u8 args[],
260 const int argn,
261 u8 resp[],
262 const int respn,
263 const int usecs)
264{
265 struct i2c_client *client = core->client;
266 int err;
267 u8 data[CMD_MAX_ARGS_COUNT + 1];
268
269 if (argn > CMD_MAX_ARGS_COUNT) {
270 err = -ENOMEM;
271 goto exit;
272 }
273
274 if (!client->adapter) {
275 err = -ENODEV;
276 goto exit;
277 }
278
279 /* First send the command and its arguments */
280 data[0] = command;
281 memcpy(&data[1], args, argn);
282 dev_dbg(&client->dev, "Command:\n %*ph\n", argn + 1, data);
283
284 err = si476x_core_i2c_xfer(core, SI476X_I2C_SEND,
285 (char *) data, argn + 1);
286 if (err != argn + 1) {
287 dev_err(&core->client->dev,
288 "Error while sending command 0x%02x\n",
289 command);
290 err = (err >= 0) ? -EIO : err;
291 goto exit;
292 }
293 /* Set CTS to zero only after the command is send to avoid
294 * possible racing conditions when working in polling mode */
295 atomic_set(&core->cts, 0);
296
297 /* if (unlikely(command == CMD_POWER_DOWN) */
298 if (!wait_event_timeout(core->command,
299 atomic_read(&core->cts),
300 usecs_to_jiffies(usecs) + 1))
301 dev_warn(&core->client->dev,
302 "(%s) [CMD 0x%02x] Answer timeout.\n",
303 __func__, command);
304
305 /*
306 When working in polling mode, for some reason the tuner will
307 report CTS bit as being set in the first status byte read,
308 but all the consequtive ones will return zeros until the
309 tuner is actually completed the POWER_UP command. To
310 workaround that we wait for second CTS to be reported
311 */
312 if (unlikely(!core->client->irq && command == CMD_POWER_UP)) {
313 if (!wait_event_timeout(core->command,
314 atomic_read(&core->cts),
315 usecs_to_jiffies(usecs) + 1))
316 dev_warn(&core->client->dev,
317 "(%s) Power up took too much time.\n",
318 __func__);
319 }
320
321 /* Then get the response */
322 err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, resp, respn);
323 if (err != respn) {
324 dev_err(&core->client->dev,
325 "Error while reading response for command 0x%02x\n",
326 command);
327 err = (err >= 0) ? -EIO : err;
328 goto exit;
329 }
330 dev_dbg(&client->dev, "Response:\n %*ph\n", respn, resp);
331
332 err = 0;
333
334 if (resp[0] & SI476X_ERR) {
335 dev_err(&core->client->dev,
336 "[CMD 0x%02x] Chip set error flag\n", command);
337 err = si476x_core_parse_and_nag_about_error(core);
338 goto exit;
339 }
340
341 if (!(resp[0] & SI476X_CTS))
342 err = -EBUSY;
343exit:
344 return err;
345}
346
347static int si476x_cmd_clear_stc(struct si476x_core *core)
348{
349 int err;
350 struct si476x_rsq_status_args args = {
351 .primary = false,
352 .rsqack = false,
353 .attune = false,
354 .cancel = false,
355 .stcack = true,
356 };
357
358 switch (core->power_up_parameters.func) {
359 case SI476X_FUNC_FM_RECEIVER:
360 err = si476x_core_cmd_fm_rsq_status(core, &args, NULL);
361 break;
362 case SI476X_FUNC_AM_RECEIVER:
363 err = si476x_core_cmd_am_rsq_status(core, &args, NULL);
364 break;
365 default:
366 err = -EINVAL;
367 }
368
369 return err;
370}
371
372static int si476x_cmd_tune_seek_freq(struct si476x_core *core,
373 uint8_t cmd,
374 const uint8_t args[], size_t argn,
375 uint8_t *resp, size_t respn)
376{
377 int err;
378
379
380 atomic_set(&core->stc, 0);
381 err = si476x_core_send_command(core, cmd, args, argn, resp, respn,
382 SI476X_TIMEOUT_TUNE);
383 if (!err) {
384 wait_event_killable(core->tuning,
385 atomic_read(&core->stc));
386 si476x_cmd_clear_stc(core);
387 }
388
389 return err;
390}
391
392/**
393 * si476x_cmd_func_info() - send 'FUNC_INFO' command to the device
394 * @core: device to send the command to
395 * @info: struct si476x_func_info to fill all the information
396 * returned by the command
397 *
398 * The command requests the firmware and patch version for currently
399 * loaded firmware (dependent on the function of the device FM/AM/WB)
400 *
401 * Function returns 0 on succsess and negative error code on
402 * failure
403 */
404int si476x_core_cmd_func_info(struct si476x_core *core,
405 struct si476x_func_info *info)
406{
407 int err;
408 u8 resp[CMD_FUNC_INFO_NRESP];
409
410 err = si476x_core_send_command(core, CMD_FUNC_INFO,
411 NULL, 0,
412 resp, ARRAY_SIZE(resp),
413 SI476X_DEFAULT_TIMEOUT);
414
415 info->firmware.major = resp[1];
416 info->firmware.minor[0] = resp[2];
417 info->firmware.minor[1] = resp[3];
418
419 info->patch_id = ((u16) resp[4] << 8) | resp[5];
420 info->func = resp[6];
421
422 return err;
423}
424EXPORT_SYMBOL_GPL(si476x_core_cmd_func_info);
425
426/**
427 * si476x_cmd_set_property() - send 'SET_PROPERTY' command to the device
428 * @core: device to send the command to
429 * @property: property address
430 * @value: property value
431 *
432 * Function returns 0 on succsess and negative error code on
433 * failure
434 */
435int si476x_core_cmd_set_property(struct si476x_core *core,
436 u16 property, u16 value)
437{
438 u8 resp[CMD_SET_PROPERTY_NRESP];
439 const u8 args[CMD_SET_PROPERTY_NARGS] = {
440 0x00,
441 msb(property),
442 lsb(property),
443 msb(value),
444 lsb(value),
445 };
446
447 return si476x_core_send_command(core, CMD_SET_PROPERTY,
448 args, ARRAY_SIZE(args),
449 resp, ARRAY_SIZE(resp),
450 SI476X_DEFAULT_TIMEOUT);
451}
452EXPORT_SYMBOL_GPL(si476x_core_cmd_set_property);
453
454/**
455 * si476x_cmd_get_property() - send 'GET_PROPERTY' command to the device
456 * @core: device to send the command to
457 * @property: property address
458 *
459 * Function return the value of property as u16 on success or a
460 * negative error on failure
461 */
462int si476x_core_cmd_get_property(struct si476x_core *core, u16 property)
463{
464 int err;
465 u8 resp[CMD_GET_PROPERTY_NRESP];
466 const u8 args[CMD_GET_PROPERTY_NARGS] = {
467 0x00,
468 msb(property),
469 lsb(property),
470 };
471
472 err = si476x_core_send_command(core, CMD_GET_PROPERTY,
473 args, ARRAY_SIZE(args),
474 resp, ARRAY_SIZE(resp),
475 SI476X_DEFAULT_TIMEOUT);
476 if (err < 0)
477 return err;
478 else
151978bf 479 return get_unaligned_be16(resp + 2);
ed4a8fe8
AS
480}
481EXPORT_SYMBOL_GPL(si476x_core_cmd_get_property);
482
483/**
484 * si476x_cmd_dig_audio_pin_cfg() - send 'DIG_AUDIO_PIN_CFG' command to
485 * the device
486 * @core: device to send the command to
487 * @dclk: DCLK pin function configuration:
488 * #SI476X_DCLK_NOOP - do not modify the behaviour
489 * #SI476X_DCLK_TRISTATE - put the pin in tristate condition,
490 * enable 1MOhm pulldown
491 * #SI476X_DCLK_DAUDIO - set the pin to be a part of digital
492 * audio interface
493 * @dfs: DFS pin function configuration:
494 * #SI476X_DFS_NOOP - do not modify the behaviour
495 * #SI476X_DFS_TRISTATE - put the pin in tristate condition,
496 * enable 1MOhm pulldown
497 * SI476X_DFS_DAUDIO - set the pin to be a part of digital
498 * audio interface
499 * @dout - DOUT pin function configuration:
500 * SI476X_DOUT_NOOP - do not modify the behaviour
501 * SI476X_DOUT_TRISTATE - put the pin in tristate condition,
502 * enable 1MOhm pulldown
503 * SI476X_DOUT_I2S_OUTPUT - set this pin to be digital out on I2S
504 * port 1
505 * SI476X_DOUT_I2S_INPUT - set this pin to be digital in on I2S
506 * port 1
507 * @xout - XOUT pin function configuration:
508 * SI476X_XOUT_NOOP - do not modify the behaviour
509 * SI476X_XOUT_TRISTATE - put the pin in tristate condition,
510 * enable 1MOhm pulldown
511 * SI476X_XOUT_I2S_INPUT - set this pin to be digital in on I2S
512 * port 1
513 * SI476X_XOUT_MODE_SELECT - set this pin to be the input that
514 * selects the mode of the I2S audio
515 * combiner (analog or HD)
516 * [SI4761/63/65/67 Only]
517 *
518 * Function returns 0 on success and negative error code on failure
519 */
520int si476x_core_cmd_dig_audio_pin_cfg(struct si476x_core *core,
521 enum si476x_dclk_config dclk,
522 enum si476x_dfs_config dfs,
523 enum si476x_dout_config dout,
524 enum si476x_xout_config xout)
525{
526 u8 resp[CMD_DIG_AUDIO_PIN_CFG_NRESP];
527 const u8 args[CMD_DIG_AUDIO_PIN_CFG_NARGS] = {
528 PIN_CFG_BYTE(dclk),
529 PIN_CFG_BYTE(dfs),
530 PIN_CFG_BYTE(dout),
531 PIN_CFG_BYTE(xout),
532 };
533
534 return si476x_core_send_command(core, CMD_DIG_AUDIO_PIN_CFG,
535 args, ARRAY_SIZE(args),
536 resp, ARRAY_SIZE(resp),
537 SI476X_DEFAULT_TIMEOUT);
538}
539EXPORT_SYMBOL_GPL(si476x_core_cmd_dig_audio_pin_cfg);
540
541/**
542 * si476x_cmd_zif_pin_cfg - send 'ZIF_PIN_CFG_COMMAND'
543 * @core - device to send the command to
544 * @iqclk - IQCL pin function configuration:
545 * SI476X_IQCLK_NOOP - do not modify the behaviour
546 * SI476X_IQCLK_TRISTATE - put the pin in tristate condition,
547 * enable 1MOhm pulldown
548 * SI476X_IQCLK_IQ - set pin to be a part of I/Q interace
549 * in master mode
550 * @iqfs - IQFS pin function configuration:
551 * SI476X_IQFS_NOOP - do not modify the behaviour
552 * SI476X_IQFS_TRISTATE - put the pin in tristate condition,
553 * enable 1MOhm pulldown
554 * SI476X_IQFS_IQ - set pin to be a part of I/Q interace
555 * in master mode
556 * @iout - IOUT pin function configuration:
557 * SI476X_IOUT_NOOP - do not modify the behaviour
558 * SI476X_IOUT_TRISTATE - put the pin in tristate condition,
559 * enable 1MOhm pulldown
560 * SI476X_IOUT_OUTPUT - set pin to be I out
561 * @qout - QOUT pin function configuration:
562 * SI476X_QOUT_NOOP - do not modify the behaviour
563 * SI476X_QOUT_TRISTATE - put the pin in tristate condition,
564 * enable 1MOhm pulldown
565 * SI476X_QOUT_OUTPUT - set pin to be Q out
566 *
567 * Function returns 0 on success and negative error code on failure
568 */
569int si476x_core_cmd_zif_pin_cfg(struct si476x_core *core,
570 enum si476x_iqclk_config iqclk,
571 enum si476x_iqfs_config iqfs,
572 enum si476x_iout_config iout,
573 enum si476x_qout_config qout)
574{
575 u8 resp[CMD_ZIF_PIN_CFG_NRESP];
576 const u8 args[CMD_ZIF_PIN_CFG_NARGS] = {
577 PIN_CFG_BYTE(iqclk),
578 PIN_CFG_BYTE(iqfs),
579 PIN_CFG_BYTE(iout),
580 PIN_CFG_BYTE(qout),
581 };
582
583 return si476x_core_send_command(core, CMD_ZIF_PIN_CFG,
584 args, ARRAY_SIZE(args),
585 resp, ARRAY_SIZE(resp),
586 SI476X_DEFAULT_TIMEOUT);
587}
588EXPORT_SYMBOL_GPL(si476x_core_cmd_zif_pin_cfg);
589
590/**
591 * si476x_cmd_ic_link_gpo_ctl_pin_cfg - send
592 * 'IC_LINK_GPIO_CTL_PIN_CFG' comand to the device
593 * @core - device to send the command to
594 * @icin - ICIN pin function configuration:
595 * SI476X_ICIN_NOOP - do not modify the behaviour
596 * SI476X_ICIN_TRISTATE - put the pin in tristate condition,
597 * enable 1MOhm pulldown
598 * SI476X_ICIN_GPO1_HIGH - set pin to be an output, drive it high
599 * SI476X_ICIN_GPO1_LOW - set pin to be an output, drive it low
600 * SI476X_ICIN_IC_LINK - set the pin to be a part of Inter-Chip link
601 * @icip - ICIP pin function configuration:
602 * SI476X_ICIP_NOOP - do not modify the behaviour
603 * SI476X_ICIP_TRISTATE - put the pin in tristate condition,
604 * enable 1MOhm pulldown
605 * SI476X_ICIP_GPO1_HIGH - set pin to be an output, drive it high
606 * SI476X_ICIP_GPO1_LOW - set pin to be an output, drive it low
607 * SI476X_ICIP_IC_LINK - set the pin to be a part of Inter-Chip link
608 * @icon - ICON pin function configuration:
609 * SI476X_ICON_NOOP - do not modify the behaviour
610 * SI476X_ICON_TRISTATE - put the pin in tristate condition,
611 * enable 1MOhm pulldown
612 * SI476X_ICON_I2S - set the pin to be a part of audio
613 * interface in slave mode (DCLK)
614 * SI476X_ICON_IC_LINK - set the pin to be a part of Inter-Chip link
615 * @icop - ICOP pin function configuration:
616 * SI476X_ICOP_NOOP - do not modify the behaviour
617 * SI476X_ICOP_TRISTATE - put the pin in tristate condition,
618 * enable 1MOhm pulldown
619 * SI476X_ICOP_I2S - set the pin to be a part of audio
620 * interface in slave mode (DOUT)
621 * [Si4761/63/65/67 Only]
622 * SI476X_ICOP_IC_LINK - set the pin to be a part of Inter-Chip link
623 *
624 * Function returns 0 on success and negative error code on failure
625 */
626int si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(struct si476x_core *core,
627 enum si476x_icin_config icin,
628 enum si476x_icip_config icip,
629 enum si476x_icon_config icon,
630 enum si476x_icop_config icop)
631{
632 u8 resp[CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP];
633 const u8 args[CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS] = {
634 PIN_CFG_BYTE(icin),
635 PIN_CFG_BYTE(icip),
636 PIN_CFG_BYTE(icon),
637 PIN_CFG_BYTE(icop),
638 };
639
640 return si476x_core_send_command(core, CMD_IC_LINK_GPO_CTL_PIN_CFG,
641 args, ARRAY_SIZE(args),
642 resp, ARRAY_SIZE(resp),
643 SI476X_DEFAULT_TIMEOUT);
644}
645EXPORT_SYMBOL_GPL(si476x_core_cmd_ic_link_gpo_ctl_pin_cfg);
646
647/**
648 * si476x_cmd_ana_audio_pin_cfg - send 'ANA_AUDIO_PIN_CFG' to the
649 * device
650 * @core - device to send the command to
651 * @lrout - LROUT pin function configuration:
652 * SI476X_LROUT_NOOP - do not modify the behaviour
653 * SI476X_LROUT_TRISTATE - put the pin in tristate condition,
654 * enable 1MOhm pulldown
655 * SI476X_LROUT_AUDIO - set pin to be audio output
656 * SI476X_LROUT_MPX - set pin to be MPX output
657 *
658 * Function returns 0 on success and negative error code on failure
659 */
660int si476x_core_cmd_ana_audio_pin_cfg(struct si476x_core *core,
661 enum si476x_lrout_config lrout)
662{
663 u8 resp[CMD_ANA_AUDIO_PIN_CFG_NRESP];
664 const u8 args[CMD_ANA_AUDIO_PIN_CFG_NARGS] = {
665 PIN_CFG_BYTE(lrout),
666 };
667
668 return si476x_core_send_command(core, CMD_ANA_AUDIO_PIN_CFG,
669 args, ARRAY_SIZE(args),
670 resp, ARRAY_SIZE(resp),
671 SI476X_DEFAULT_TIMEOUT);
672}
673EXPORT_SYMBOL_GPL(si476x_core_cmd_ana_audio_pin_cfg);
674
675
676/**
677 * si476x_cmd_intb_pin_cfg - send 'INTB_PIN_CFG' command to the device
678 * @core - device to send the command to
679 * @intb - INTB pin function configuration:
680 * SI476X_INTB_NOOP - do not modify the behaviour
681 * SI476X_INTB_TRISTATE - put the pin in tristate condition,
682 * enable 1MOhm pulldown
683 * SI476X_INTB_DAUDIO - set pin to be a part of digital
684 * audio interface in slave mode
685 * SI476X_INTB_IRQ - set pin to be an interrupt request line
686 * @a1 - A1 pin function configuration:
687 * SI476X_A1_NOOP - do not modify the behaviour
688 * SI476X_A1_TRISTATE - put the pin in tristate condition,
689 * enable 1MOhm pulldown
690 * SI476X_A1_IRQ - set pin to be an interrupt request line
691 *
692 * Function returns 0 on success and negative error code on failure
693 */
694static int si476x_core_cmd_intb_pin_cfg_a10(struct si476x_core *core,
695 enum si476x_intb_config intb,
696 enum si476x_a1_config a1)
697{
698 u8 resp[CMD_INTB_PIN_CFG_A10_NRESP];
699 const u8 args[CMD_INTB_PIN_CFG_NARGS] = {
700 PIN_CFG_BYTE(intb),
701 PIN_CFG_BYTE(a1),
702 };
703
704 return si476x_core_send_command(core, CMD_INTB_PIN_CFG,
705 args, ARRAY_SIZE(args),
706 resp, ARRAY_SIZE(resp),
707 SI476X_DEFAULT_TIMEOUT);
708}
709
710static int si476x_core_cmd_intb_pin_cfg_a20(struct si476x_core *core,
711 enum si476x_intb_config intb,
712 enum si476x_a1_config a1)
713{
714 u8 resp[CMD_INTB_PIN_CFG_A20_NRESP];
715 const u8 args[CMD_INTB_PIN_CFG_NARGS] = {
716 PIN_CFG_BYTE(intb),
717 PIN_CFG_BYTE(a1),
718 };
719
720 return si476x_core_send_command(core, CMD_INTB_PIN_CFG,
721 args, ARRAY_SIZE(args),
722 resp, ARRAY_SIZE(resp),
723 SI476X_DEFAULT_TIMEOUT);
724}
725
726
727
728/**
729 * si476x_cmd_am_rsq_status - send 'AM_RSQ_STATUS' command to the
730 * device
731 * @core - device to send the command to
732 * @rsqack - if set command clears RSQINT, SNRINT, SNRLINT, RSSIHINT,
733 * RSSSILINT, BLENDINT, MULTHINT and MULTLINT
734 * @attune - when set the values in the status report are the values
735 * that were calculated at tune
736 * @cancel - abort ongoing seek/tune opertation
737 * @stcack - clear the STCINT bin in status register
738 * @report - all signal quality information retured by the command
739 * (if NULL then the output of the command is ignored)
740 *
741 * Function returns 0 on success and negative error code on failure
742 */
743int si476x_core_cmd_am_rsq_status(struct si476x_core *core,
744 struct si476x_rsq_status_args *rsqargs,
745 struct si476x_rsq_status_report *report)
746{
747 int err;
748 u8 resp[CMD_AM_RSQ_STATUS_NRESP];
749 const u8 args[CMD_AM_RSQ_STATUS_NARGS] = {
750 rsqargs->rsqack << 3 | rsqargs->attune << 2 |
751 rsqargs->cancel << 1 | rsqargs->stcack,
752 };
753
754 err = si476x_core_send_command(core, CMD_AM_RSQ_STATUS,
755 args, ARRAY_SIZE(args),
756 resp, ARRAY_SIZE(resp),
757 SI476X_DEFAULT_TIMEOUT);
758 /*
759 * Besides getting received signal quality information this
760 * command can be used to just acknowledge different interrupt
761 * flags in those cases it is useless to copy and parse
762 * received data so user can pass NULL, and thus avoid
763 * unnecessary copying.
764 */
765 if (!report)
766 return err;
767
b0222afa
GU
768 report->snrhint = 0x08 & resp[1];
769 report->snrlint = 0x04 & resp[1];
770 report->rssihint = 0x02 & resp[1];
771 report->rssilint = 0x01 & resp[1];
ed4a8fe8 772
b0222afa
GU
773 report->bltf = 0x80 & resp[2];
774 report->snr_ready = 0x20 & resp[2];
775 report->rssiready = 0x08 & resp[2];
776 report->afcrl = 0x02 & resp[2];
777 report->valid = 0x01 & resp[2];
ed4a8fe8 778
151978bf 779 report->readfreq = get_unaligned_be16(resp + 3);
ed4a8fe8
AS
780 report->freqoff = resp[5];
781 report->rssi = resp[6];
782 report->snr = resp[7];
783 report->lassi = resp[9];
784 report->hassi = resp[10];
785 report->mult = resp[11];
786 report->dev = resp[12];
787
788 return err;
789}
790EXPORT_SYMBOL_GPL(si476x_core_cmd_am_rsq_status);
791
792int si476x_core_cmd_fm_acf_status(struct si476x_core *core,
793 struct si476x_acf_status_report *report)
794{
795 int err;
796 u8 resp[CMD_FM_ACF_STATUS_NRESP];
797 const u8 args[CMD_FM_ACF_STATUS_NARGS] = {
798 0x0,
799 };
800
801 if (!report)
802 return -EINVAL;
803
804 err = si476x_core_send_command(core, CMD_FM_ACF_STATUS,
805 args, ARRAY_SIZE(args),
806 resp, ARRAY_SIZE(resp),
807 SI476X_DEFAULT_TIMEOUT);
808 if (err < 0)
809 return err;
810
811 report->blend_int = resp[1] & SI476X_ACF_BLEND_INT;
812 report->hblend_int = resp[1] & SI476X_ACF_HIBLEND_INT;
813 report->hicut_int = resp[1] & SI476X_ACF_HICUT_INT;
814 report->chbw_int = resp[1] & SI476X_ACF_CHBW_INT;
815 report->softmute_int = resp[1] & SI476X_ACF_SOFTMUTE_INT;
816 report->smute = resp[2] & SI476X_ACF_SMUTE;
817 report->smattn = resp[3] & SI476X_ACF_SMATTN;
818 report->chbw = resp[4];
819 report->hicut = resp[5];
820 report->hiblend = resp[6];
821 report->pilot = resp[7] & SI476X_ACF_PILOT;
822 report->stblend = resp[7] & SI476X_ACF_STBLEND;
823
824 return err;
825}
826EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_acf_status);
827
828int si476x_core_cmd_am_acf_status(struct si476x_core *core,
829 struct si476x_acf_status_report *report)
830{
831 int err;
832 u8 resp[CMD_AM_ACF_STATUS_NRESP];
833 const u8 args[CMD_AM_ACF_STATUS_NARGS] = {
834 0x0,
835 };
836
837 if (!report)
838 return -EINVAL;
839
840 err = si476x_core_send_command(core, CMD_AM_ACF_STATUS,
841 args, ARRAY_SIZE(args),
842 resp, ARRAY_SIZE(resp),
843 SI476X_DEFAULT_TIMEOUT);
844 if (err < 0)
845 return err;
846
847 report->blend_int = resp[1] & SI476X_ACF_BLEND_INT;
848 report->hblend_int = resp[1] & SI476X_ACF_HIBLEND_INT;
849 report->hicut_int = resp[1] & SI476X_ACF_HICUT_INT;
850 report->chbw_int = resp[1] & SI476X_ACF_CHBW_INT;
851 report->softmute_int = resp[1] & SI476X_ACF_SOFTMUTE_INT;
852 report->smute = resp[2] & SI476X_ACF_SMUTE;
853 report->smattn = resp[3] & SI476X_ACF_SMATTN;
854 report->chbw = resp[4];
855 report->hicut = resp[5];
856
857 return err;
858}
859EXPORT_SYMBOL_GPL(si476x_core_cmd_am_acf_status);
860
861
862/**
863 * si476x_cmd_fm_seek_start - send 'FM_SEEK_START' command to the
864 * device
865 * @core - device to send the command to
866 * @seekup - if set the direction of the search is 'up'
867 * @wrap - if set seek wraps when hitting band limit
868 *
869 * This function begins search for a valid station. The station is
870 * considered valid when 'FM_VALID_SNR_THRESHOLD' and
871 * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
872 * are met.
873} *
874 * Function returns 0 on success and negative error code on failure
875 */
876int si476x_core_cmd_fm_seek_start(struct si476x_core *core,
877 bool seekup, bool wrap)
878{
879 u8 resp[CMD_FM_SEEK_START_NRESP];
880 const u8 args[CMD_FM_SEEK_START_NARGS] = {
881 seekup << 3 | wrap << 2,
882 };
883
884 return si476x_cmd_tune_seek_freq(core, CMD_FM_SEEK_START,
885 args, sizeof(args),
886 resp, sizeof(resp));
887}
888EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_seek_start);
889
890/**
891 * si476x_cmd_fm_rds_status - send 'FM_RDS_STATUS' command to the
892 * device
893 * @core - device to send the command to
894 * @status_only - if set the data is not removed from RDSFIFO,
895 * RDSFIFOUSED is not decremented and data in all the
896 * rest RDS data contains the last valid info received
897 * @mtfifo if set the command clears RDS receive FIFO
898 * @intack if set the command clards the RDSINT bit.
899 *
900 * Function returns 0 on success and negative error code on failure
901 */
902int si476x_core_cmd_fm_rds_status(struct si476x_core *core,
903 bool status_only,
904 bool mtfifo,
905 bool intack,
906 struct si476x_rds_status_report *report)
907{
908 int err;
909 u8 resp[CMD_FM_RDS_STATUS_NRESP];
910 const u8 args[CMD_FM_RDS_STATUS_NARGS] = {
911 status_only << 2 | mtfifo << 1 | intack,
912 };
913
914 err = si476x_core_send_command(core, CMD_FM_RDS_STATUS,
915 args, ARRAY_SIZE(args),
916 resp, ARRAY_SIZE(resp),
917 SI476X_DEFAULT_TIMEOUT);
918 /*
919 * Besides getting RDS status information this command can be
920 * used to just acknowledge different interrupt flags in those
921 * cases it is useless to copy and parse received data so user
922 * can pass NULL, and thus avoid unnecessary copying.
923 */
924 if (err < 0 || report == NULL)
925 return err;
926
b0222afa
GU
927 report->rdstpptyint = 0x10 & resp[1];
928 report->rdspiint = 0x08 & resp[1];
929 report->rdssyncint = 0x02 & resp[1];
930 report->rdsfifoint = 0x01 & resp[1];
ed4a8fe8 931
b0222afa
GU
932 report->tpptyvalid = 0x10 & resp[2];
933 report->pivalid = 0x08 & resp[2];
934 report->rdssync = 0x02 & resp[2];
935 report->rdsfifolost = 0x01 & resp[2];
ed4a8fe8 936
b0222afa
GU
937 report->tp = 0x20 & resp[3];
938 report->pty = 0x1f & resp[3];
ed4a8fe8 939
151978bf 940 report->pi = get_unaligned_be16(resp + 4);
ed4a8fe8
AS
941 report->rdsfifoused = resp[6];
942
b0222afa
GU
943 report->ble[V4L2_RDS_BLOCK_A] = 0xc0 & resp[7];
944 report->ble[V4L2_RDS_BLOCK_B] = 0x30 & resp[7];
945 report->ble[V4L2_RDS_BLOCK_C] = 0x0c & resp[7];
946 report->ble[V4L2_RDS_BLOCK_D] = 0x03 & resp[7];
ed4a8fe8
AS
947
948 report->rds[V4L2_RDS_BLOCK_A].block = V4L2_RDS_BLOCK_A;
949 report->rds[V4L2_RDS_BLOCK_A].msb = resp[8];
950 report->rds[V4L2_RDS_BLOCK_A].lsb = resp[9];
951
952 report->rds[V4L2_RDS_BLOCK_B].block = V4L2_RDS_BLOCK_B;
953 report->rds[V4L2_RDS_BLOCK_B].msb = resp[10];
954 report->rds[V4L2_RDS_BLOCK_B].lsb = resp[11];
955
956 report->rds[V4L2_RDS_BLOCK_C].block = V4L2_RDS_BLOCK_C;
957 report->rds[V4L2_RDS_BLOCK_C].msb = resp[12];
958 report->rds[V4L2_RDS_BLOCK_C].lsb = resp[13];
959
960 report->rds[V4L2_RDS_BLOCK_D].block = V4L2_RDS_BLOCK_D;
961 report->rds[V4L2_RDS_BLOCK_D].msb = resp[14];
962 report->rds[V4L2_RDS_BLOCK_D].lsb = resp[15];
963
964 return err;
965}
966EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_status);
967
968int si476x_core_cmd_fm_rds_blockcount(struct si476x_core *core,
969 bool clear,
970 struct si476x_rds_blockcount_report *report)
971{
972 int err;
973 u8 resp[CMD_FM_RDS_BLOCKCOUNT_NRESP];
974 const u8 args[CMD_FM_RDS_BLOCKCOUNT_NARGS] = {
975 clear,
976 };
977
978 if (!report)
979 return -EINVAL;
980
981 err = si476x_core_send_command(core, CMD_FM_RDS_BLOCKCOUNT,
982 args, ARRAY_SIZE(args),
983 resp, ARRAY_SIZE(resp),
984 SI476X_DEFAULT_TIMEOUT);
985
986 if (!err) {
151978bf
GU
987 report->expected = get_unaligned_be16(resp + 2);
988 report->received = get_unaligned_be16(resp + 4);
989 report->uncorrectable = get_unaligned_be16(resp + 6);
ed4a8fe8
AS
990 }
991
992 return err;
993}
994EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_blockcount);
995
996int si476x_core_cmd_fm_phase_diversity(struct si476x_core *core,
997 enum si476x_phase_diversity_mode mode)
998{
999 u8 resp[CMD_FM_PHASE_DIVERSITY_NRESP];
1000 const u8 args[CMD_FM_PHASE_DIVERSITY_NARGS] = {
b0222afa 1001 mode & 0x07,
ed4a8fe8
AS
1002 };
1003
1004 return si476x_core_send_command(core, CMD_FM_PHASE_DIVERSITY,
1005 args, ARRAY_SIZE(args),
1006 resp, ARRAY_SIZE(resp),
1007 SI476X_DEFAULT_TIMEOUT);
1008}
1009EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_diversity);
1010/**
1011 * si476x_core_cmd_fm_phase_div_status() - get the phase diversity
1012 * status
1013 *
1014 * @core: si476x device
1015 *
1016 * NOTE caller must hold core lock
1017 *
1018 * Function returns the value of the status bit in case of success and
1019 * negative error code in case of failre.
1020 */
1021int si476x_core_cmd_fm_phase_div_status(struct si476x_core *core)
1022{
1023 int err;
1024 u8 resp[CMD_FM_PHASE_DIV_STATUS_NRESP];
1025
1026 err = si476x_core_send_command(core, CMD_FM_PHASE_DIV_STATUS,
1027 NULL, 0,
1028 resp, ARRAY_SIZE(resp),
1029 SI476X_DEFAULT_TIMEOUT);
1030
1031 return (err < 0) ? err : resp[1];
1032}
1033EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_div_status);
1034
1035
1036/**
1037 * si476x_cmd_am_seek_start - send 'FM_SEEK_START' command to the
1038 * device
1039 * @core - device to send the command to
1040 * @seekup - if set the direction of the search is 'up'
1041 * @wrap - if set seek wraps when hitting band limit
1042 *
1043 * This function begins search for a valid station. The station is
1044 * considered valid when 'FM_VALID_SNR_THRESHOLD' and
1045 * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
1046 * are met.
1047 *
1048 * Function returns 0 on success and negative error code on failure
1049 */
1050int si476x_core_cmd_am_seek_start(struct si476x_core *core,
1051 bool seekup, bool wrap)
1052{
1053 u8 resp[CMD_AM_SEEK_START_NRESP];
1054 const u8 args[CMD_AM_SEEK_START_NARGS] = {
1055 seekup << 3 | wrap << 2,
1056 };
1057
1058 return si476x_cmd_tune_seek_freq(core, CMD_AM_SEEK_START,
1059 args, sizeof(args),
1060 resp, sizeof(resp));
1061}
1062EXPORT_SYMBOL_GPL(si476x_core_cmd_am_seek_start);
1063
1064
1065
1066static int si476x_core_cmd_power_up_a10(struct si476x_core *core,
1067 struct si476x_power_up_args *puargs)
1068{
1069 u8 resp[CMD_POWER_UP_A10_NRESP];
1070 const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
1071 const bool ctsen = (core->client->irq != 0);
1072 const u8 args[CMD_POWER_UP_A10_NARGS] = {
1073 0xF7, /* Reserved, always 0xF7 */
1074 0x3F & puargs->xcload, /* First two bits are reserved to be
1075 * zeros */
1076 ctsen << 7 | intsel << 6 | 0x07, /* Last five bits
1077 * are reserved to
1078 * be written as 0x7 */
1079 puargs->func << 4 | puargs->freq,
1080 0x11, /* Reserved, always 0x11 */
1081 };
1082
1083 return si476x_core_send_command(core, CMD_POWER_UP,
1084 args, ARRAY_SIZE(args),
1085 resp, ARRAY_SIZE(resp),
1086 SI476X_TIMEOUT_POWER_UP);
1087}
1088
1089static int si476x_core_cmd_power_up_a20(struct si476x_core *core,
1090 struct si476x_power_up_args *puargs)
1091{
1092 u8 resp[CMD_POWER_UP_A20_NRESP];
1093 const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
1094 const bool ctsen = (core->client->irq != 0);
1095 const u8 args[CMD_POWER_UP_A20_NARGS] = {
1096 puargs->ibias6x << 7 | puargs->xstart,
1097 0x3F & puargs->xcload, /* First two bits are reserved to be
1098 * zeros */
1099 ctsen << 7 | intsel << 6 | puargs->fastboot << 5 |
1100 puargs->xbiashc << 3 | puargs->xbias,
1101 puargs->func << 4 | puargs->freq,
1102 0x10 | puargs->xmode,
1103 };
1104
1105 return si476x_core_send_command(core, CMD_POWER_UP,
1106 args, ARRAY_SIZE(args),
1107 resp, ARRAY_SIZE(resp),
1108 SI476X_TIMEOUT_POWER_UP);
1109}
1110
1111static int si476x_core_cmd_power_down_a10(struct si476x_core *core,
1112 struct si476x_power_down_args *pdargs)
1113{
1114 u8 resp[CMD_POWER_DOWN_A10_NRESP];
1115
1116 return si476x_core_send_command(core, CMD_POWER_DOWN,
1117 NULL, 0,
1118 resp, ARRAY_SIZE(resp),
1119 SI476X_DEFAULT_TIMEOUT);
1120}
1121
1122static int si476x_core_cmd_power_down_a20(struct si476x_core *core,
1123 struct si476x_power_down_args *pdargs)
1124{
1125 u8 resp[CMD_POWER_DOWN_A20_NRESP];
1126 const u8 args[CMD_POWER_DOWN_A20_NARGS] = {
1127 pdargs->xosc,
1128 };
1129 return si476x_core_send_command(core, CMD_POWER_DOWN,
1130 args, ARRAY_SIZE(args),
1131 resp, ARRAY_SIZE(resp),
1132 SI476X_DEFAULT_TIMEOUT);
1133}
1134
1135static int si476x_core_cmd_am_tune_freq_a10(struct si476x_core *core,
1136 struct si476x_tune_freq_args *tuneargs)
1137{
1138
1139 const int am_freq = tuneargs->freq;
1140 u8 resp[CMD_AM_TUNE_FREQ_NRESP];
1141 const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
1142 (tuneargs->hd << 6),
1143 msb(am_freq),
1144 lsb(am_freq),
1145 };
1146
1147 return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, args,
1148 sizeof(args),
1149 resp, sizeof(resp));
1150}
1151
1152static int si476x_core_cmd_am_tune_freq_a20(struct si476x_core *core,
1153 struct si476x_tune_freq_args *tuneargs)
1154{
1155 const int am_freq = tuneargs->freq;
1156 u8 resp[CMD_AM_TUNE_FREQ_NRESP];
1157 const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
b0222afa 1158 (tuneargs->zifsr << 6) | (tuneargs->injside & 0x03),
ed4a8fe8
AS
1159 msb(am_freq),
1160 lsb(am_freq),
1161 };
1162
1163 return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ,
1164 args, sizeof(args),
1165 resp, sizeof(resp));
1166}
1167
1168static int si476x_core_cmd_fm_rsq_status_a10(struct si476x_core *core,
1169 struct si476x_rsq_status_args *rsqargs,
1170 struct si476x_rsq_status_report *report)
1171{
1172 int err;
1173 u8 resp[CMD_FM_RSQ_STATUS_A10_NRESP];
1174 const u8 args[CMD_FM_RSQ_STATUS_A10_NARGS] = {
1175 rsqargs->rsqack << 3 | rsqargs->attune << 2 |
1176 rsqargs->cancel << 1 | rsqargs->stcack,
1177 };
1178
1179 err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1180 args, ARRAY_SIZE(args),
1181 resp, ARRAY_SIZE(resp),
1182 SI476X_DEFAULT_TIMEOUT);
1183 /*
1184 * Besides getting received signal quality information this
1185 * command can be used to just acknowledge different interrupt
1186 * flags in those cases it is useless to copy and parse
1187 * received data so user can pass NULL, and thus avoid
1188 * unnecessary copying.
1189 */
1190 if (err < 0 || report == NULL)
1191 return err;
1192
b0222afa
GU
1193 report->multhint = 0x80 & resp[1];
1194 report->multlint = 0x40 & resp[1];
1195 report->snrhint = 0x08 & resp[1];
1196 report->snrlint = 0x04 & resp[1];
1197 report->rssihint = 0x02 & resp[1];
1198 report->rssilint = 0x01 & resp[1];
ed4a8fe8 1199
b0222afa
GU
1200 report->bltf = 0x80 & resp[2];
1201 report->snr_ready = 0x20 & resp[2];
1202 report->rssiready = 0x08 & resp[2];
1203 report->afcrl = 0x02 & resp[2];
1204 report->valid = 0x01 & resp[2];
ed4a8fe8 1205
151978bf 1206 report->readfreq = get_unaligned_be16(resp + 3);
ed4a8fe8
AS
1207 report->freqoff = resp[5];
1208 report->rssi = resp[6];
1209 report->snr = resp[7];
1210 report->lassi = resp[9];
1211 report->hassi = resp[10];
1212 report->mult = resp[11];
1213 report->dev = resp[12];
151978bf 1214 report->readantcap = get_unaligned_be16(resp + 13);
ed4a8fe8
AS
1215 report->assi = resp[15];
1216 report->usn = resp[16];
1217
1218 return err;
1219}
1220
1221static int si476x_core_cmd_fm_rsq_status_a20(struct si476x_core *core,
d69d4212
LJ
1222 struct si476x_rsq_status_args *rsqargs,
1223 struct si476x_rsq_status_report *report)
ed4a8fe8
AS
1224{
1225 int err;
1226 u8 resp[CMD_FM_RSQ_STATUS_A10_NRESP];
1227 const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
1228 rsqargs->primary << 4 | rsqargs->rsqack << 3 |
1229 rsqargs->attune << 2 | rsqargs->cancel << 1 |
1230 rsqargs->stcack,
1231 };
1232
1233 err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1234 args, ARRAY_SIZE(args),
1235 resp, ARRAY_SIZE(resp),
1236 SI476X_DEFAULT_TIMEOUT);
1237 /*
1238 * Besides getting received signal quality information this
1239 * command can be used to just acknowledge different interrupt
1240 * flags in those cases it is useless to copy and parse
1241 * received data so user can pass NULL, and thus avoid
1242 * unnecessary copying.
1243 */
1244 if (err < 0 || report == NULL)
1245 return err;
1246
b0222afa
GU
1247 report->multhint = 0x80 & resp[1];
1248 report->multlint = 0x40 & resp[1];
1249 report->snrhint = 0x08 & resp[1];
1250 report->snrlint = 0x04 & resp[1];
1251 report->rssihint = 0x02 & resp[1];
1252 report->rssilint = 0x01 & resp[1];
ed4a8fe8 1253
b0222afa
GU
1254 report->bltf = 0x80 & resp[2];
1255 report->snr_ready = 0x20 & resp[2];
1256 report->rssiready = 0x08 & resp[2];
1257 report->afcrl = 0x02 & resp[2];
1258 report->valid = 0x01 & resp[2];
ed4a8fe8 1259
151978bf 1260 report->readfreq = get_unaligned_be16(resp + 3);
ed4a8fe8
AS
1261 report->freqoff = resp[5];
1262 report->rssi = resp[6];
1263 report->snr = resp[7];
1264 report->lassi = resp[9];
1265 report->hassi = resp[10];
1266 report->mult = resp[11];
1267 report->dev = resp[12];
151978bf 1268 report->readantcap = get_unaligned_be16(resp + 13);
ed4a8fe8
AS
1269 report->assi = resp[15];
1270 report->usn = resp[16];
1271
1272 return err;
1273}
1274
1275
1276static int si476x_core_cmd_fm_rsq_status_a30(struct si476x_core *core,
1277 struct si476x_rsq_status_args *rsqargs,
1278 struct si476x_rsq_status_report *report)
1279{
1280 int err;
1281 u8 resp[CMD_FM_RSQ_STATUS_A30_NRESP];
1282 const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
1283 rsqargs->primary << 4 | rsqargs->rsqack << 3 |
1284 rsqargs->attune << 2 | rsqargs->cancel << 1 |
1285 rsqargs->stcack,
1286 };
1287
1288 err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1289 args, ARRAY_SIZE(args),
1290 resp, ARRAY_SIZE(resp),
1291 SI476X_DEFAULT_TIMEOUT);
1292 /*
1293 * Besides getting received signal quality information this
1294 * command can be used to just acknowledge different interrupt
1295 * flags in those cases it is useless to copy and parse
1296 * received data so user can pass NULL, and thus avoid
1297 * unnecessary copying.
1298 */
1299 if (err < 0 || report == NULL)
1300 return err;
1301
b0222afa
GU
1302 report->multhint = 0x80 & resp[1];
1303 report->multlint = 0x40 & resp[1];
1304 report->snrhint = 0x08 & resp[1];
1305 report->snrlint = 0x04 & resp[1];
1306 report->rssihint = 0x02 & resp[1];
1307 report->rssilint = 0x01 & resp[1];
1308
1309 report->bltf = 0x80 & resp[2];
1310 report->snr_ready = 0x20 & resp[2];
1311 report->rssiready = 0x08 & resp[2];
1312 report->injside = 0x04 & resp[2];
1313 report->afcrl = 0x02 & resp[2];
1314 report->valid = 0x01 & resp[2];
ed4a8fe8 1315
151978bf 1316 report->readfreq = get_unaligned_be16(resp + 3);
ed4a8fe8
AS
1317 report->freqoff = resp[5];
1318 report->rssi = resp[6];
1319 report->snr = resp[7];
1320 report->issi = resp[8];
1321 report->lassi = resp[9];
1322 report->hassi = resp[10];
1323 report->mult = resp[11];
1324 report->dev = resp[12];
151978bf 1325 report->readantcap = get_unaligned_be16(resp + 13);
ed4a8fe8
AS
1326 report->assi = resp[15];
1327 report->usn = resp[16];
1328
1329 report->pilotdev = resp[17];
1330 report->rdsdev = resp[18];
1331 report->assidev = resp[19];
1332 report->strongdev = resp[20];
151978bf 1333 report->rdspi = get_unaligned_be16(resp + 21);
ed4a8fe8
AS
1334
1335 return err;
1336}
1337
1338static int si476x_core_cmd_fm_tune_freq_a10(struct si476x_core *core,
1339 struct si476x_tune_freq_args *tuneargs)
1340{
1341 u8 resp[CMD_FM_TUNE_FREQ_NRESP];
1342 const u8 args[CMD_FM_TUNE_FREQ_A10_NARGS] = {
1343 (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
1344 | (tuneargs->smoothmetrics << 2),
1345 msb(tuneargs->freq),
1346 lsb(tuneargs->freq),
1347 msb(tuneargs->antcap),
1348 lsb(tuneargs->antcap)
1349 };
1350
1351 return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
1352 args, sizeof(args),
1353 resp, sizeof(resp));
1354}
1355
1356static int si476x_core_cmd_fm_tune_freq_a20(struct si476x_core *core,
1357 struct si476x_tune_freq_args *tuneargs)
1358{
1359 u8 resp[CMD_FM_TUNE_FREQ_NRESP];
1360 const u8 args[CMD_FM_TUNE_FREQ_A20_NARGS] = {
1361 (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
1362 | (tuneargs->smoothmetrics << 2) | (tuneargs->injside),
1363 msb(tuneargs->freq),
1364 lsb(tuneargs->freq),
1365 };
1366
1367 return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
1368 args, sizeof(args),
1369 resp, sizeof(resp));
1370}
1371
1372static int si476x_core_cmd_agc_status_a20(struct si476x_core *core,
1373 struct si476x_agc_status_report *report)
1374{
1375 int err;
1376 u8 resp[CMD_AGC_STATUS_NRESP_A20];
1377
1378 if (!report)
1379 return -EINVAL;
1380
1381 err = si476x_core_send_command(core, CMD_AGC_STATUS,
1382 NULL, 0,
1383 resp, ARRAY_SIZE(resp),
1384 SI476X_DEFAULT_TIMEOUT);
1385 if (err < 0)
1386 return err;
1387
1388 report->mxhi = resp[1] & SI476X_AGC_MXHI;
1389 report->mxlo = resp[1] & SI476X_AGC_MXLO;
1390 report->lnahi = resp[1] & SI476X_AGC_LNAHI;
1391 report->lnalo = resp[1] & SI476X_AGC_LNALO;
1392 report->fmagc1 = resp[2];
1393 report->fmagc2 = resp[3];
1394 report->pgagain = resp[4];
1395 report->fmwblang = resp[5];
1396
1397 return err;
1398}
1399
1400static int si476x_core_cmd_agc_status_a10(struct si476x_core *core,
1401 struct si476x_agc_status_report *report)
1402{
1403 int err;
1404 u8 resp[CMD_AGC_STATUS_NRESP_A10];
1405
1406 if (!report)
1407 return -EINVAL;
1408
1409 err = si476x_core_send_command(core, CMD_AGC_STATUS,
1410 NULL, 0,
1411 resp, ARRAY_SIZE(resp),
1412 SI476X_DEFAULT_TIMEOUT);
1413 if (err < 0)
1414 return err;
1415
1416 report->mxhi = resp[1] & SI476X_AGC_MXHI;
1417 report->mxlo = resp[1] & SI476X_AGC_MXLO;
1418 report->lnahi = resp[1] & SI476X_AGC_LNAHI;
1419 report->lnalo = resp[1] & SI476X_AGC_LNALO;
1420
1421 return err;
1422}
1423
1424typedef int (*tune_freq_func_t) (struct si476x_core *core,
1425 struct si476x_tune_freq_args *tuneargs);
1426
1427static struct {
d69d4212
LJ
1428 int (*power_up)(struct si476x_core *,
1429 struct si476x_power_up_args *);
1430 int (*power_down)(struct si476x_core *,
1431 struct si476x_power_down_args *);
ed4a8fe8
AS
1432
1433 tune_freq_func_t fm_tune_freq;
1434 tune_freq_func_t am_tune_freq;
1435
1436 int (*fm_rsq_status)(struct si476x_core *,
1437 struct si476x_rsq_status_args *,
1438 struct si476x_rsq_status_report *);
1439
1440 int (*agc_status)(struct si476x_core *,
1441 struct si476x_agc_status_report *);
1442 int (*intb_pin_cfg)(struct si476x_core *core,
1443 enum si476x_intb_config intb,
1444 enum si476x_a1_config a1);
1445} si476x_cmds_vtable[] = {
1446 [SI476X_REVISION_A10] = {
1447 .power_up = si476x_core_cmd_power_up_a10,
1448 .power_down = si476x_core_cmd_power_down_a10,
1449 .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a10,
1450 .am_tune_freq = si476x_core_cmd_am_tune_freq_a10,
1451 .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a10,
1452 .agc_status = si476x_core_cmd_agc_status_a10,
1453 .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a10,
1454 },
1455 [SI476X_REVISION_A20] = {
1456 .power_up = si476x_core_cmd_power_up_a20,
1457 .power_down = si476x_core_cmd_power_down_a20,
1458 .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a20,
1459 .am_tune_freq = si476x_core_cmd_am_tune_freq_a20,
1460 .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a20,
1461 .agc_status = si476x_core_cmd_agc_status_a20,
1462 .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a20,
1463 },
1464 [SI476X_REVISION_A30] = {
1465 .power_up = si476x_core_cmd_power_up_a20,
1466 .power_down = si476x_core_cmd_power_down_a20,
1467 .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a20,
1468 .am_tune_freq = si476x_core_cmd_am_tune_freq_a20,
1469 .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a30,
1470 .agc_status = si476x_core_cmd_agc_status_a20,
1471 .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a20,
1472 },
1473};
1474
1475int si476x_core_cmd_power_up(struct si476x_core *core,
1476 struct si476x_power_up_args *args)
1477{
1478 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1479 core->revision == -1);
1480 return si476x_cmds_vtable[core->revision].power_up(core, args);
1481}
1482EXPORT_SYMBOL_GPL(si476x_core_cmd_power_up);
1483
1484int si476x_core_cmd_power_down(struct si476x_core *core,
1485 struct si476x_power_down_args *args)
1486{
1487 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1488 core->revision == -1);
1489 return si476x_cmds_vtable[core->revision].power_down(core, args);
1490}
1491EXPORT_SYMBOL_GPL(si476x_core_cmd_power_down);
1492
1493int si476x_core_cmd_fm_tune_freq(struct si476x_core *core,
1494 struct si476x_tune_freq_args *args)
1495{
1496 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1497 core->revision == -1);
1498 return si476x_cmds_vtable[core->revision].fm_tune_freq(core, args);
1499}
1500EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_tune_freq);
1501
1502int si476x_core_cmd_am_tune_freq(struct si476x_core *core,
1503 struct si476x_tune_freq_args *args)
1504{
1505 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1506 core->revision == -1);
1507 return si476x_cmds_vtable[core->revision].am_tune_freq(core, args);
1508}
1509EXPORT_SYMBOL_GPL(si476x_core_cmd_am_tune_freq);
1510
1511int si476x_core_cmd_fm_rsq_status(struct si476x_core *core,
1512 struct si476x_rsq_status_args *args,
1513 struct si476x_rsq_status_report *report)
1514
1515{
1516 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1517 core->revision == -1);
1518 return si476x_cmds_vtable[core->revision].fm_rsq_status(core, args,
1519 report);
1520}
1521EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rsq_status);
1522
1523int si476x_core_cmd_agc_status(struct si476x_core *core,
1524 struct si476x_agc_status_report *report)
1525
1526{
1527 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1528 core->revision == -1);
1529 return si476x_cmds_vtable[core->revision].agc_status(core, report);
1530}
1531EXPORT_SYMBOL_GPL(si476x_core_cmd_agc_status);
1532
1533int si476x_core_cmd_intb_pin_cfg(struct si476x_core *core,
1534 enum si476x_intb_config intb,
1535 enum si476x_a1_config a1)
1536{
1537 BUG_ON(core->revision > SI476X_REVISION_A30 ||
1538 core->revision == -1);
1539
1540 return si476x_cmds_vtable[core->revision].intb_pin_cfg(core, intb, a1);
1541}
1542EXPORT_SYMBOL_GPL(si476x_core_cmd_intb_pin_cfg);
1543
1544MODULE_LICENSE("GPL");
1545MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
1546MODULE_DESCRIPTION("API for command exchange for si476x");