drm/vc4: txp: Protect device resources
[platform/kernel/linux-starfive.git] / drivers / mfd / si476x-cmd.c
1 // SPDX-License-Identifier: GPL-2.0-only
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>
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
23 #include <asm/unaligned.h>
24
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
138 enum 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),
146         SI476X_ACF_SMATTN       = 0x1f,
147         SI476X_ACF_PILOT        = (1 << 7),
148         SI476X_ACF_STBLEND      = ~SI476X_ACF_PILOT,
149 };
150
151 enum 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
158 enum 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
171 static 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  * @resp:     buffer to place the expected response from the device
250  * @respn:    actual size of @resp
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  */
257 static 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;
343 exit:
344         return err;
345 }
346
347 static 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
372 static 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_core_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  */
404 int 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 }
424 EXPORT_SYMBOL_GPL(si476x_core_cmd_func_info);
425
426 /**
427  * si476x_core_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  */
435 int 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 }
452 EXPORT_SYMBOL_GPL(si476x_core_cmd_set_property);
453
454 /**
455  * si476x_core_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  */
462 int 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
479                 return get_unaligned_be16(resp + 2);
480 }
481 EXPORT_SYMBOL_GPL(si476x_core_cmd_get_property);
482
483 /**
484  * si476x_core_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  */
520 int 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 }
539 EXPORT_SYMBOL_GPL(si476x_core_cmd_dig_audio_pin_cfg);
540
541 /**
542  * si476x_core_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  */
569 int 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 }
588 EXPORT_SYMBOL_GPL(si476x_core_cmd_zif_pin_cfg);
589
590 /**
591  * si476x_core_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  */
626 int 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 }
645 EXPORT_SYMBOL_GPL(si476x_core_cmd_ic_link_gpo_ctl_pin_cfg);
646
647 /**
648  * si476x_core_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  */
660 int 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 }
673 EXPORT_SYMBOL_GPL(si476x_core_cmd_ana_audio_pin_cfg);
674
675
676 /**
677  * si476x_core_cmd_intb_pin_cfg_a10 - 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  */
694 static 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
710 static 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_core_cmd_am_rsq_status - send 'AM_RSQ_STATUS' command to the
730  * device
731  * @core:  - device to send the command to
732  * @rsqargs: - pointer to a structure containing a group of sub-args
733  *             relevant to sending the RSQ status command
734  * @report: - all signal quality information returned by the command
735  *           (if NULL then the output of the command is ignored)
736  *
737  * Function returns 0 on success and negative error code on failure
738  */
739 int si476x_core_cmd_am_rsq_status(struct si476x_core *core,
740                                   struct si476x_rsq_status_args *rsqargs,
741                                   struct si476x_rsq_status_report *report)
742 {
743         int err;
744         u8       resp[CMD_AM_RSQ_STATUS_NRESP];
745         const u8 args[CMD_AM_RSQ_STATUS_NARGS] = {
746                 rsqargs->rsqack << 3 | rsqargs->attune << 2 |
747                 rsqargs->cancel << 1 | rsqargs->stcack,
748         };
749
750         err = si476x_core_send_command(core, CMD_AM_RSQ_STATUS,
751                                        args, ARRAY_SIZE(args),
752                                        resp, ARRAY_SIZE(resp),
753                                        SI476X_DEFAULT_TIMEOUT);
754         /*
755          * Besides getting received signal quality information this
756          * command can be used to just acknowledge different interrupt
757          * flags in those cases it is useless to copy and parse
758          * received data so user can pass NULL, and thus avoid
759          * unnecessary copying.
760          */
761         if (!report)
762                 return err;
763
764         report->snrhint         = 0x08 & resp[1];
765         report->snrlint         = 0x04 & resp[1];
766         report->rssihint        = 0x02 & resp[1];
767         report->rssilint        = 0x01 & resp[1];
768
769         report->bltf            = 0x80 & resp[2];
770         report->snr_ready       = 0x20 & resp[2];
771         report->rssiready       = 0x08 & resp[2];
772         report->afcrl           = 0x02 & resp[2];
773         report->valid           = 0x01 & resp[2];
774
775         report->readfreq        = get_unaligned_be16(resp + 3);
776         report->freqoff         = resp[5];
777         report->rssi            = resp[6];
778         report->snr             = resp[7];
779         report->lassi           = resp[9];
780         report->hassi           = resp[10];
781         report->mult            = resp[11];
782         report->dev             = resp[12];
783
784         return err;
785 }
786 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_rsq_status);
787
788 int si476x_core_cmd_fm_acf_status(struct si476x_core *core,
789                              struct si476x_acf_status_report *report)
790 {
791         int err;
792         u8       resp[CMD_FM_ACF_STATUS_NRESP];
793         const u8 args[CMD_FM_ACF_STATUS_NARGS] = {
794                 0x0,
795         };
796
797         if (!report)
798                 return -EINVAL;
799
800         err = si476x_core_send_command(core, CMD_FM_ACF_STATUS,
801                                        args, ARRAY_SIZE(args),
802                                        resp, ARRAY_SIZE(resp),
803                                        SI476X_DEFAULT_TIMEOUT);
804         if (err < 0)
805                 return err;
806
807         report->blend_int       = resp[1] & SI476X_ACF_BLEND_INT;
808         report->hblend_int      = resp[1] & SI476X_ACF_HIBLEND_INT;
809         report->hicut_int       = resp[1] & SI476X_ACF_HICUT_INT;
810         report->chbw_int        = resp[1] & SI476X_ACF_CHBW_INT;
811         report->softmute_int    = resp[1] & SI476X_ACF_SOFTMUTE_INT;
812         report->smute           = resp[2] & SI476X_ACF_SMUTE;
813         report->smattn          = resp[3] & SI476X_ACF_SMATTN;
814         report->chbw            = resp[4];
815         report->hicut           = resp[5];
816         report->hiblend         = resp[6];
817         report->pilot           = resp[7] & SI476X_ACF_PILOT;
818         report->stblend         = resp[7] & SI476X_ACF_STBLEND;
819
820         return err;
821 }
822 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_acf_status);
823
824 int si476x_core_cmd_am_acf_status(struct si476x_core *core,
825                                   struct si476x_acf_status_report *report)
826 {
827         int err;
828         u8       resp[CMD_AM_ACF_STATUS_NRESP];
829         const u8 args[CMD_AM_ACF_STATUS_NARGS] = {
830                 0x0,
831         };
832
833         if (!report)
834                 return -EINVAL;
835
836         err = si476x_core_send_command(core, CMD_AM_ACF_STATUS,
837                                        args, ARRAY_SIZE(args),
838                                        resp, ARRAY_SIZE(resp),
839                                        SI476X_DEFAULT_TIMEOUT);
840         if (err < 0)
841                 return err;
842
843         report->blend_int       = resp[1] & SI476X_ACF_BLEND_INT;
844         report->hblend_int      = resp[1] & SI476X_ACF_HIBLEND_INT;
845         report->hicut_int       = resp[1] & SI476X_ACF_HICUT_INT;
846         report->chbw_int        = resp[1] & SI476X_ACF_CHBW_INT;
847         report->softmute_int    = resp[1] & SI476X_ACF_SOFTMUTE_INT;
848         report->smute           = resp[2] & SI476X_ACF_SMUTE;
849         report->smattn          = resp[3] & SI476X_ACF_SMATTN;
850         report->chbw            = resp[4];
851         report->hicut           = resp[5];
852
853         return err;
854 }
855 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_acf_status);
856
857
858 /**
859  * si476x_core_cmd_fm_seek_start - send 'FM_SEEK_START' command to the
860  * device
861  * @core:  - device to send the command to
862  * @seekup: - if set the direction of the search is 'up'
863  * @wrap:   - if set seek wraps when hitting band limit
864  *
865  * This function begins search for a valid station. The station is
866  * considered valid when 'FM_VALID_SNR_THRESHOLD' and
867  * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
868  * are met.
869 } *
870  * Function returns 0 on success and negative error code on failure
871  */
872 int si476x_core_cmd_fm_seek_start(struct si476x_core *core,
873                                   bool seekup, bool wrap)
874 {
875         u8       resp[CMD_FM_SEEK_START_NRESP];
876         const u8 args[CMD_FM_SEEK_START_NARGS] = {
877                 seekup << 3 | wrap << 2,
878         };
879
880         return si476x_cmd_tune_seek_freq(core, CMD_FM_SEEK_START,
881                                          args, sizeof(args),
882                                          resp, sizeof(resp));
883 }
884 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_seek_start);
885
886 /**
887  * si476x_core_cmd_fm_rds_status - send 'FM_RDS_STATUS' command to the
888  * device
889  * @core: - device to send the command to
890  * @status_only: - if set the data is not removed from RDSFIFO,
891  *                RDSFIFOUSED is not decremented and data in all the
892  *                rest RDS data contains the last valid info received
893  * @mtfifo: if set the command clears RDS receive FIFO
894  * @intack: if set the command clards the RDSINT bit.
895  * @report: - all signal quality information returned by the command
896  *           (if NULL then the output of the command is ignored)
897  *
898  * Function returns 0 on success and negative error code on failure
899  */
900 int si476x_core_cmd_fm_rds_status(struct si476x_core *core,
901                                   bool status_only,
902                                   bool mtfifo,
903                                   bool intack,
904                                   struct si476x_rds_status_report *report)
905 {
906         int err;
907         u8       resp[CMD_FM_RDS_STATUS_NRESP];
908         const u8 args[CMD_FM_RDS_STATUS_NARGS] = {
909                 status_only << 2 | mtfifo << 1 | intack,
910         };
911
912         err = si476x_core_send_command(core, CMD_FM_RDS_STATUS,
913                                        args, ARRAY_SIZE(args),
914                                        resp, ARRAY_SIZE(resp),
915                                        SI476X_DEFAULT_TIMEOUT);
916         /*
917          * Besides getting RDS status information this command can be
918          * used to just acknowledge different interrupt flags in those
919          * cases it is useless to copy and parse received data so user
920          * can pass NULL, and thus avoid unnecessary copying.
921          */
922         if (err < 0 || report == NULL)
923                 return err;
924
925         report->rdstpptyint     = 0x10 & resp[1];
926         report->rdspiint        = 0x08 & resp[1];
927         report->rdssyncint      = 0x02 & resp[1];
928         report->rdsfifoint      = 0x01 & resp[1];
929
930         report->tpptyvalid      = 0x10 & resp[2];
931         report->pivalid         = 0x08 & resp[2];
932         report->rdssync         = 0x02 & resp[2];
933         report->rdsfifolost     = 0x01 & resp[2];
934
935         report->tp              = 0x20 & resp[3];
936         report->pty             = 0x1f & resp[3];
937
938         report->pi              = get_unaligned_be16(resp + 4);
939         report->rdsfifoused     = resp[6];
940
941         report->ble[V4L2_RDS_BLOCK_A]   = 0xc0 & resp[7];
942         report->ble[V4L2_RDS_BLOCK_B]   = 0x30 & resp[7];
943         report->ble[V4L2_RDS_BLOCK_C]   = 0x0c & resp[7];
944         report->ble[V4L2_RDS_BLOCK_D]   = 0x03 & resp[7];
945
946         report->rds[V4L2_RDS_BLOCK_A].block = V4L2_RDS_BLOCK_A;
947         report->rds[V4L2_RDS_BLOCK_A].msb = resp[8];
948         report->rds[V4L2_RDS_BLOCK_A].lsb = resp[9];
949
950         report->rds[V4L2_RDS_BLOCK_B].block = V4L2_RDS_BLOCK_B;
951         report->rds[V4L2_RDS_BLOCK_B].msb = resp[10];
952         report->rds[V4L2_RDS_BLOCK_B].lsb = resp[11];
953
954         report->rds[V4L2_RDS_BLOCK_C].block = V4L2_RDS_BLOCK_C;
955         report->rds[V4L2_RDS_BLOCK_C].msb = resp[12];
956         report->rds[V4L2_RDS_BLOCK_C].lsb = resp[13];
957
958         report->rds[V4L2_RDS_BLOCK_D].block = V4L2_RDS_BLOCK_D;
959         report->rds[V4L2_RDS_BLOCK_D].msb = resp[14];
960         report->rds[V4L2_RDS_BLOCK_D].lsb = resp[15];
961
962         return err;
963 }
964 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_status);
965
966 int si476x_core_cmd_fm_rds_blockcount(struct si476x_core *core,
967                                 bool clear,
968                                 struct si476x_rds_blockcount_report *report)
969 {
970         int err;
971         u8       resp[CMD_FM_RDS_BLOCKCOUNT_NRESP];
972         const u8 args[CMD_FM_RDS_BLOCKCOUNT_NARGS] = {
973                 clear,
974         };
975
976         if (!report)
977                 return -EINVAL;
978
979         err = si476x_core_send_command(core, CMD_FM_RDS_BLOCKCOUNT,
980                                        args, ARRAY_SIZE(args),
981                                        resp, ARRAY_SIZE(resp),
982                                        SI476X_DEFAULT_TIMEOUT);
983
984         if (!err) {
985                 report->expected        = get_unaligned_be16(resp + 2);
986                 report->received        = get_unaligned_be16(resp + 4);
987                 report->uncorrectable   = get_unaligned_be16(resp + 6);
988         }
989
990         return err;
991 }
992 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_blockcount);
993
994 int si476x_core_cmd_fm_phase_diversity(struct si476x_core *core,
995                                        enum si476x_phase_diversity_mode mode)
996 {
997         u8       resp[CMD_FM_PHASE_DIVERSITY_NRESP];
998         const u8 args[CMD_FM_PHASE_DIVERSITY_NARGS] = {
999                 mode & 0x07,
1000         };
1001
1002         return si476x_core_send_command(core, CMD_FM_PHASE_DIVERSITY,
1003                                         args, ARRAY_SIZE(args),
1004                                         resp, ARRAY_SIZE(resp),
1005                                         SI476X_DEFAULT_TIMEOUT);
1006 }
1007 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_diversity);
1008 /**
1009  * si476x_core_cmd_fm_phase_div_status() - get the phase diversity
1010  * status
1011  *
1012  * @core: si476x device
1013  *
1014  * NOTE caller must hold core lock
1015  *
1016  * Function returns the value of the status bit in case of success and
1017  * negative error code in case of failre.
1018  */
1019 int si476x_core_cmd_fm_phase_div_status(struct si476x_core *core)
1020 {
1021         int err;
1022         u8 resp[CMD_FM_PHASE_DIV_STATUS_NRESP];
1023
1024         err = si476x_core_send_command(core, CMD_FM_PHASE_DIV_STATUS,
1025                                        NULL, 0,
1026                                        resp, ARRAY_SIZE(resp),
1027                                        SI476X_DEFAULT_TIMEOUT);
1028
1029         return (err < 0) ? err : resp[1];
1030 }
1031 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_div_status);
1032
1033
1034 /**
1035  * si476x_core_cmd_am_seek_start - send 'FM_SEEK_START' command to the
1036  * device
1037  * @core:  - device to send the command to
1038  * @seekup: - if set the direction of the search is 'up'
1039  * @wrap:   - if set seek wraps when hitting band limit
1040  *
1041  * This function begins search for a valid station. The station is
1042  * considered valid when 'FM_VALID_SNR_THRESHOLD' and
1043  * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria
1044  * are met.
1045  *
1046  * Function returns 0 on success and negative error code on failure
1047  */
1048 int si476x_core_cmd_am_seek_start(struct si476x_core *core,
1049                                   bool seekup, bool wrap)
1050 {
1051         u8       resp[CMD_AM_SEEK_START_NRESP];
1052         const u8 args[CMD_AM_SEEK_START_NARGS] = {
1053                 seekup << 3 | wrap << 2,
1054         };
1055
1056         return si476x_cmd_tune_seek_freq(core,  CMD_AM_SEEK_START,
1057                                          args, sizeof(args),
1058                                          resp, sizeof(resp));
1059 }
1060 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_seek_start);
1061
1062
1063
1064 static int si476x_core_cmd_power_up_a10(struct si476x_core *core,
1065                                         struct si476x_power_up_args *puargs)
1066 {
1067         u8       resp[CMD_POWER_UP_A10_NRESP];
1068         const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
1069         const bool ctsen  = (core->client->irq != 0);
1070         const u8 args[CMD_POWER_UP_A10_NARGS] = {
1071                 0xF7,           /* Reserved, always 0xF7 */
1072                 0x3F & puargs->xcload,  /* First two bits are reserved to be
1073                                  * zeros */
1074                 ctsen << 7 | intsel << 6 | 0x07, /* Last five bits
1075                                                    * are reserved to
1076                                                    * be written as 0x7 */
1077                 puargs->func << 4 | puargs->freq,
1078                 0x11,           /* Reserved, always 0x11 */
1079         };
1080
1081         return si476x_core_send_command(core, CMD_POWER_UP,
1082                                         args, ARRAY_SIZE(args),
1083                                         resp, ARRAY_SIZE(resp),
1084                                         SI476X_TIMEOUT_POWER_UP);
1085 }
1086
1087 static int si476x_core_cmd_power_up_a20(struct si476x_core *core,
1088                                  struct si476x_power_up_args *puargs)
1089 {
1090         u8       resp[CMD_POWER_UP_A20_NRESP];
1091         const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ);
1092         const bool ctsen  = (core->client->irq != 0);
1093         const u8 args[CMD_POWER_UP_A20_NARGS] = {
1094                 puargs->ibias6x << 7 | puargs->xstart,
1095                 0x3F & puargs->xcload,  /* First two bits are reserved to be
1096                                          * zeros */
1097                 ctsen << 7 | intsel << 6 | puargs->fastboot << 5 |
1098                 puargs->xbiashc << 3 | puargs->xbias,
1099                 puargs->func << 4 | puargs->freq,
1100                 0x10 | puargs->xmode,
1101         };
1102
1103         return si476x_core_send_command(core, CMD_POWER_UP,
1104                                         args, ARRAY_SIZE(args),
1105                                         resp, ARRAY_SIZE(resp),
1106                                         SI476X_TIMEOUT_POWER_UP);
1107 }
1108
1109 static int si476x_core_cmd_power_down_a10(struct si476x_core *core,
1110                                           struct si476x_power_down_args *pdargs)
1111 {
1112         u8 resp[CMD_POWER_DOWN_A10_NRESP];
1113
1114         return si476x_core_send_command(core, CMD_POWER_DOWN,
1115                                         NULL, 0,
1116                                         resp, ARRAY_SIZE(resp),
1117                                         SI476X_DEFAULT_TIMEOUT);
1118 }
1119
1120 static int si476x_core_cmd_power_down_a20(struct si476x_core *core,
1121                                           struct si476x_power_down_args *pdargs)
1122 {
1123         u8 resp[CMD_POWER_DOWN_A20_NRESP];
1124         const u8 args[CMD_POWER_DOWN_A20_NARGS] = {
1125                 pdargs->xosc,
1126         };
1127         return si476x_core_send_command(core, CMD_POWER_DOWN,
1128                                         args, ARRAY_SIZE(args),
1129                                         resp, ARRAY_SIZE(resp),
1130                                         SI476X_DEFAULT_TIMEOUT);
1131 }
1132
1133 static int si476x_core_cmd_am_tune_freq_a10(struct si476x_core *core,
1134                                         struct si476x_tune_freq_args *tuneargs)
1135 {
1136
1137         const int am_freq = tuneargs->freq;
1138         u8       resp[CMD_AM_TUNE_FREQ_NRESP];
1139         const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
1140                 (tuneargs->hd << 6),
1141                 msb(am_freq),
1142                 lsb(am_freq),
1143         };
1144
1145         return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, args,
1146                                          sizeof(args),
1147                                          resp, sizeof(resp));
1148 }
1149
1150 static int si476x_core_cmd_am_tune_freq_a20(struct si476x_core *core,
1151                                         struct si476x_tune_freq_args *tuneargs)
1152 {
1153         const int am_freq = tuneargs->freq;
1154         u8       resp[CMD_AM_TUNE_FREQ_NRESP];
1155         const u8 args[CMD_AM_TUNE_FREQ_NARGS] = {
1156                 (tuneargs->zifsr << 6) | (tuneargs->injside & 0x03),
1157                 msb(am_freq),
1158                 lsb(am_freq),
1159         };
1160
1161         return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ,
1162                                          args, sizeof(args),
1163                                          resp, sizeof(resp));
1164 }
1165
1166 static int si476x_core_cmd_fm_rsq_status_a10(struct si476x_core *core,
1167                                         struct si476x_rsq_status_args *rsqargs,
1168                                         struct si476x_rsq_status_report *report)
1169 {
1170         int err;
1171         u8       resp[CMD_FM_RSQ_STATUS_A10_NRESP];
1172         const u8 args[CMD_FM_RSQ_STATUS_A10_NARGS] = {
1173                 rsqargs->rsqack << 3 | rsqargs->attune << 2 |
1174                 rsqargs->cancel << 1 | rsqargs->stcack,
1175         };
1176
1177         err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1178                                        args, ARRAY_SIZE(args),
1179                                        resp, ARRAY_SIZE(resp),
1180                                        SI476X_DEFAULT_TIMEOUT);
1181         /*
1182          * Besides getting received signal quality information this
1183          * command can be used to just acknowledge different interrupt
1184          * flags in those cases it is useless to copy and parse
1185          * received data so user can pass NULL, and thus avoid
1186          * unnecessary copying.
1187          */
1188         if (err < 0 || report == NULL)
1189                 return err;
1190
1191         report->multhint        = 0x80 & resp[1];
1192         report->multlint        = 0x40 & resp[1];
1193         report->snrhint         = 0x08 & resp[1];
1194         report->snrlint         = 0x04 & resp[1];
1195         report->rssihint        = 0x02 & resp[1];
1196         report->rssilint        = 0x01 & resp[1];
1197
1198         report->bltf            = 0x80 & resp[2];
1199         report->snr_ready       = 0x20 & resp[2];
1200         report->rssiready       = 0x08 & resp[2];
1201         report->afcrl           = 0x02 & resp[2];
1202         report->valid           = 0x01 & resp[2];
1203
1204         report->readfreq        = get_unaligned_be16(resp + 3);
1205         report->freqoff         = resp[5];
1206         report->rssi            = resp[6];
1207         report->snr             = resp[7];
1208         report->lassi           = resp[9];
1209         report->hassi           = resp[10];
1210         report->mult            = resp[11];
1211         report->dev             = resp[12];
1212         report->readantcap      = get_unaligned_be16(resp + 13);
1213         report->assi            = resp[15];
1214         report->usn             = resp[16];
1215
1216         return err;
1217 }
1218
1219 static int si476x_core_cmd_fm_rsq_status_a20(struct si476x_core *core,
1220                                      struct si476x_rsq_status_args *rsqargs,
1221                                      struct si476x_rsq_status_report *report)
1222 {
1223         int err;
1224         u8       resp[CMD_FM_RSQ_STATUS_A10_NRESP];
1225         const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
1226                 rsqargs->primary << 4 | rsqargs->rsqack << 3 |
1227                 rsqargs->attune  << 2 | rsqargs->cancel << 1 |
1228                 rsqargs->stcack,
1229         };
1230
1231         err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1232                                        args, ARRAY_SIZE(args),
1233                                        resp, ARRAY_SIZE(resp),
1234                                        SI476X_DEFAULT_TIMEOUT);
1235         /*
1236          * Besides getting received signal quality information this
1237          * command can be used to just acknowledge different interrupt
1238          * flags in those cases it is useless to copy and parse
1239          * received data so user can pass NULL, and thus avoid
1240          * unnecessary copying.
1241          */
1242         if (err < 0 || report == NULL)
1243                 return err;
1244
1245         report->multhint        = 0x80 & resp[1];
1246         report->multlint        = 0x40 & resp[1];
1247         report->snrhint         = 0x08 & resp[1];
1248         report->snrlint         = 0x04 & resp[1];
1249         report->rssihint        = 0x02 & resp[1];
1250         report->rssilint        = 0x01 & resp[1];
1251
1252         report->bltf            = 0x80 & resp[2];
1253         report->snr_ready       = 0x20 & resp[2];
1254         report->rssiready       = 0x08 & resp[2];
1255         report->afcrl           = 0x02 & resp[2];
1256         report->valid           = 0x01 & resp[2];
1257
1258         report->readfreq        = get_unaligned_be16(resp + 3);
1259         report->freqoff         = resp[5];
1260         report->rssi            = resp[6];
1261         report->snr             = resp[7];
1262         report->lassi           = resp[9];
1263         report->hassi           = resp[10];
1264         report->mult            = resp[11];
1265         report->dev             = resp[12];
1266         report->readantcap      = get_unaligned_be16(resp + 13);
1267         report->assi            = resp[15];
1268         report->usn             = resp[16];
1269
1270         return err;
1271 }
1272
1273
1274 static int si476x_core_cmd_fm_rsq_status_a30(struct si476x_core *core,
1275                                         struct si476x_rsq_status_args *rsqargs,
1276                                         struct si476x_rsq_status_report *report)
1277 {
1278         int err;
1279         u8       resp[CMD_FM_RSQ_STATUS_A30_NRESP];
1280         const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = {
1281                 rsqargs->primary << 4 | rsqargs->rsqack << 3 |
1282                 rsqargs->attune << 2 | rsqargs->cancel << 1 |
1283                 rsqargs->stcack,
1284         };
1285
1286         err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS,
1287                                        args, ARRAY_SIZE(args),
1288                                        resp, ARRAY_SIZE(resp),
1289                                        SI476X_DEFAULT_TIMEOUT);
1290         /*
1291          * Besides getting received signal quality information this
1292          * command can be used to just acknowledge different interrupt
1293          * flags in those cases it is useless to copy and parse
1294          * received data so user can pass NULL, and thus avoid
1295          * unnecessary copying.
1296          */
1297         if (err < 0 || report == NULL)
1298                 return err;
1299
1300         report->multhint        = 0x80 & resp[1];
1301         report->multlint        = 0x40 & resp[1];
1302         report->snrhint         = 0x08 & resp[1];
1303         report->snrlint         = 0x04 & resp[1];
1304         report->rssihint        = 0x02 & resp[1];
1305         report->rssilint        = 0x01 & resp[1];
1306
1307         report->bltf            = 0x80 & resp[2];
1308         report->snr_ready       = 0x20 & resp[2];
1309         report->rssiready       = 0x08 & resp[2];
1310         report->injside         = 0x04 & resp[2];
1311         report->afcrl           = 0x02 & resp[2];
1312         report->valid           = 0x01 & resp[2];
1313
1314         report->readfreq        = get_unaligned_be16(resp + 3);
1315         report->freqoff         = resp[5];
1316         report->rssi            = resp[6];
1317         report->snr             = resp[7];
1318         report->issi            = resp[8];
1319         report->lassi           = resp[9];
1320         report->hassi           = resp[10];
1321         report->mult            = resp[11];
1322         report->dev             = resp[12];
1323         report->readantcap      = get_unaligned_be16(resp + 13);
1324         report->assi            = resp[15];
1325         report->usn             = resp[16];
1326
1327         report->pilotdev        = resp[17];
1328         report->rdsdev          = resp[18];
1329         report->assidev         = resp[19];
1330         report->strongdev       = resp[20];
1331         report->rdspi           = get_unaligned_be16(resp + 21);
1332
1333         return err;
1334 }
1335
1336 static int si476x_core_cmd_fm_tune_freq_a10(struct si476x_core *core,
1337                                         struct si476x_tune_freq_args *tuneargs)
1338 {
1339         u8       resp[CMD_FM_TUNE_FREQ_NRESP];
1340         const u8 args[CMD_FM_TUNE_FREQ_A10_NARGS] = {
1341                 (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
1342                 | (tuneargs->smoothmetrics << 2),
1343                 msb(tuneargs->freq),
1344                 lsb(tuneargs->freq),
1345                 msb(tuneargs->antcap),
1346                 lsb(tuneargs->antcap)
1347         };
1348
1349         return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
1350                                          args, sizeof(args),
1351                                          resp, sizeof(resp));
1352 }
1353
1354 static int si476x_core_cmd_fm_tune_freq_a20(struct si476x_core *core,
1355                                         struct si476x_tune_freq_args *tuneargs)
1356 {
1357         u8       resp[CMD_FM_TUNE_FREQ_NRESP];
1358         const u8 args[CMD_FM_TUNE_FREQ_A20_NARGS] = {
1359                 (tuneargs->hd << 6) | (tuneargs->tunemode << 4)
1360                 |  (tuneargs->smoothmetrics << 2) | (tuneargs->injside),
1361                 msb(tuneargs->freq),
1362                 lsb(tuneargs->freq),
1363         };
1364
1365         return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ,
1366                                          args, sizeof(args),
1367                                          resp, sizeof(resp));
1368 }
1369
1370 static int si476x_core_cmd_agc_status_a20(struct si476x_core *core,
1371                                         struct si476x_agc_status_report *report)
1372 {
1373         int err;
1374         u8 resp[CMD_AGC_STATUS_NRESP_A20];
1375
1376         if (!report)
1377                 return -EINVAL;
1378
1379         err = si476x_core_send_command(core, CMD_AGC_STATUS,
1380                                        NULL, 0,
1381                                        resp, ARRAY_SIZE(resp),
1382                                        SI476X_DEFAULT_TIMEOUT);
1383         if (err < 0)
1384                 return err;
1385
1386         report->mxhi            = resp[1] & SI476X_AGC_MXHI;
1387         report->mxlo            = resp[1] & SI476X_AGC_MXLO;
1388         report->lnahi           = resp[1] & SI476X_AGC_LNAHI;
1389         report->lnalo           = resp[1] & SI476X_AGC_LNALO;
1390         report->fmagc1          = resp[2];
1391         report->fmagc2          = resp[3];
1392         report->pgagain         = resp[4];
1393         report->fmwblang        = resp[5];
1394
1395         return err;
1396 }
1397
1398 static int si476x_core_cmd_agc_status_a10(struct si476x_core *core,
1399                                         struct si476x_agc_status_report *report)
1400 {
1401         int err;
1402         u8 resp[CMD_AGC_STATUS_NRESP_A10];
1403
1404         if (!report)
1405                 return -EINVAL;
1406
1407         err = si476x_core_send_command(core, CMD_AGC_STATUS,
1408                                        NULL, 0,
1409                                        resp, ARRAY_SIZE(resp),
1410                                        SI476X_DEFAULT_TIMEOUT);
1411         if (err < 0)
1412                 return err;
1413
1414         report->mxhi    = resp[1] & SI476X_AGC_MXHI;
1415         report->mxlo    = resp[1] & SI476X_AGC_MXLO;
1416         report->lnahi   = resp[1] & SI476X_AGC_LNAHI;
1417         report->lnalo   = resp[1] & SI476X_AGC_LNALO;
1418
1419         return err;
1420 }
1421
1422 typedef int (*tune_freq_func_t) (struct si476x_core *core,
1423                                  struct si476x_tune_freq_args *tuneargs);
1424
1425 static struct {
1426         int (*power_up)(struct si476x_core *,
1427                         struct si476x_power_up_args *);
1428         int (*power_down)(struct si476x_core *,
1429                           struct si476x_power_down_args *);
1430
1431         tune_freq_func_t fm_tune_freq;
1432         tune_freq_func_t am_tune_freq;
1433
1434         int (*fm_rsq_status)(struct si476x_core *,
1435                              struct si476x_rsq_status_args *,
1436                              struct si476x_rsq_status_report *);
1437
1438         int (*agc_status)(struct si476x_core *,
1439                           struct si476x_agc_status_report *);
1440         int (*intb_pin_cfg)(struct si476x_core *core,
1441                             enum si476x_intb_config intb,
1442                             enum si476x_a1_config a1);
1443 } si476x_cmds_vtable[] = {
1444         [SI476X_REVISION_A10] = {
1445                 .power_up       = si476x_core_cmd_power_up_a10,
1446                 .power_down     = si476x_core_cmd_power_down_a10,
1447                 .fm_tune_freq   = si476x_core_cmd_fm_tune_freq_a10,
1448                 .am_tune_freq   = si476x_core_cmd_am_tune_freq_a10,
1449                 .fm_rsq_status  = si476x_core_cmd_fm_rsq_status_a10,
1450                 .agc_status     = si476x_core_cmd_agc_status_a10,
1451                 .intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a10,
1452         },
1453         [SI476X_REVISION_A20] = {
1454                 .power_up       = si476x_core_cmd_power_up_a20,
1455                 .power_down     = si476x_core_cmd_power_down_a20,
1456                 .fm_tune_freq   = si476x_core_cmd_fm_tune_freq_a20,
1457                 .am_tune_freq   = si476x_core_cmd_am_tune_freq_a20,
1458                 .fm_rsq_status  = si476x_core_cmd_fm_rsq_status_a20,
1459                 .agc_status     = si476x_core_cmd_agc_status_a20,
1460                 .intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a20,
1461         },
1462         [SI476X_REVISION_A30] = {
1463                 .power_up       = si476x_core_cmd_power_up_a20,
1464                 .power_down     = si476x_core_cmd_power_down_a20,
1465                 .fm_tune_freq   = si476x_core_cmd_fm_tune_freq_a20,
1466                 .am_tune_freq   = si476x_core_cmd_am_tune_freq_a20,
1467                 .fm_rsq_status  = si476x_core_cmd_fm_rsq_status_a30,
1468                 .agc_status     = si476x_core_cmd_agc_status_a20,
1469                 .intb_pin_cfg   = si476x_core_cmd_intb_pin_cfg_a20,
1470         },
1471 };
1472
1473 int si476x_core_cmd_power_up(struct si476x_core *core,
1474                              struct si476x_power_up_args *args)
1475 {
1476         BUG_ON(core->revision > SI476X_REVISION_A30 ||
1477                core->revision == -1);
1478         return si476x_cmds_vtable[core->revision].power_up(core, args);
1479 }
1480 EXPORT_SYMBOL_GPL(si476x_core_cmd_power_up);
1481
1482 int si476x_core_cmd_power_down(struct si476x_core *core,
1483                                struct si476x_power_down_args *args)
1484 {
1485         BUG_ON(core->revision > SI476X_REVISION_A30 ||
1486                core->revision == -1);
1487         return si476x_cmds_vtable[core->revision].power_down(core, args);
1488 }
1489 EXPORT_SYMBOL_GPL(si476x_core_cmd_power_down);
1490
1491 int si476x_core_cmd_fm_tune_freq(struct si476x_core *core,
1492                                  struct si476x_tune_freq_args *args)
1493 {
1494         BUG_ON(core->revision > SI476X_REVISION_A30 ||
1495                core->revision == -1);
1496         return si476x_cmds_vtable[core->revision].fm_tune_freq(core, args);
1497 }
1498 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_tune_freq);
1499
1500 int si476x_core_cmd_am_tune_freq(struct si476x_core *core,
1501                                  struct si476x_tune_freq_args *args)
1502 {
1503         BUG_ON(core->revision > SI476X_REVISION_A30 ||
1504                core->revision == -1);
1505         return si476x_cmds_vtable[core->revision].am_tune_freq(core, args);
1506 }
1507 EXPORT_SYMBOL_GPL(si476x_core_cmd_am_tune_freq);
1508
1509 int si476x_core_cmd_fm_rsq_status(struct si476x_core *core,
1510                                   struct si476x_rsq_status_args *args,
1511                                   struct si476x_rsq_status_report *report)
1512
1513 {
1514         BUG_ON(core->revision > SI476X_REVISION_A30 ||
1515                core->revision == -1);
1516         return si476x_cmds_vtable[core->revision].fm_rsq_status(core, args,
1517                                                                 report);
1518 }
1519 EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rsq_status);
1520
1521 int si476x_core_cmd_agc_status(struct si476x_core *core,
1522                                   struct si476x_agc_status_report *report)
1523
1524 {
1525         BUG_ON(core->revision > SI476X_REVISION_A30 ||
1526                core->revision == -1);
1527         return si476x_cmds_vtable[core->revision].agc_status(core, report);
1528 }
1529 EXPORT_SYMBOL_GPL(si476x_core_cmd_agc_status);
1530
1531 int si476x_core_cmd_intb_pin_cfg(struct si476x_core *core,
1532                             enum si476x_intb_config intb,
1533                             enum si476x_a1_config a1)
1534 {
1535         BUG_ON(core->revision > SI476X_REVISION_A30 ||
1536                core->revision == -1);
1537
1538         return si476x_cmds_vtable[core->revision].intb_pin_cfg(core, intb, a1);
1539 }
1540 EXPORT_SYMBOL_GPL(si476x_core_cmd_intb_pin_cfg);
1541
1542 MODULE_LICENSE("GPL");
1543 MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
1544 MODULE_DESCRIPTION("API for command exchange for si476x");