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