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