radio-aztech.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. /* radio-aztech.c - Aztech radio card driver for Linux 2.2
  2. *
  3. * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
  4. * Adapted to support the Video for Linux API by
  5. * Russell Kroll <rkroll@exploits.org>. Based on original tuner code by:
  6. *
  7. * Quay Ly
  8. * Donald Song
  9. * Jason Lewis (jlewis@twilight.vtc.vsc.edu)
  10. * Scott McGrath (smcgrath@twilight.vtc.vsc.edu)
  11. * William McGrath (wmcgrath@twilight.vtc.vsc.edu)
  12. *
  13. * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/
  14. * along with more information on the card itself.
  15. *
  16. * History:
  17. * 1999-02-24 Russell Kroll <rkroll@exploits.org>
  18. * Fine tuning/VIDEO_TUNER_LOW
  19. * Range expanded to 87-108 MHz (from 87.9-107.8)
  20. *
  21. * Notable changes from the original source:
  22. * - includes stripped down to the essentials
  23. * - for loops used as delays replaced with udelay()
  24. * - #defines removed, changed to static values
  25. * - tuning structure changed - no more character arrays, other changes
  26. */
  27. #include <linux/module.h> /* Modules */
  28. #include <linux/init.h> /* Initdata */
  29. #include <linux/ioport.h> /* request_region */
  30. #include <linux/delay.h> /* udelay */
  31. #include <linux/videodev2.h> /* kernel radio structs */
  32. #include <linux/version.h> /* for KERNEL_VERSION MACRO */
  33. #include <linux/io.h> /* outb, outb_p */
  34. #include <linux/uaccess.h> /* copy to/from user */
  35. #include <media/v4l2-device.h>
  36. #include <media/v4l2-ioctl.h>
  37. MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
  38. MODULE_DESCRIPTION("A driver for the Aztech radio card.");
  39. MODULE_LICENSE("GPL");
  40. /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
  41. #ifndef CONFIG_RADIO_AZTECH_PORT
  42. #define CONFIG_RADIO_AZTECH_PORT -1
  43. #endif
  44. static int io = CONFIG_RADIO_AZTECH_PORT;
  45. static int radio_nr = -1;
  46. static int radio_wait_time = 1000;
  47. module_param(io, int, 0);
  48. module_param(radio_nr, int, 0);
  49. MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
  50. #define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
  51. struct aztech
  52. {
  53. struct v4l2_device v4l2_dev;
  54. struct video_device vdev;
  55. int io;
  56. int curvol;
  57. unsigned long curfreq;
  58. int stereo;
  59. struct mutex lock;
  60. };
  61. static struct aztech aztech_card;
  62. static int volconvert(int level)
  63. {
  64. level >>= 14; /* Map 16bits down to 2 bit */
  65. level &= 3;
  66. /* convert to card-friendly values */
  67. switch (level) {
  68. case 0:
  69. return 0;
  70. case 1:
  71. return 1;
  72. case 2:
  73. return 4;
  74. case 3:
  75. return 5;
  76. }
  77. return 0; /* Quieten gcc */
  78. }
  79. static void send_0_byte(struct aztech *az)
  80. {
  81. udelay(radio_wait_time);
  82. outb_p(2 + volconvert(az->curvol), az->io);
  83. outb_p(64 + 2 + volconvert(az->curvol), az->io);
  84. }
  85. static void send_1_byte(struct aztech *az)
  86. {
  87. udelay (radio_wait_time);
  88. outb_p(128 + 2 + volconvert(az->curvol), az->io);
  89. outb_p(128 + 64 + 2 + volconvert(az->curvol), az->io);
  90. }
  91. static int az_setvol(struct aztech *az, int vol)
  92. {
  93. mutex_lock(&az->lock);
  94. outb(volconvert(vol), az->io);
  95. mutex_unlock(&az->lock);
  96. return 0;
  97. }
  98. /* thanks to Michael Dwyer for giving me a dose of clues in
  99. * the signal strength department..
  100. *
  101. * This card has a stereo bit - bit 0 set = mono, not set = stereo
  102. * It also has a "signal" bit - bit 1 set = bad signal, not set = good
  103. *
  104. */
  105. static int az_getsigstr(struct aztech *az)
  106. {
  107. int sig = 1;
  108. mutex_lock(&az->lock);
  109. if (inb(az->io) & 2) /* bit set = no signal present */
  110. sig = 0;
  111. mutex_unlock(&az->lock);
  112. return sig;
  113. }
  114. static int az_getstereo(struct aztech *az)
  115. {
  116. int stereo = 1;
  117. mutex_lock(&az->lock);
  118. if (inb(az->io) & 1) /* bit set = mono */
  119. stereo = 0;
  120. mutex_unlock(&az->lock);
  121. return stereo;
  122. }
  123. static int az_setfreq(struct aztech *az, unsigned long frequency)
  124. {
  125. int i;
  126. mutex_lock(&az->lock);
  127. az->curfreq = frequency;
  128. frequency += 171200; /* Add 10.7 MHz IF */
  129. frequency /= 800; /* Convert to 50 kHz units */
  130. send_0_byte(az); /* 0: LSB of frequency */
  131. for (i = 0; i < 13; i++) /* : frequency bits (1-13) */
  132. if (frequency & (1 << i))
  133. send_1_byte(az);
  134. else
  135. send_0_byte(az);
  136. send_0_byte(az); /* 14: test bit - always 0 */
  137. send_0_byte(az); /* 15: test bit - always 0 */
  138. send_0_byte(az); /* 16: band data 0 - always 0 */
  139. if (az->stereo) /* 17: stereo (1 to enable) */
  140. send_1_byte(az);
  141. else
  142. send_0_byte(az);
  143. send_1_byte(az); /* 18: band data 1 - unknown */
  144. send_0_byte(az); /* 19: time base - always 0 */
  145. send_0_byte(az); /* 20: spacing (0 = 25 kHz) */
  146. send_1_byte(az); /* 21: spacing (1 = 25 kHz) */
  147. send_0_byte(az); /* 22: spacing (0 = 25 kHz) */
  148. send_1_byte(az); /* 23: AM/FM (FM = 1, always) */
  149. /* latch frequency */
  150. udelay(radio_wait_time);
  151. outb_p(128 + 64 + volconvert(az->curvol), az->io);
  152. mutex_unlock(&az->lock);
  153. return 0;
  154. }
  155. static int vidioc_querycap(struct file *file, void *priv,
  156. struct v4l2_capability *v)
  157. {
  158. strlcpy(v->driver, "radio-aztech", sizeof(v->driver));
  159. strlcpy(v->card, "Aztech Radio", sizeof(v->card));
  160. strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
  161. v->version = RADIO_VERSION;
  162. v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
  163. return 0;
  164. }
  165. static int vidioc_g_tuner(struct file *file, void *priv,
  166. struct v4l2_tuner *v)
  167. {
  168. struct aztech *az = video_drvdata(file);
  169. if (v->index > 0)
  170. return -EINVAL;
  171. strlcpy(v->name, "FM", sizeof(v->name));
  172. v->type = V4L2_TUNER_RADIO;
  173. v->rangelow = 87 * 16000;
  174. v->rangehigh = 108 * 16000;
  175. v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
  176. v->capability = V4L2_TUNER_CAP_LOW;
  177. if (az_getstereo(az))
  178. v->audmode = V4L2_TUNER_MODE_STEREO;
  179. else
  180. v->audmode = V4L2_TUNER_MODE_MONO;
  181. v->signal = 0xFFFF * az_getsigstr(az);
  182. return 0;
  183. }
  184. static int vidioc_s_tuner(struct file *file, void *priv,
  185. struct v4l2_tuner *v)
  186. {
  187. return v->index ? -EINVAL : 0;
  188. }
  189. static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
  190. {
  191. *i = 0;
  192. return 0;
  193. }
  194. static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
  195. {
  196. return i ? -EINVAL : 0;
  197. }
  198. static int vidioc_g_audio(struct file *file, void *priv,
  199. struct v4l2_audio *a)
  200. {
  201. a->index = 0;
  202. strlcpy(a->name, "Radio", sizeof(a->name));
  203. a->capability = V4L2_AUDCAP_STEREO;
  204. return 0;
  205. }
  206. static int vidioc_s_audio(struct file *file, void *priv,
  207. struct v4l2_audio *a)
  208. {
  209. return a->index ? -EINVAL : 0;
  210. }
  211. static int vidioc_s_frequency(struct file *file, void *priv,
  212. struct v4l2_frequency *f)
  213. {
  214. struct aztech *az = video_drvdata(file);
  215. az_setfreq(az, f->frequency);
  216. return 0;
  217. }
  218. static int vidioc_g_frequency(struct file *file, void *priv,
  219. struct v4l2_frequency *f)
  220. {
  221. struct aztech *az = video_drvdata(file);
  222. f->type = V4L2_TUNER_RADIO;
  223. f->frequency = az->curfreq;
  224. return 0;
  225. }
  226. static int vidioc_queryctrl(struct file *file, void *priv,
  227. struct v4l2_queryctrl *qc)
  228. {
  229. switch (qc->id) {
  230. case V4L2_CID_AUDIO_MUTE:
  231. return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
  232. case V4L2_CID_AUDIO_VOLUME:
  233. return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
  234. }
  235. return -EINVAL;
  236. }
  237. static int vidioc_g_ctrl(struct file *file, void *priv,
  238. struct v4l2_control *ctrl)
  239. {
  240. struct aztech *az = video_drvdata(file);
  241. switch (ctrl->id) {
  242. case V4L2_CID_AUDIO_MUTE:
  243. if (az->curvol == 0)
  244. ctrl->value = 1;
  245. else
  246. ctrl->value = 0;
  247. return 0;
  248. case V4L2_CID_AUDIO_VOLUME:
  249. ctrl->value = az->curvol * 6554;
  250. return 0;
  251. }
  252. return -EINVAL;
  253. }
  254. static int vidioc_s_ctrl(struct file *file, void *priv,
  255. struct v4l2_control *ctrl)
  256. {
  257. struct aztech *az = video_drvdata(file);
  258. switch (ctrl->id) {
  259. case V4L2_CID_AUDIO_MUTE:
  260. if (ctrl->value)
  261. az_setvol(az, 0);
  262. else
  263. az_setvol(az, az->curvol);
  264. return 0;
  265. case V4L2_CID_AUDIO_VOLUME:
  266. az_setvol(az, ctrl->value);
  267. return 0;
  268. }
  269. return -EINVAL;
  270. }
  271. static int aztech_open(struct file *file)
  272. {
  273. return 0;
  274. }
  275. static int aztech_release(struct file *file)
  276. {
  277. return 0;
  278. }
  279. static const struct v4l2_file_operations aztech_fops = {
  280. .owner = THIS_MODULE,
  281. .open = aztech_open,
  282. .release = aztech_release,
  283. .ioctl = video_ioctl2,
  284. };
  285. static const struct v4l2_ioctl_ops aztech_ioctl_ops = {
  286. .vidioc_querycap = vidioc_querycap,
  287. .vidioc_g_tuner = vidioc_g_tuner,
  288. .vidioc_s_tuner = vidioc_s_tuner,
  289. .vidioc_g_audio = vidioc_g_audio,
  290. .vidioc_s_audio = vidioc_s_audio,
  291. .vidioc_g_input = vidioc_g_input,
  292. .vidioc_s_input = vidioc_s_input,
  293. .vidioc_g_frequency = vidioc_g_frequency,
  294. .vidioc_s_frequency = vidioc_s_frequency,
  295. .vidioc_queryctrl = vidioc_queryctrl,
  296. .vidioc_g_ctrl = vidioc_g_ctrl,
  297. .vidioc_s_ctrl = vidioc_s_ctrl,
  298. };
  299. static int __init aztech_init(void)
  300. {
  301. struct aztech *az = &aztech_card;
  302. struct v4l2_device *v4l2_dev = &az->v4l2_dev;
  303. int res;
  304. strlcpy(v4l2_dev->name, "aztech", sizeof(v4l2_dev->name));
  305. az->io = io;
  306. if (az->io == -1) {
  307. v4l2_err(v4l2_dev, "you must set an I/O address with io=0x???\n");
  308. return -EINVAL;
  309. }
  310. if (!request_region(az->io, 2, "aztech")) {
  311. v4l2_err(v4l2_dev, "port 0x%x already in use\n", az->io);
  312. return -EBUSY;
  313. }
  314. res = v4l2_device_register(NULL, v4l2_dev);
  315. if (res < 0) {
  316. release_region(az->io, 2);
  317. v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
  318. return res;
  319. }
  320. mutex_init(&az->lock);
  321. strlcpy(az->vdev.name, v4l2_dev->name, sizeof(az->vdev.name));
  322. az->vdev.v4l2_dev = v4l2_dev;
  323. az->vdev.fops = &aztech_fops;
  324. az->vdev.ioctl_ops = &aztech_ioctl_ops;
  325. az->vdev.release = video_device_release_empty;
  326. video_set_drvdata(&az->vdev, az);
  327. if (video_register_device(&az->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
  328. v4l2_device_unregister(v4l2_dev);
  329. release_region(az->io, 2);
  330. return -EINVAL;
  331. }
  332. v4l2_info(v4l2_dev, "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n");
  333. /* mute card - prevents noisy bootups */
  334. outb(0, az->io);
  335. return 0;
  336. }
  337. static void __exit aztech_exit(void)
  338. {
  339. struct aztech *az = &aztech_card;
  340. video_unregister_device(&az->vdev);
  341. v4l2_device_unregister(&az->v4l2_dev);
  342. release_region(az->io, 2);
  343. }
  344. module_init(aztech_init);
  345. module_exit(aztech_exit);