Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   2) /* radio-cadet.c - A video4linux driver for the ADS Cadet AM/FM Radio Card
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   3)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   4)  * by Fred Gleason <fredg@wava.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   5)  * Version 0.3.3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   6)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   7)  * (Loosely) based on code for the Aztech radio card by
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   8)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   9)  * Russell Kroll    (rkroll@exploits.org)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  10)  * Quay Ly
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  11)  * Donald Song
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  12)  * Jason Lewis      (jlewis@twilight.vtc.vsc.edu)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  13)  * Scott McGrath    (smcgrath@twilight.vtc.vsc.edu)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  14)  * William McGrath  (wmcgrath@twilight.vtc.vsc.edu)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  15)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  16)  * History:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  17)  * 2000-04-29	Russell Kroll <rkroll@exploits.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  18)  *		Added ISAPnP detection for Linux 2.3/2.4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  19)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  20)  * 2001-01-10	Russell Kroll <rkroll@exploits.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  21)  *		Removed dead CONFIG_RADIO_CADET_PORT code
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  22)  *		PnP detection on load is now default (no args necessary)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  23)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  24)  * 2002-01-17	Adam Belay <ambx1@neo.rr.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  25)  *		Updated to latest pnp code
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  26)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  27)  * 2003-01-31	Alan Cox <alan@lxorguk.ukuu.org.uk>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  28)  *		Cleaned up locking, delay code, general odds and ends
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  29)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  30)  * 2006-07-30	Hans J. Koch <koch@hjk-az.de>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  31)  *		Changed API to V4L2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  32)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  33) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  34) #include <linux/module.h>	/* Modules			*/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  35) #include <linux/init.h>		/* Initdata			*/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  36) #include <linux/ioport.h>	/* request_region		*/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  37) #include <linux/delay.h>	/* udelay			*/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  38) #include <linux/videodev2.h>	/* V4L2 API defs		*/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  39) #include <linux/param.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  40) #include <linux/pnp.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  41) #include <linux/sched.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  42) #include <linux/io.h>		/* outb, outb_p			*/
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  43) #include <media/v4l2-device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  44) #include <media/v4l2-ioctl.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  45) #include <media/v4l2-ctrls.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  46) #include <media/v4l2-fh.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  47) #include <media/v4l2-event.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  48) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  49) MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  50) MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card.");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  51) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  52) MODULE_VERSION("0.3.4");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  53) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  54) static int io = -1;		/* default to isapnp activation */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  55) static int radio_nr = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  56) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  57) module_param(io, int, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  58) MODULE_PARM_DESC(io, "I/O address of Cadet card (0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e)");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  59) module_param(radio_nr, int, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  60) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  61) #define RDS_BUFFER 256
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  62) #define RDS_RX_FLAG 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  63) #define MBS_RX_FLAG 2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  64) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  65) struct cadet {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  66) 	struct v4l2_device v4l2_dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  67) 	struct video_device vdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  68) 	struct v4l2_ctrl_handler ctrl_handler;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  69) 	int io;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  70) 	bool is_fm_band;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  71) 	u32 curfreq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  72) 	int tunestat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  73) 	int sigstrength;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  74) 	wait_queue_head_t read_queue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  75) 	struct timer_list readtimer;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  76) 	u8 rdsin, rdsout, rdsstat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  77) 	unsigned char rdsbuf[RDS_BUFFER];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  78) 	struct mutex lock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  79) 	int reading;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  80) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  81) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  82) static struct cadet cadet_card;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  83) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  84) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  85)  * Signal Strength Threshold Values
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  86)  * The V4L API spec does not define any particular unit for the signal
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  87)  * strength value.  These values are in microvolts of RF at the tuner's input.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  88)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  89) static u16 sigtable[2][4] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  90) 	{ 1835, 2621,  4128, 65535 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  91) 	{ 2185, 4369, 13107, 65535 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  92) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  93) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  94) static const struct v4l2_frequency_band bands[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  95) 	{
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  96) 		.index = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  97) 		.type = V4L2_TUNER_RADIO,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  98) 		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  99) 		.rangelow = 8320,      /* 520 kHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) 		.rangehigh = 26400,    /* 1650 kHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) 		.modulation = V4L2_BAND_MODULATION_AM,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) 	}, {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) 		.index = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) 		.type = V4L2_TUNER_RADIO,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) 		.capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) 			V4L2_TUNER_CAP_RDS_BLOCK_IO | V4L2_TUNER_CAP_LOW |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) 			V4L2_TUNER_CAP_FREQ_BANDS,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) 		.rangelow = 1400000,   /* 87.5 MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) 		.rangehigh = 1728000,  /* 108.0 MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) 		.modulation = V4L2_BAND_MODULATION_FM,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) 	},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) static int cadet_getstereo(struct cadet *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) 	int ret = V4L2_TUNER_SUB_MONO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) 	if (!dev->is_fm_band)	/* Only FM has stereo capability! */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) 		return V4L2_TUNER_SUB_MONO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) 	outb(7, dev->io);          /* Select tuner control */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) 	if ((inb(dev->io + 1) & 0x40) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) 		ret = V4L2_TUNER_SUB_STEREO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) 	return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) static unsigned cadet_gettune(struct cadet *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) 	int curvol, i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) 	unsigned fifo = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) 	 * Prepare for read
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) 	outb(7, dev->io);       /* Select tuner control */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) 	curvol = inb(dev->io + 1); /* Save current volume/mute setting */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) 	outb(0x00, dev->io + 1);  /* Ensure WRITE-ENABLE is LOW */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) 	dev->tunestat = 0xffff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) 	 * Read the shift register
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) 	for (i = 0; i < 25; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) 		fifo = (fifo << 1) | ((inb(dev->io + 1) >> 7) & 0x01);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) 		if (i < 24) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) 			outb(0x01, dev->io + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) 			dev->tunestat &= inb(dev->io + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) 			outb(0x00, dev->io + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) 	 * Restore volume/mute setting
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) 	outb(curvol, dev->io + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) 	return fifo;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) static unsigned cadet_getfreq(struct cadet *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) 	int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) 	unsigned freq = 0, test, fifo = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) 	 * Read current tuning
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) 	fifo = cadet_gettune(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) 	 * Convert to actual frequency
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) 	if (!dev->is_fm_band)    /* AM */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) 		return ((fifo & 0x7fff) - 450) * 16;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) 	test = 12500;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) 	for (i = 0; i < 14; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) 		if ((fifo & 0x01) != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) 			freq += test;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) 		test = test << 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) 		fifo = fifo >> 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) 	freq -= 10700000;           /* IF frequency is 10.7 MHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) 	freq = (freq * 16) / 1000;   /* Make it 1/16 kHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) 	return freq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) static void cadet_settune(struct cadet *dev, unsigned fifo)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) 	int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) 	unsigned test;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) 	outb(7, dev->io);                /* Select tuner control */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) 	 * Write the shift register
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) 	test = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) 	test = (fifo >> 23) & 0x02;      /* Align data for SDO */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) 	test |= 0x1c;                /* SDM=1, SWE=1, SEN=1, SCK=0 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) 	outb(7, dev->io);                /* Select tuner control */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) 	outb(test, dev->io + 1);           /* Initialize for write */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) 	for (i = 0; i < 25; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) 		test |= 0x01;              /* Toggle SCK High */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) 		outb(test, dev->io + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) 		test &= 0xfe;              /* Toggle SCK Low */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) 		outb(test, dev->io + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) 		fifo = fifo << 1;            /* Prepare the next bit */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) 		test = 0x1c | ((fifo >> 23) & 0x02);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) 		outb(test, dev->io + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) static void cadet_setfreq(struct cadet *dev, unsigned freq)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) 	unsigned fifo;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) 	int i, j, test;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) 	int curvol;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) 	freq = clamp(freq, bands[dev->is_fm_band].rangelow,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) 			   bands[dev->is_fm_band].rangehigh);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) 	dev->curfreq = freq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) 	 * Formulate a fifo command
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) 	fifo = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) 	if (dev->is_fm_band) {    /* FM */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) 		test = 102400;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) 		freq = freq / 16;       /* Make it kHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) 		freq += 10700;               /* IF is 10700 kHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) 		for (i = 0; i < 14; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) 			fifo = fifo << 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) 			if (freq >= test) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) 				fifo |= 0x01;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) 				freq -= test;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) 			}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) 			test = test >> 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) 	} else {	/* AM */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) 		fifo = (freq / 16) + 450;	/* Make it kHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) 		fifo |= 0x100000;		/* Select AM Band */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) 	 * Save current volume/mute setting
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) 	outb(7, dev->io);                /* Select tuner control */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) 	curvol = inb(dev->io + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) 	 * Tune the card
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) 	for (j = 3; j > -1; j--) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) 		cadet_settune(dev, fifo | (j << 16));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) 		outb(7, dev->io);         /* Select tuner control */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) 		outb(curvol, dev->io + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) 		msleep(100);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) 		cadet_gettune(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) 		if ((dev->tunestat & 0x40) == 0) {   /* Tuned */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) 			dev->sigstrength = sigtable[dev->is_fm_band][j];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) 			goto reset_rds;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) 	dev->sigstrength = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) reset_rds:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) 	outb(3, dev->io);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) 	outb(inb(dev->io + 1) & 0x7f, dev->io + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) static bool cadet_has_rds_data(struct cadet *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) 	bool result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) 	mutex_lock(&dev->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) 	result = dev->rdsin != dev->rdsout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) 	mutex_unlock(&dev->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) 	return result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) static void cadet_handler(struct timer_list *t)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) 	struct cadet *dev = from_timer(dev, t, readtimer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) 	/* Service the RDS fifo */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) 	if (mutex_trylock(&dev->lock)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) 		outb(0x3, dev->io);       /* Select RDS Decoder Control */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) 		if ((inb(dev->io + 1) & 0x20) != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) 			pr_err("cadet: RDS fifo overflow\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) 		outb(0x80, dev->io);      /* Select RDS fifo */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) 		while ((inb(dev->io) & 0x80) != 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) 			dev->rdsbuf[dev->rdsin] = inb(dev->io + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) 			if (dev->rdsin + 1 != dev->rdsout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) 				dev->rdsin++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) 		mutex_unlock(&dev->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) 	 * Service pending read
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) 	if (cadet_has_rds_data(dev))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) 		wake_up_interruptible(&dev->read_queue);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) 	 * Clean up and exit
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) 	dev->readtimer.expires = jiffies + msecs_to_jiffies(50);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) 	add_timer(&dev->readtimer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) static void cadet_start_rds(struct cadet *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) 	dev->rdsstat = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) 	outb(0x80, dev->io);        /* Select RDS fifo */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) 	timer_setup(&dev->readtimer, cadet_handler, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) 	dev->readtimer.expires = jiffies + msecs_to_jiffies(50);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) 	add_timer(&dev->readtimer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) static ssize_t cadet_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) 	struct cadet *dev = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) 	unsigned char readbuf[RDS_BUFFER];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) 	int i = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) 	mutex_lock(&dev->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) 	if (dev->rdsstat == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) 		cadet_start_rds(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) 	mutex_unlock(&dev->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) 	if (!cadet_has_rds_data(dev) && (file->f_flags & O_NONBLOCK))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) 		return -EWOULDBLOCK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) 	i = wait_event_interruptible(dev->read_queue, cadet_has_rds_data(dev));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) 	if (i)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) 		return i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) 	mutex_lock(&dev->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) 	while (i < count && dev->rdsin != dev->rdsout)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) 		readbuf[i++] = dev->rdsbuf[dev->rdsout++];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) 	mutex_unlock(&dev->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) 	if (i && copy_to_user(data, readbuf, i))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) 		return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) 	return i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) static int vidioc_querycap(struct file *file, void *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) 				struct v4l2_capability *v)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) 	strscpy(v->driver, "ADS Cadet", sizeof(v->driver));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) 	strscpy(v->card, "ADS Cadet", sizeof(v->card));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) 	strscpy(v->bus_info, "ISA:radio-cadet", sizeof(v->bus_info));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) static int vidioc_g_tuner(struct file *file, void *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) 				struct v4l2_tuner *v)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) 	struct cadet *dev = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) 	if (v->index)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) 	v->type = V4L2_TUNER_RADIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) 	strscpy(v->name, "Radio", sizeof(v->name));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) 	v->capability = bands[0].capability | bands[1].capability;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) 	v->rangelow = bands[0].rangelow;	   /* 520 kHz (start of AM band) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) 	v->rangehigh = bands[1].rangehigh;    /* 108.0 MHz (end of FM band) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) 	if (dev->is_fm_band) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) 		v->rxsubchans = cadet_getstereo(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) 		outb(3, dev->io);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) 		outb(inb(dev->io + 1) & 0x7f, dev->io + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) 		mdelay(100);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) 		outb(3, dev->io);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) 		if (inb(dev->io + 1) & 0x80)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) 			v->rxsubchans |= V4L2_TUNER_SUB_RDS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) 	} else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) 		v->rangelow = 8320;      /* 520 kHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) 		v->rangehigh = 26400;    /* 1650 kHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) 		v->rxsubchans = V4L2_TUNER_SUB_MONO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) 	v->audmode = V4L2_TUNER_MODE_STEREO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) 	v->signal = dev->sigstrength; /* We might need to modify scaling of this */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) static int vidioc_s_tuner(struct file *file, void *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) 				const struct v4l2_tuner *v)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) 	return v->index ? -EINVAL : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) static int vidioc_enum_freq_bands(struct file *file, void *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) 				struct v4l2_frequency_band *band)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) 	if (band->tuner)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) 	if (band->index >= ARRAY_SIZE(bands))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) 	*band = bands[band->index];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) static int vidioc_g_frequency(struct file *file, void *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) 				struct v4l2_frequency *f)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) 	struct cadet *dev = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) 	if (f->tuner)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) 	f->type = V4L2_TUNER_RADIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) 	f->frequency = dev->curfreq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) static int vidioc_s_frequency(struct file *file, void *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) 				const struct v4l2_frequency *f)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) 	struct cadet *dev = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) 	if (f->tuner)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) 	dev->is_fm_band =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) 		f->frequency >= (bands[0].rangehigh + bands[1].rangelow) / 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) 	cadet_setfreq(dev, f->frequency);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) static int cadet_s_ctrl(struct v4l2_ctrl *ctrl)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) 	struct cadet *dev = container_of(ctrl->handler, struct cadet, ctrl_handler);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) 	switch (ctrl->id) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) 	case V4L2_CID_AUDIO_MUTE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) 		outb(7, dev->io);                /* Select tuner control */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) 		if (ctrl->val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) 			outb(0x00, dev->io + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) 		else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) 			outb(0x20, dev->io + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) 		return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) 	return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) static int cadet_open(struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) 	struct cadet *dev = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) 	int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) 	mutex_lock(&dev->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) 	err = v4l2_fh_open(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) 	if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) 		goto fail;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461) 	if (v4l2_fh_is_singular_file(file))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) 		init_waitqueue_head(&dev->read_queue);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) fail:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) 	mutex_unlock(&dev->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465) 	return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) static int cadet_release(struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) 	struct cadet *dev = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) 	mutex_lock(&dev->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) 	if (v4l2_fh_is_singular_file(file) && dev->rdsstat) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474) 		del_timer_sync(&dev->readtimer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475) 		dev->rdsstat = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) 	v4l2_fh_release(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478) 	mutex_unlock(&dev->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482) static __poll_t cadet_poll(struct file *file, struct poll_table_struct *wait)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) 	struct cadet *dev = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485) 	__poll_t req_events = poll_requested_events(wait);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486) 	__poll_t res = v4l2_ctrl_poll(file, wait);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488) 	poll_wait(file, &dev->read_queue, wait);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489) 	if (dev->rdsstat == 0 && (req_events & (EPOLLIN | EPOLLRDNORM))) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) 		mutex_lock(&dev->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491) 		if (dev->rdsstat == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492) 			cadet_start_rds(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) 		mutex_unlock(&dev->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495) 	if (cadet_has_rds_data(dev))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) 		res |= EPOLLIN | EPOLLRDNORM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) 	return res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) static const struct v4l2_file_operations cadet_fops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502) 	.owner		= THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503) 	.open		= cadet_open,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) 	.release	= cadet_release,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) 	.read		= cadet_read,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506) 	.unlocked_ioctl	= video_ioctl2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) 	.poll		= cadet_poll,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) static const struct v4l2_ioctl_ops cadet_ioctl_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) 	.vidioc_querycap    = vidioc_querycap,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512) 	.vidioc_g_tuner     = vidioc_g_tuner,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513) 	.vidioc_s_tuner     = vidioc_s_tuner,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514) 	.vidioc_g_frequency = vidioc_g_frequency,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) 	.vidioc_s_frequency = vidioc_s_frequency,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516) 	.vidioc_enum_freq_bands = vidioc_enum_freq_bands,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517) 	.vidioc_log_status  = v4l2_ctrl_log_status,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518) 	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519) 	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522) static const struct v4l2_ctrl_ops cadet_ctrl_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523) 	.s_ctrl = cadet_s_ctrl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 524) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 525) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 526) #ifdef CONFIG_PNP
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528) static const struct pnp_device_id cadet_pnp_devices[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529) 	/* ADS Cadet AM/FM Radio Card */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530) 	{.id = "MSM0c24", .driver_data = 0},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) 	{.id = ""}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534) MODULE_DEVICE_TABLE(pnp, cadet_pnp_devices);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536) static int cadet_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538) 	if (!dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539) 		return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) 	/* only support one device */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541) 	if (io > 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542) 		return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544) 	if (!pnp_port_valid(dev, 0))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545) 		return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) 	io = pnp_port_start(dev, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549) 	printk(KERN_INFO "radio-cadet: PnP reports device at %#x\n", io);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551) 	return io;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554) static struct pnp_driver cadet_pnp_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555) 	.name		= "radio-cadet",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556) 	.id_table	= cadet_pnp_devices,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557) 	.probe		= cadet_pnp_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 558) 	.remove		= NULL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 559) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 560) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561) #else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 562) static struct pnp_driver cadet_pnp_driver;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 563) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 564) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565) static void cadet_probe(struct cadet *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567) 	static int iovals[8] = { 0x330, 0x332, 0x334, 0x336, 0x338, 0x33a, 0x33c, 0x33e };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568) 	int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) 	for (i = 0; i < 8; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571) 		dev->io = iovals[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 572) 		if (request_region(dev->io, 2, "cadet-probe")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 573) 			cadet_setfreq(dev, bands[1].rangelow);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 574) 			if (cadet_getfreq(dev) == bands[1].rangelow) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575) 				release_region(dev->io, 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) 				return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577) 			}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 578) 			release_region(dev->io, 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 579) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 580) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 581) 	dev->io = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 582) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 583) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585)  * io should only be set if the user has used something like
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586)  * isapnp (the userspace program) to initialize this card for us
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589) static int __init cadet_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591) 	struct cadet *dev = &cadet_card;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) 	struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593) 	struct v4l2_ctrl_handler *hdl;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594) 	int res = -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596) 	strscpy(v4l2_dev->name, "cadet", sizeof(v4l2_dev->name));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597) 	mutex_init(&dev->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599) 	/* If a probe was requested then probe ISAPnP first (safest) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600) 	if (io < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 601) 		pnp_register_driver(&cadet_pnp_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 602) 	dev->io = io;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 603) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604) 	/* If that fails then probe unsafely if probe is requested */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605) 	if (dev->io < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606) 		cadet_probe(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608) 	/* Else we bail out */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609) 	if (dev->io < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610) #ifdef MODULE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 611) 		v4l2_err(v4l2_dev, "you must set an I/O address with io=0x330, 0x332, 0x334,\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 612) 		v4l2_err(v4l2_dev, "0x336, 0x338, 0x33a, 0x33c or 0x33e\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 613) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 614) 		goto fail;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 615) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 616) 	if (!request_region(dev->io, 2, "cadet"))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 617) 		goto fail;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 618) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 619) 	res = v4l2_device_register(NULL, v4l2_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 620) 	if (res < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 621) 		release_region(dev->io, 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 622) 		v4l2_err(v4l2_dev, "could not register v4l2_device\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 623) 		goto fail;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 624) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 625) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 626) 	hdl = &dev->ctrl_handler;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 627) 	v4l2_ctrl_handler_init(hdl, 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 628) 	v4l2_ctrl_new_std(hdl, &cadet_ctrl_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 629) 			V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 630) 	v4l2_dev->ctrl_handler = hdl;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 631) 	if (hdl->error) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 632) 		res = hdl->error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 633) 		v4l2_err(v4l2_dev, "Could not register controls\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 634) 		goto err_hdl;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 635) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 636) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 637) 	dev->is_fm_band = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 638) 	dev->curfreq = bands[dev->is_fm_band].rangelow;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 639) 	cadet_setfreq(dev, dev->curfreq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 640) 	strscpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 641) 	dev->vdev.v4l2_dev = v4l2_dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 642) 	dev->vdev.fops = &cadet_fops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 643) 	dev->vdev.ioctl_ops = &cadet_ioctl_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 644) 	dev->vdev.release = video_device_release_empty;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 645) 	dev->vdev.lock = &dev->lock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 646) 	dev->vdev.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 647) 				V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 648) 	video_set_drvdata(&dev->vdev, dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 649) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 650) 	res = video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 651) 	if (res < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 652) 		goto err_hdl;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 653) 	v4l2_info(v4l2_dev, "ADS Cadet Radio Card at 0x%x\n", dev->io);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 654) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 655) err_hdl:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 656) 	v4l2_ctrl_handler_free(hdl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 657) 	v4l2_device_unregister(v4l2_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 658) 	release_region(dev->io, 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 659) fail:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 660) 	pnp_unregister_driver(&cadet_pnp_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 661) 	return res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 662) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 663) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 664) static void __exit cadet_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 665) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 666) 	struct cadet *dev = &cadet_card;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 667) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 668) 	video_unregister_device(&dev->vdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 669) 	v4l2_ctrl_handler_free(&dev->ctrl_handler);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 670) 	v4l2_device_unregister(&dev->v4l2_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 671) 	outb(7, dev->io);	/* Mute */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 672) 	outb(0x00, dev->io + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 673) 	release_region(dev->io, 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 674) 	pnp_unregister_driver(&cadet_pnp_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 675) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 676) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 677) module_init(cadet_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 678) module_exit(cadet_exit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 679)