Commit | Line | Data |
---|---|---|
4286c6f6 | 1 | /* radio-trust.c - Trust FM Radio card driver for Linux 2.2 |
1da177e4 LT |
2 | * by Eric Lammerts <eric@scintilla.utwente.nl> |
3 | * | |
4 | * Based on radio-aztech.c. Original notes: | |
5 | * | |
4286c6f6 | 6 | * Adapted to support the Video for Linux API by |
1da177e4 LT |
7 | * Russell Kroll <rkroll@exploits.org>. Based on original tuner code by: |
8 | * | |
9 | * Quay Ly | |
10 | * Donald Song | |
4286c6f6 | 11 | * Jason Lewis (jlewis@twilight.vtc.vsc.edu) |
1da177e4 LT |
12 | * Scott McGrath (smcgrath@twilight.vtc.vsc.edu) |
13 | * William McGrath (wmcgrath@twilight.vtc.vsc.edu) | |
14 | * | |
982eddb9 | 15 | * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> |
1da177e4 LT |
16 | */ |
17 | ||
18 | #include <stdarg.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/init.h> | |
21 | #include <linux/ioport.h> | |
982eddb9 | 22 | #include <linux/videodev2.h> |
1dc8aafc | 23 | #include <linux/io.h> |
9f1dfccf | 24 | #include <linux/slab.h> |
1dc8aafc | 25 | #include <media/v4l2-device.h> |
35ea11ff | 26 | #include <media/v4l2-ioctl.h> |
1d211f26 | 27 | #include "radio-isa.h" |
1da177e4 | 28 | |
1dc8aafc HV |
29 | MODULE_AUTHOR("Eric Lammerts, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); |
30 | MODULE_DESCRIPTION("A driver for the Trust FM Radio card."); | |
31 | MODULE_LICENSE("GPL"); | |
1d211f26 | 32 | MODULE_VERSION("0.1.99"); |
982eddb9 | 33 | |
1da177e4 LT |
34 | /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */ |
35 | ||
36 | #ifndef CONFIG_RADIO_TRUST_PORT | |
37 | #define CONFIG_RADIO_TRUST_PORT -1 | |
38 | #endif | |
39 | ||
1d211f26 | 40 | #define TRUST_MAX 2 |
1dc8aafc | 41 | |
1d211f26 HV |
42 | static int io[TRUST_MAX] = { [0] = CONFIG_RADIO_TRUST_PORT, |
43 | [1 ... (TRUST_MAX - 1)] = -1 }; | |
44 | static int radio_nr[TRUST_MAX] = { [0 ... (TRUST_MAX - 1)] = -1 }; | |
45 | ||
46 | module_param_array(io, int, NULL, 0444); | |
47 | MODULE_PARM_DESC(io, "I/O addresses of the Trust FM Radio card (0x350 or 0x358)"); | |
48 | module_param_array(radio_nr, int, NULL, 0444); | |
49 | MODULE_PARM_DESC(radio_nr, "Radio device numbers"); | |
1dc8aafc | 50 | |
1dc8aafc | 51 | struct trust { |
1d211f26 | 52 | struct radio_isa_card isa; |
1dc8aafc | 53 | int ioval; |
1dc8aafc HV |
54 | }; |
55 | ||
1d211f26 HV |
56 | static struct radio_isa_card *trust_alloc(void) |
57 | { | |
58 | struct trust *tr = kzalloc(sizeof(*tr), GFP_KERNEL); | |
59 | ||
60 | return tr ? &tr->isa : NULL; | |
61 | } | |
1da177e4 LT |
62 | |
63 | /* i2c addresses */ | |
64 | #define TDA7318_ADDR 0x88 | |
65 | #define TSA6060T_ADDR 0xc4 | |
66 | ||
1d211f26 HV |
67 | #define TR_DELAY do { inb(tr->isa.io); inb(tr->isa.io); inb(tr->isa.io); } while (0) |
68 | #define TR_SET_SCL outb(tr->ioval |= 2, tr->isa.io) | |
69 | #define TR_CLR_SCL outb(tr->ioval &= 0xfd, tr->isa.io) | |
70 | #define TR_SET_SDA outb(tr->ioval |= 1, tr->isa.io) | |
71 | #define TR_CLR_SDA outb(tr->ioval &= 0xfe, tr->isa.io) | |
1da177e4 | 72 | |
1dc8aafc | 73 | static void write_i2c(struct trust *tr, int n, ...) |
1da177e4 LT |
74 | { |
75 | unsigned char val, mask; | |
76 | va_list args; | |
77 | ||
78 | va_start(args, n); | |
79 | ||
80 | /* start condition */ | |
81 | TR_SET_SDA; | |
82 | TR_SET_SCL; | |
83 | TR_DELAY; | |
84 | TR_CLR_SDA; | |
85 | TR_CLR_SCL; | |
86 | TR_DELAY; | |
87 | ||
1d211f26 | 88 | for (; n; n--) { |
1da177e4 | 89 | val = va_arg(args, unsigned); |
1d211f26 HV |
90 | for (mask = 0x80; mask; mask >>= 1) { |
91 | if (val & mask) | |
1da177e4 LT |
92 | TR_SET_SDA; |
93 | else | |
94 | TR_CLR_SDA; | |
95 | TR_SET_SCL; | |
96 | TR_DELAY; | |
97 | TR_CLR_SCL; | |
98 | TR_DELAY; | |
99 | } | |
100 | /* acknowledge bit */ | |
101 | TR_SET_SDA; | |
102 | TR_SET_SCL; | |
103 | TR_DELAY; | |
104 | TR_CLR_SCL; | |
105 | TR_DELAY; | |
106 | } | |
107 | ||
108 | /* stop condition */ | |
109 | TR_CLR_SDA; | |
110 | TR_DELAY; | |
111 | TR_SET_SCL; | |
112 | TR_DELAY; | |
113 | TR_SET_SDA; | |
114 | TR_DELAY; | |
115 | ||
116 | va_end(args); | |
117 | } | |
118 | ||
1d211f26 | 119 | static int trust_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) |
1da177e4 | 120 | { |
1d211f26 | 121 | struct trust *tr = container_of(isa, struct trust, isa); |
1da177e4 | 122 | |
1d211f26 HV |
123 | tr->ioval = (tr->ioval & 0xf7) | (mute << 3); |
124 | outb(tr->ioval, isa->io); | |
125 | write_i2c(tr, 2, TDA7318_ADDR, vol ^ 0x1f); | |
126 | return 0; | |
1da177e4 LT |
127 | } |
128 | ||
1d211f26 | 129 | static int trust_s_stereo(struct radio_isa_card *isa, bool stereo) |
1da177e4 | 130 | { |
1d211f26 | 131 | struct trust *tr = container_of(isa, struct trust, isa); |
1da177e4 | 132 | |
1d211f26 HV |
133 | tr->ioval = (tr->ioval & 0xfb) | (!stereo << 2); |
134 | outb(tr->ioval, isa->io); | |
135 | return 0; | |
1da177e4 LT |
136 | } |
137 | ||
1d211f26 | 138 | static u32 trust_g_signal(struct radio_isa_card *isa) |
1da177e4 LT |
139 | { |
140 | int i, v; | |
4286c6f6 | 141 | |
1dc8aafc | 142 | for (i = 0, v = 0; i < 100; i++) |
1d211f26 | 143 | v |= inb(isa->io); |
1dc8aafc | 144 | return (v & 1) ? 0 : 0xffff; |
1da177e4 LT |
145 | } |
146 | ||
1d211f26 | 147 | static int trust_s_frequency(struct radio_isa_card *isa, u32 freq) |
1da177e4 | 148 | { |
1d211f26 | 149 | struct trust *tr = container_of(isa, struct trust, isa); |
1da177e4 | 150 | |
1d211f26 HV |
151 | freq /= 160; /* Convert to 10 kHz units */ |
152 | freq += 1070; /* Add 10.7 MHz IF */ | |
153 | write_i2c(tr, 5, TSA6060T_ADDR, (freq << 1) | 1, | |
154 | freq >> 7, 0x60 | ((freq >> 15) & 1), 0); | |
c5f822bf DL |
155 | return 0; |
156 | } | |
1da177e4 | 157 | |
1d211f26 HV |
158 | static int basstreble2chip[15] = { |
159 | 0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8 | |
160 | }; | |
982eddb9 | 161 | |
1d211f26 | 162 | static int trust_s_ctrl(struct v4l2_ctrl *ctrl) |
c5f822bf | 163 | { |
1d211f26 HV |
164 | struct radio_isa_card *isa = |
165 | container_of(ctrl->handler, struct radio_isa_card, hdl); | |
166 | struct trust *tr = container_of(isa, struct trust, isa); | |
1dc8aafc | 167 | |
c5f822bf | 168 | switch (ctrl->id) { |
c5f822bf | 169 | case V4L2_CID_AUDIO_BASS: |
1d211f26 | 170 | write_i2c(tr, 2, TDA7318_ADDR, 0x60 | basstreble2chip[ctrl->val]); |
c5f822bf DL |
171 | return 0; |
172 | case V4L2_CID_AUDIO_TREBLE: | |
1d211f26 | 173 | write_i2c(tr, 2, TDA7318_ADDR, 0x70 | basstreble2chip[ctrl->val]); |
c5f822bf | 174 | return 0; |
1da177e4 | 175 | } |
c5f822bf | 176 | return -EINVAL; |
1da177e4 LT |
177 | } |
178 | ||
1d211f26 HV |
179 | static const struct v4l2_ctrl_ops trust_ctrl_ops = { |
180 | .s_ctrl = trust_s_ctrl, | |
1da177e4 LT |
181 | }; |
182 | ||
1d211f26 | 183 | static int trust_initialize(struct radio_isa_card *isa) |
1da177e4 | 184 | { |
1d211f26 | 185 | struct trust *tr = container_of(isa, struct trust, isa); |
1dc8aafc | 186 | |
1dc8aafc | 187 | tr->ioval = 0xf; |
1dc8aafc HV |
188 | write_i2c(tr, 2, TDA7318_ADDR, 0x80); /* speaker att. LF = 0 dB */ |
189 | write_i2c(tr, 2, TDA7318_ADDR, 0xa0); /* speaker att. RF = 0 dB */ | |
190 | write_i2c(tr, 2, TDA7318_ADDR, 0xc0); /* speaker att. LR = 0 dB */ | |
191 | write_i2c(tr, 2, TDA7318_ADDR, 0xe0); /* speaker att. RR = 0 dB */ | |
192 | write_i2c(tr, 2, TDA7318_ADDR, 0x40); /* stereo 1 input, gain = 18.75 dB */ | |
1da177e4 | 193 | |
1d211f26 HV |
194 | v4l2_ctrl_new_std(&isa->hdl, &trust_ctrl_ops, |
195 | V4L2_CID_AUDIO_BASS, 0, 15, 1, 8); | |
196 | v4l2_ctrl_new_std(&isa->hdl, &trust_ctrl_ops, | |
197 | V4L2_CID_AUDIO_TREBLE, 0, 15, 1, 8); | |
198 | return isa->hdl.error; | |
199 | } | |
1da177e4 | 200 | |
1d211f26 HV |
201 | static const struct radio_isa_ops trust_ops = { |
202 | .init = trust_initialize, | |
203 | .alloc = trust_alloc, | |
204 | .s_mute_volume = trust_s_mute_volume, | |
205 | .s_frequency = trust_s_frequency, | |
206 | .s_stereo = trust_s_stereo, | |
207 | .g_signal = trust_g_signal, | |
208 | }; | |
32958fdd | 209 | |
1d211f26 HV |
210 | static const int trust_ioports[] = { 0x350, 0x358 }; |
211 | ||
212 | static struct radio_isa_driver trust_driver = { | |
213 | .driver = { | |
214 | .match = radio_isa_match, | |
215 | .probe = radio_isa_probe, | |
216 | .remove = radio_isa_remove, | |
217 | .driver = { | |
218 | .name = "radio-trust", | |
219 | }, | |
220 | }, | |
221 | .io_params = io, | |
222 | .radio_nr_params = radio_nr, | |
223 | .io_ports = trust_ioports, | |
224 | .num_of_io_ports = ARRAY_SIZE(trust_ioports), | |
225 | .region_size = 2, | |
226 | .card = "Trust FM Radio", | |
227 | .ops = &trust_ops, | |
228 | .has_stereo = true, | |
229 | .max_volume = 31, | |
230 | }; | |
32958fdd | 231 | |
1d211f26 HV |
232 | static int __init trust_init(void) |
233 | { | |
234 | return isa_register_driver(&trust_driver.driver, TRUST_MAX); | |
1da177e4 LT |
235 | } |
236 | ||
1d211f26 | 237 | static void __exit trust_exit(void) |
1da177e4 | 238 | { |
1d211f26 | 239 | isa_unregister_driver(&trust_driver.driver); |
1da177e4 LT |
240 | } |
241 | ||
242 | module_init(trust_init); | |
1d211f26 | 243 | module_exit(trust_exit); |