diff options
-rw-r--r-- | firmware/drivers/tuner/si4700.c | 145 |
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 |
36 | static 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 | |||
37 | static bool tuner_present = false; | 58 | static bool tuner_present = false; |
59 | static unsigned short cache[16]; | ||
60 | |||
61 | /* reads <len> registers from radio at offset 0x0A into cache */ | ||
62 | static 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 */ | ||
78 | static 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 | ||
39 | void si4700_init(void) | 94 | void 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 | ||
57 | static void si4700_tune(void) | 117 | static 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 */ |
164 | int si4700_get(int setting) | 233 | int 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 | ||