Commit | Line | Data |
---|---|---|
3088fba8 HV |
1 | /* |
2 | * radio-aztech.c - Aztech radio card driver | |
1da177e4 | 3 | * |
3088fba8 | 4 | * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@xs4all.nl> |
a4366af4 | 5 | * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> |
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 | * | |
3088fba8 | 15 | * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool. |
1da177e4 LT |
16 | */ |
17 | ||
18 | #include <linux/module.h> /* Modules */ | |
19 | #include <linux/init.h> /* Initdata */ | |
fb911ee8 | 20 | #include <linux/ioport.h> /* request_region */ |
1da177e4 | 21 | #include <linux/delay.h> /* udelay */ |
a4366af4 | 22 | #include <linux/videodev2.h> /* kernel radio structs */ |
e697e12e | 23 | #include <linux/io.h> /* outb, outb_p */ |
9f1dfccf | 24 | #include <linux/slab.h> |
e697e12e | 25 | #include <media/v4l2-device.h> |
35ea11ff | 26 | #include <media/v4l2-ioctl.h> |
3088fba8 HV |
27 | #include <media/v4l2-ctrls.h> |
28 | #include "radio-isa.h" | |
1da177e4 | 29 | |
e697e12e HV |
30 | MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); |
31 | MODULE_DESCRIPTION("A driver for the Aztech radio card."); | |
32 | MODULE_LICENSE("GPL"); | |
3088fba8 | 33 | MODULE_VERSION("1.0.0"); |
a4366af4 | 34 | |
1da177e4 | 35 | /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */ |
1da177e4 LT |
36 | #ifndef CONFIG_RADIO_AZTECH_PORT |
37 | #define CONFIG_RADIO_AZTECH_PORT -1 | |
38 | #endif | |
39 | ||
3088fba8 | 40 | #define AZTECH_MAX 2 |
1da177e4 | 41 | |
3088fba8 HV |
42 | static int io[AZTECH_MAX] = { [0] = CONFIG_RADIO_AZTECH_PORT, |
43 | [1 ... (AZTECH_MAX - 1)] = -1 }; | |
44 | static int radio_nr[AZTECH_MAX] = { [0 ... (AZTECH_MAX - 1)] = -1 }; | |
45 | static const int radio_wait_time = 1000; | |
e697e12e | 46 | |
3088fba8 HV |
47 | module_param_array(io, int, NULL, 0444); |
48 | MODULE_PARM_DESC(io, "I/O addresses of the Aztech card (0x350 or 0x358)"); | |
49 | module_param_array(radio_nr, int, NULL, 0444); | |
50 | MODULE_PARM_DESC(radio_nr, "Radio device numbers"); | |
51 | ||
52 | struct aztech { | |
53 | struct radio_isa_card isa; | |
1da177e4 | 54 | int curvol; |
1da177e4 LT |
55 | }; |
56 | ||
e697e12e | 57 | static void send_0_byte(struct aztech *az) |
1da177e4 LT |
58 | { |
59 | udelay(radio_wait_time); | |
3088fba8 HV |
60 | outb_p(2 + az->curvol, az->isa.io); |
61 | outb_p(64 + 2 + az->curvol, az->isa.io); | |
1da177e4 LT |
62 | } |
63 | ||
e697e12e | 64 | static void send_1_byte(struct aztech *az) |
1da177e4 | 65 | { |
3088fba8 HV |
66 | udelay(radio_wait_time); |
67 | outb_p(128 + 2 + az->curvol, az->isa.io); | |
68 | outb_p(128 + 64 + 2 + az->curvol, az->isa.io); | |
1da177e4 LT |
69 | } |
70 | ||
3088fba8 | 71 | static struct radio_isa_card *aztech_alloc(void) |
1da177e4 | 72 | { |
3088fba8 | 73 | struct aztech *az = kzalloc(sizeof(*az), GFP_KERNEL); |
e697e12e | 74 | |
3088fba8 | 75 | return az ? &az->isa : NULL; |
1da177e4 LT |
76 | } |
77 | ||
3088fba8 | 78 | static int aztech_s_frequency(struct radio_isa_card *isa, u32 freq) |
1da177e4 | 79 | { |
3088fba8 | 80 | struct aztech *az = container_of(isa, struct aztech, isa); |
1da177e4 LT |
81 | int i; |
82 | ||
3088fba8 HV |
83 | freq += 171200; /* Add 10.7 MHz IF */ |
84 | freq /= 800; /* Convert to 50 kHz units */ | |
4286c6f6 | 85 | |
e697e12e | 86 | send_0_byte(az); /* 0: LSB of frequency */ |
1da177e4 LT |
87 | |
88 | for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ | |
3088fba8 | 89 | if (freq & (1 << i)) |
e697e12e | 90 | send_1_byte(az); |
1da177e4 | 91 | else |
e697e12e | 92 | send_0_byte(az); |
1da177e4 | 93 | |
e697e12e HV |
94 | send_0_byte(az); /* 14: test bit - always 0 */ |
95 | send_0_byte(az); /* 15: test bit - always 0 */ | |
96 | send_0_byte(az); /* 16: band data 0 - always 0 */ | |
3088fba8 | 97 | if (isa->stereo) /* 17: stereo (1 to enable) */ |
e697e12e | 98 | send_1_byte(az); |
1da177e4 | 99 | else |
e697e12e | 100 | send_0_byte(az); |
1da177e4 | 101 | |
e697e12e HV |
102 | send_1_byte(az); /* 18: band data 1 - unknown */ |
103 | send_0_byte(az); /* 19: time base - always 0 */ | |
104 | send_0_byte(az); /* 20: spacing (0 = 25 kHz) */ | |
105 | send_1_byte(az); /* 21: spacing (1 = 25 kHz) */ | |
106 | send_0_byte(az); /* 22: spacing (0 = 25 kHz) */ | |
107 | send_1_byte(az); /* 23: AM/FM (FM = 1, always) */ | |
1da177e4 LT |
108 | |
109 | /* latch frequency */ | |
110 | ||
e697e12e | 111 | udelay(radio_wait_time); |
3088fba8 | 112 | outb_p(128 + 64 + az->curvol, az->isa.io); |
676b0ac7 | 113 | |
a0c05ab9 MCC |
114 | return 0; |
115 | } | |
116 | ||
3088fba8 HV |
117 | /* thanks to Michael Dwyer for giving me a dose of clues in |
118 | * the signal strength department.. | |
119 | * | |
120 | * This card has a stereo bit - bit 0 set = mono, not set = stereo | |
121 | */ | |
122 | static u32 aztech_g_rxsubchans(struct radio_isa_card *isa) | |
e697e12e | 123 | { |
3088fba8 HV |
124 | if (inb(isa->io) & 1) |
125 | return V4L2_TUNER_SUB_MONO; | |
126 | return V4L2_TUNER_SUB_STEREO; | |
e697e12e HV |
127 | } |
128 | ||
3088fba8 | 129 | static int aztech_s_stereo(struct radio_isa_card *isa, bool stereo) |
99218fe4 | 130 | { |
3088fba8 | 131 | return aztech_s_frequency(isa, isa->freq); |
99218fe4 MCC |
132 | } |
133 | ||
3088fba8 | 134 | static int aztech_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) |
99218fe4 | 135 | { |
3088fba8 | 136 | struct aztech *az = container_of(isa, struct aztech, isa); |
99218fe4 | 137 | |
3088fba8 HV |
138 | if (mute) |
139 | vol = 0; | |
140 | az->curvol = (vol & 1) + ((vol & 2) << 1); | |
141 | outb(az->curvol, isa->io); | |
99218fe4 MCC |
142 | return 0; |
143 | } | |
144 | ||
3088fba8 HV |
145 | static const struct radio_isa_ops aztech_ops = { |
146 | .alloc = aztech_alloc, | |
147 | .s_mute_volume = aztech_s_mute_volume, | |
148 | .s_frequency = aztech_s_frequency, | |
149 | .s_stereo = aztech_s_stereo, | |
150 | .g_rxsubchans = aztech_g_rxsubchans, | |
1da177e4 LT |
151 | }; |
152 | ||
3088fba8 HV |
153 | static const int aztech_ioports[] = { 0x350, 0x358 }; |
154 | ||
155 | static struct radio_isa_driver aztech_driver = { | |
156 | .driver = { | |
157 | .match = radio_isa_match, | |
158 | .probe = radio_isa_probe, | |
159 | .remove = radio_isa_remove, | |
160 | .driver = { | |
161 | .name = "radio-aztech", | |
162 | }, | |
163 | }, | |
164 | .io_params = io, | |
165 | .radio_nr_params = radio_nr, | |
166 | .io_ports = aztech_ioports, | |
167 | .num_of_io_ports = ARRAY_SIZE(aztech_ioports), | |
168 | .region_size = 2, | |
169 | .card = "Aztech Radio", | |
170 | .ops = &aztech_ops, | |
171 | .has_stereo = true, | |
172 | .max_volume = 3, | |
1da177e4 LT |
173 | }; |
174 | ||
175 | static int __init aztech_init(void) | |
176 | { | |
3088fba8 | 177 | return isa_register_driver(&aztech_driver.driver, AZTECH_MAX); |
1da177e4 LT |
178 | } |
179 | ||
e697e12e | 180 | static void __exit aztech_exit(void) |
1da177e4 | 181 | { |
3088fba8 | 182 | isa_unregister_driver(&aztech_driver.driver); |
1da177e4 LT |
183 | } |
184 | ||
185 | module_init(aztech_init); | |
e697e12e | 186 | module_exit(aztech_exit); |