summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--firmware/drivers/tuner/si4700.c145
1 files changed, 106 insertions, 39 deletions
diff --git a/firmware/drivers/tuner/si4700.c b/firmware/drivers/tuner/si4700.c
index a55a8cfcc4..f68ea5a750 100644
--- a/firmware/drivers/tuner/si4700.c
+++ b/firmware/drivers/tuner/si4700.c
@@ -31,24 +31,84 @@
31 31
32#define I2C_ADR 0x20 32#define I2C_ADR 0x20
33 33
34/* I2C writes start at register 02h so the first two bytes are 34#define DEVICEID 0x0
35 02h, next two 03h, etc. */ 35#define CHIPID 0x1
36static unsigned char write_bytes[8]; /* registers 02 - 05 */ 36#define POWERCFG 0x2
37#define CHANNEL 0x3
38#define SYSCONFIG1 0x4
39#define SYSCONFIG2 0x5
40#define SYSCONFIG3 0x6
41#define TEST1 0x7
42#define TEST2 0x8
43#define BOOTCONFIG 0x9
44#define STATUSRSSI 0xA
45#define READCHAN 0xB
46#define RDSA 0xC
47#define RDSB 0xD
48#define RDSC 0xE
49#define RDSD 0xF
50
51/* some models use the internal 32 kHz oscillator which needs special attention
52 during initialisation, power-up and power-down.
53*/
54#if defined(SANSA_CLIP) || defined(SANSA_E200V2) || defined(SANSA_FUZE)
55#define USE_INTERNAL_OSCILLATOR
56#endif
57
37static bool tuner_present = false; 58static bool tuner_present = false;
59static unsigned short cache[16];
60
61/* reads <len> registers from radio at offset 0x0A into cache */
62static void si4700_read(int len)
63{
64 int i;
65 unsigned char buf[32];
66 unsigned char *ptr = buf;
67 unsigned short data;
68
69 fmradio_i2c_read(I2C_ADR, buf, len * 2);
70 for (i = 0; i < len; i++) {
71 data = ptr[0] << 8 | ptr[1];
72 cache[(i + STATUSRSSI) & 0xF] = data;
73 ptr += 2;
74 }
75}
76
77/* writes <len> registers from cache to radio at offset 0x02 */
78static void si4700_write(int len)
79{
80 int i;
81 unsigned char buf[32];
82 unsigned char *ptr = buf;
83 unsigned short data;
84
85 for (i = 0; i < len; i++) {
86 data = cache[(i + POWERCFG) & 0xF];
87 *ptr++ = (data >> 8) & 0xFF;
88 *ptr++ = data & 0xFF;
89 }
90 fmradio_i2c_write(I2C_ADR, buf, len * 2);
91}
92
38 93
39void si4700_init(void) 94void si4700_init(void)
40{ 95{
41 unsigned char read_bytes[32];
42 tuner_power(true); 96 tuner_power(true);
43 fmradio_i2c_read(I2C_ADR, read_bytes, sizeof(read_bytes));
44 97
45 if ((read_bytes[12] << 8 | read_bytes[13]) == 0x1242) 98 /* read all registers */
99 si4700_read(16);
100
101 /* check device id */
102 if (cache[DEVICEID] == 0x1242)
46 { 103 {
47 tuner_present = true; 104 tuner_present = true;
48 /* fill in the initial values in write_bytes */ 105
49 memcpy(&write_bytes[0], &read_bytes[16], sizeof(write_bytes)); 106#ifdef USE_INTERNAL_OSCILLATOR
50 /* -6dB volume, keep everything else as default */ 107 /* enable the internal oscillator */
51 write_bytes[7] = (write_bytes[7] & ~0xf) | 0xc; 108 cache[TEST1] |= (1 << 15); /* XOSCEN */
109 si4700_write(6);
110 sleep(HZ/2);
111#endif
52 } 112 }
53 113
54 tuner_power(false); 114 tuner_power(false);
@@ -56,20 +116,19 @@ void si4700_init(void)
56 116
57static void si4700_tune(void) 117static void si4700_tune(void)
58{ 118{
59 unsigned char read_bytes[1]; 119 cache[CHANNEL] |= (1 << 15); /* Set TUNE high to start tuning */
60 120 si4700_write(2);
61 write_bytes[2] |= (1 << 7); /* Set TUNE high to start tuning */
62 fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes));
63 121
64 do 122 do
65 { 123 {
66 sleep(HZ/50); 124 /* tuning should be done within 60 ms according to the datasheet */
67 fmradio_i2c_read(I2C_ADR, read_bytes, 1); 125 sleep(HZ * 60 / 1000);
126 si4700_read(2);
68 } 127 }
69 while (!(read_bytes[0] & (1 << 6))); /* STC high == Seek/Tune complete */ 128 while (!(cache[STATUSRSSI] & (1 << 14))); /* STC high */
70 129
71 write_bytes[2] &= ~(1 << 7); /* Set TUNE low */ 130 cache[CHANNEL] &= ~(1 << 15); /* Set TUNE low */
72 fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes)); 131 si4700_write(2);
73} 132}
74 133
75/* tuner abstraction layer: set something to the tuner */ 134/* tuner abstraction layer: set something to the tuner */
@@ -80,13 +139,25 @@ int si4700_set(int setting, int value)
80 case RADIO_SLEEP: 139 case RADIO_SLEEP:
81 if (value) 140 if (value)
82 { 141 {
83 write_bytes[1] = (1 | (1 << 6)); /* ENABLE high, DISABLE high */ 142 /* power down */
143 cache[POWERCFG] = (1 | (1 << 6)); /* ENABLE high, DISABLE high */
144 si4700_write(1);
84 } 145 }
85 else 146 else
86 { 147 {
87 write_bytes[1] = 1; /* ENABLE high, DISABLE low */ 148 /* power up */
149 cache[POWERCFG] = 1; /* ENABLE high, DISABLE low */
150 si4700_write(1);
151 sleep(110 * HZ / 1000);
152
153 /* update register cache */
154 si4700_read(16);
155
156 /* -6dB volume, keep everything else as default */
157 cache[SYSCONFIG2] = (cache[SYSCONFIG2] & ~0xF) | 0xC;
158 si4700_write(5);
88 } 159 }
89 break; 160 return 1;
90 161
91 case RADIO_FREQUENCY: 162 case RADIO_FREQUENCY:
92 { 163 {
@@ -95,9 +166,9 @@ int si4700_set(int setting, int value)
95 200000, 100000, 50000 166 200000, 100000, 50000
96 }; 167 };
97 unsigned int chan; 168 unsigned int chan;
98 unsigned int spacing = spacings[(write_bytes[7] >> 4) & 3] ; 169 unsigned int spacing = spacings[(cache[5] >> 4) & 3] ;
99 170
100 if (write_bytes[7] & (3 << 6)) /* check BAND */ 171 if (cache[SYSCONFIG2] & (3 << 6)) /* check BAND */
101 { 172 {
102 chan = (value - 76000000) / spacing; 173 chan = (value - 76000000) / spacing;
103 } 174 }
@@ -106,9 +177,7 @@ int si4700_set(int setting, int value)
106 chan = (value - 87500000) / spacing; 177 chan = (value - 87500000) / spacing;
107 } 178 }
108 179
109 write_bytes[2] = (write_bytes[2] & ~3) | ((chan & (3 << 8)) >> 8); 180 cache[CHANNEL] = (cache[CHANNEL] & ~0x3FF) | chan;
110 write_bytes[3] = (chan & 0xff);
111 fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes));
112 si4700_tune(); 181 si4700_tune();
113 return 1; 182 return 1;
114 } 183 }
@@ -121,12 +190,12 @@ int si4700_set(int setting, int value)
121 if (value) 190 if (value)
122 { 191 {
123 /* mute */ 192 /* mute */
124 write_bytes[0] &= ~(1 << 6); 193 cache[POWERCFG] &= ~(1 << 14);
125 } 194 }
126 else 195 else
127 { 196 {
128 /* unmute */ 197 /* unmute */
129 write_bytes[0] |= (1 << 6); 198 cache[POWERCFG] |= (1 << 14);
130 } 199 }
131 break; 200 break;
132 201
@@ -135,20 +204,20 @@ int si4700_set(int setting, int value)
135 const struct si4700_region_data *rd = 204 const struct si4700_region_data *rd =
136 &si4700_region_data[value]; 205 &si4700_region_data[value];
137 206
138 write_bytes[4] = ((write_bytes[4] & ~(1 << 3)) | (rd->deemphasis << 3)); 207 cache[SYSCONFIG1] = (cache[SYSCONFIG1] & ~(1 << 11)) | (rd->deemphasis << 11);
139 write_bytes[7] = ((write_bytes[7] & ~(3 << 6)) | (rd->band << 6)); 208 cache[SYSCONFIG2] = (cache[SYSCONFIG2] & ~(3 << 6)) | (rd->band << 6);
140 write_bytes[7] = ((write_bytes[7] & ~(3 << 4)) | (rd->spacing << 4)); 209 cache[SYSCONFIG2] = (cache[SYSCONFIG2] & ~(3 << 4)) | (rd->spacing << 4);
141 break; 210 break;
142 } 211 }
143 212
144 case RADIO_FORCE_MONO: 213 case RADIO_FORCE_MONO:
145 if (value) 214 if (value)
146 { 215 {
147 write_bytes[0] |= (1 << 5); 216 cache[POWERCFG] |= (1 << 13);
148 } 217 }
149 else 218 else
150 { 219 {
151 write_bytes[0] &= ~(1 << 5); 220 cache[POWERCFG] &= ~(1 << 13);
152 } 221 }
153 break; 222 break;
154 223
@@ -156,15 +225,13 @@ int si4700_set(int setting, int value)
156 return -1; 225 return -1;
157 } 226 }
158 227
159 fmradio_i2c_write(I2C_ADR, write_bytes, sizeof(write_bytes)); 228 si4700_write(5);
160 return 1; 229 return 1;
161} 230}
162 231
163/* tuner abstraction layer: read something from the tuner */ 232/* tuner abstraction layer: read something from the tuner */
164int si4700_get(int setting) 233int si4700_get(int setting)
165{ 234{
166 /* I2C reads start with register 0xA */
167 unsigned char read_bytes[1];
168 int val = -1; /* default for unsupported query */ 235 int val = -1; /* default for unsupported query */
169 236
170 switch(setting) 237 switch(setting)
@@ -178,8 +245,8 @@ int si4700_get(int setting)
178 break; 245 break;
179 246
180 case RADIO_STEREO: 247 case RADIO_STEREO:
181 fmradio_i2c_read(I2C_ADR, read_bytes, sizeof(read_bytes)); 248 si4700_read(1);
182 val = (read_bytes[0] & 1); /* ST high == Stereo */ 249 val = (cache[STATUSRSSI] & (1 << 8)); /* ST high == Stereo */
183 break; 250 break;
184 } 251 }
185 252