xfs: remote attribute lookups require the value length
[GitHub/mt8127/android_kernel_alcatel_ttab.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");