hc
2023-10-16 def2367077573b56f9fc4f824e5c0377a3a4175a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/*
 *  drivers/media/radio/si470x/radio-si470x.h
 *
 *  Driver for radios with Silicon Labs Si470x FM Radio Receivers
 *
 *  Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
 
 
/* driver definitions */
#define DRIVER_NAME "radio-si470x"
 
 
/* kernel includes */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/videodev2.h>
#include <linux/mutex.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
#include <media/v4l2-device.h>
#include <asm/unaligned.h>
 
 
 
/**************************************************************************
 * Register Definitions
 **************************************************************************/
#define RADIO_REGISTER_SIZE    2    /* 16 register bit width */
#define RADIO_REGISTER_NUM    16    /* DEVICEID   ... RDSD */
#define RDS_REGISTER_NUM    6    /* STATUSRSSI ... RDSD */
 
#define DEVICEID        0    /* Device ID */
#define DEVICEID_PN        0xf000    /* bits 15..12: Part Number */
#define DEVICEID_MFGID        0x0fff    /* bits 11..00: Manufacturer ID */
 
#define SI_CHIPID        1    /* Chip ID */
#define SI_CHIPID_REV        0xfc00    /* bits 15..10: Chip Version */
#define SI_CHIPID_DEV        0x0200    /* bits 09..09: Device */
#define SI_CHIPID_FIRMWARE    0x01ff    /* bits 08..00: Firmware Version */
 
#define POWERCFG        2    /* Power Configuration */
#define POWERCFG_DSMUTE        0x8000    /* bits 15..15: Softmute Disable */
#define POWERCFG_DMUTE        0x4000    /* bits 14..14: Mute Disable */
#define POWERCFG_MONO        0x2000    /* bits 13..13: Mono Select */
#define POWERCFG_RDSM        0x0800    /* bits 11..11: RDS Mode (Si4701 only) */
#define POWERCFG_SKMODE        0x0400    /* bits 10..10: Seek Mode */
#define POWERCFG_SEEKUP        0x0200    /* bits 09..09: Seek Direction */
#define POWERCFG_SEEK        0x0100    /* bits 08..08: Seek */
#define POWERCFG_DISABLE    0x0040    /* bits 06..06: Powerup Disable */
#define POWERCFG_ENABLE        0x0001    /* bits 00..00: Powerup Enable */
 
#define CHANNEL            3    /* Channel */
#define CHANNEL_TUNE        0x8000    /* bits 15..15: Tune */
#define CHANNEL_CHAN        0x03ff    /* bits 09..00: Channel Select */
 
#define SYSCONFIG1        4    /* System Configuration 1 */
#define SYSCONFIG1_RDSIEN    0x8000    /* bits 15..15: RDS Interrupt Enable (Si4701 only) */
#define SYSCONFIG1_STCIEN    0x4000    /* bits 14..14: Seek/Tune Complete Interrupt Enable */
#define SYSCONFIG1_RDS        0x1000    /* bits 12..12: RDS Enable (Si4701 only) */
#define SYSCONFIG1_DE        0x0800    /* bits 11..11: De-emphasis (0=75us 1=50us) */
#define SYSCONFIG1_AGCD        0x0400    /* bits 10..10: AGC Disable */
#define SYSCONFIG1_BLNDADJ    0x00c0    /* bits 07..06: Stereo/Mono Blend Level Adjustment */
#define SYSCONFIG1_GPIO3    0x0030    /* bits 05..04: General Purpose I/O 3 */
#define SYSCONFIG1_GPIO2    0x000c    /* bits 03..02: General Purpose I/O 2 */
#define SYSCONFIG1_GPIO2_DIS    0x0000    /* Disable GPIO 2 interrupt */
#define SYSCONFIG1_GPIO2_INT    0x0004    /* Enable STC/RDS interrupt */
#define SYSCONFIG1_GPIO1    0x0003    /* bits 01..00: General Purpose I/O 1 */
 
#define SYSCONFIG2        5    /* System Configuration 2 */
#define SYSCONFIG2_SEEKTH    0xff00    /* bits 15..08: RSSI Seek Threshold */
#define SYSCONFIG2_BAND        0x00c0    /* bits 07..06: Band Select */
#define SYSCONFIG2_SPACE    0x0030    /* bits 05..04: Channel Spacing */
#define SYSCONFIG2_VOLUME    0x000f    /* bits 03..00: Volume */
 
#define SYSCONFIG3        6    /* System Configuration 3 */
#define SYSCONFIG3_SMUTER    0xc000    /* bits 15..14: Softmute Attack/Recover Rate */
#define SYSCONFIG3_SMUTEA    0x3000    /* bits 13..12: Softmute Attenuation */
#define SYSCONFIG3_SKSNR    0x00f0    /* bits 07..04: Seek SNR Threshold */
#define SYSCONFIG3_SKCNT    0x000f    /* bits 03..00: Seek FM Impulse Detection Threshold */
 
#define TEST1            7    /* Test 1 */
#define TEST1_AHIZEN        0x4000    /* bits 14..14: Audio High-Z Enable */
 
#define TEST2            8    /* Test 2 */
/* TEST2 only contains reserved bits */
 
#define BOOTCONFIG        9    /* Boot Configuration */
/* BOOTCONFIG only contains reserved bits */
 
#define STATUSRSSI        10    /* Status RSSI */
#define STATUSRSSI_RDSR        0x8000    /* bits 15..15: RDS Ready (Si4701 only) */
#define STATUSRSSI_STC        0x4000    /* bits 14..14: Seek/Tune Complete */
#define STATUSRSSI_SF        0x2000    /* bits 13..13: Seek Fail/Band Limit */
#define STATUSRSSI_AFCRL    0x1000    /* bits 12..12: AFC Rail */
#define STATUSRSSI_RDSS        0x0800    /* bits 11..11: RDS Synchronized (Si4701 only) */
#define STATUSRSSI_BLERA    0x0600    /* bits 10..09: RDS Block A Errors (Si4701 only) */
#define STATUSRSSI_ST        0x0100    /* bits 08..08: Stereo Indicator */
#define STATUSRSSI_RSSI        0x00ff    /* bits 07..00: RSSI (Received Signal Strength Indicator) */
 
#define READCHAN        11    /* Read Channel */
#define READCHAN_BLERB        0xc000    /* bits 15..14: RDS Block D Errors (Si4701 only) */
#define READCHAN_BLERC        0x3000    /* bits 13..12: RDS Block C Errors (Si4701 only) */
#define READCHAN_BLERD        0x0c00    /* bits 11..10: RDS Block B Errors (Si4701 only) */
#define READCHAN_READCHAN    0x03ff    /* bits 09..00: Read Channel */
 
#define RDSA            12    /* RDSA */
#define RDSA_RDSA        0xffff    /* bits 15..00: RDS Block A Data (Si4701 only) */
 
#define RDSB            13    /* RDSB */
#define RDSB_RDSB        0xffff    /* bits 15..00: RDS Block B Data (Si4701 only) */
 
#define RDSC            14    /* RDSC */
#define RDSC_RDSC        0xffff    /* bits 15..00: RDS Block C Data (Si4701 only) */
 
#define RDSD            15    /* RDSD */
#define RDSD_RDSD        0xffff    /* bits 15..00: RDS Block D Data (Si4701 only) */
 
 
 
/**************************************************************************
 * General Driver Definitions
 **************************************************************************/
 
/*
 * si470x_device - private data
 */
struct si470x_device {
   struct v4l2_device v4l2_dev;
   struct video_device videodev;
   struct v4l2_ctrl_handler hdl;
   int band;
 
   /* Silabs internal registers (0..15) */
   unsigned short registers[RADIO_REGISTER_NUM];
 
   /* RDS receive buffer */
   wait_queue_head_t read_queue;
   struct mutex lock;        /* buffer locking */
   unsigned char *buffer;        /* size is always multiple of three */
   unsigned int buf_size;
   unsigned int rd_index;
   unsigned int wr_index;
 
   struct completion completion;
   bool status_rssi_auto_update;    /* Does RSSI get updated automatic? */
 
   /* si470x ops */
 
   int (*get_register)(struct si470x_device *radio, int regnr);
   int (*set_register)(struct si470x_device *radio, int regnr);
   int (*fops_open)(struct file *file);
   int (*fops_release)(struct file *file);
   int (*vidioc_querycap)(struct file *file, void *priv,
                  struct v4l2_capability *capability);
 
#if IS_ENABLED(CONFIG_USB_SI470X)
   /* reference to USB and video device */
   struct usb_device *usbdev;
   struct usb_interface *intf;
   char *usb_buf;
 
   /* Interrupt endpoint handling */
   char *int_in_buffer;
   struct usb_endpoint_descriptor *int_in_endpoint;
   struct urb *int_in_urb;
   int int_in_running;
 
   /* scratch page */
   unsigned char software_version;
   unsigned char hardware_version;
#endif
 
#if IS_ENABLED(CONFIG_I2C_SI470X)
   struct i2c_client *client;
#endif
};
 
 
 
/**************************************************************************
 * Firmware Versions
 **************************************************************************/
 
#define RADIO_FW_VERSION    12
 
 
 
/**************************************************************************
 * Frequency Multiplicator
 **************************************************************************/
 
/*
 * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW,
 * 62.5 kHz otherwise.
 * The tuner is able to have a channel spacing of 50, 100 or 200 kHz.
 * tuner->capability is therefore set to V4L2_TUNER_CAP_LOW
 * The FREQ_MUL is then: 1 MHz / 62.5 Hz = 16000
 */
#define FREQ_MUL (1000000 / 62.5)
 
 
 
/**************************************************************************
 * Common Functions
 **************************************************************************/
extern const struct video_device si470x_viddev_template;
extern const struct v4l2_ctrl_ops si470x_ctrl_ops;
int si470x_disconnect_check(struct si470x_device *radio);
int si470x_set_freq(struct si470x_device *radio, unsigned int freq);
int si470x_start(struct si470x_device *radio);
int si470x_stop(struct si470x_device *radio);