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) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   3)  * Miro PCM20 radio driver for Linux radio support
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   4)  * (c) 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   5)  * Thanks to Norberto Pellici for the ACI device interface specification
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   6)  * The API part is based on the radiotrack driver by M. Kirkwood
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   7)  * This driver relies on the aci mixer provided by the snd-miro
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   8)  * ALSA driver.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   9)  * Look there for further info...
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  10)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  11)  * From the original miro RDS sources:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  12)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  13)  *  (c) 2001 Robert Siemer <Robert.Siemer@gmx.de>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  14)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  15)  *  Many thanks to Fred Seidel <seidel@metabox.de>, the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  16)  *  designer of the RDS decoder hardware. With his help
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  17)  *  I was able to code this driver.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  18)  *  Thanks also to Norberto Pellicci, Dominic Mounteney
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  19)  *  <DMounteney@pinnaclesys.com> and www.teleauskunft.de
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  20)  *  for good hints on finding Fred. It was somewhat hard
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  21)  *  to locate him here in Germany... [:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  22)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  23)  * This code has been reintroduced and converted to use
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  24)  * the new V4L2 RDS API by:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  25)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  26)  * Hans Verkuil <hans.verkuil@cisco.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  27)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  28) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  29) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  30) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  31) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  32) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  33) #include <linux/videodev2.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  34) #include <linux/kthread.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  35) #include <media/v4l2-device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  36) #include <media/v4l2-ioctl.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  37) #include <media/v4l2-ctrls.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  38) #include <media/v4l2-fh.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  39) #include <media/v4l2-event.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  40) #include <sound/aci.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  41) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  42) #define RDS_DATASHIFT          2   /* Bit 2 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  43) #define RDS_DATAMASK        (1 << RDS_DATASHIFT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  44) #define RDS_BUSYMASK        0x10   /* Bit 4 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  45) #define RDS_CLOCKMASK       0x08   /* Bit 3 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  46) #define RDS_DATA(x)         (((x) >> RDS_DATASHIFT) & 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  47) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  48) #define RDS_STATUS      0x01
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  49) #define RDS_STATIONNAME 0x02
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  50) #define RDS_TEXT        0x03
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  51) #define RDS_ALTFREQ     0x04
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  52) #define RDS_TIMEDATE    0x05
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  53) #define RDS_PI_CODE     0x06
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  54) #define RDS_PTYTATP     0x07
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  55) #define RDS_RESET       0x08
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  56) #define RDS_RXVALUE     0x09
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  57) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  58) static int radio_nr = -1;
^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) MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX).  Default: -1 (autodetect)");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  61) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  62) struct pcm20 {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  63) 	struct v4l2_device v4l2_dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  64) 	struct video_device vdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  65) 	struct v4l2_ctrl_handler ctrl_handler;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  66) 	struct v4l2_ctrl *rds_pty;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  67) 	struct v4l2_ctrl *rds_ps_name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  68) 	struct v4l2_ctrl *rds_radio_test;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  69) 	struct v4l2_ctrl *rds_ta;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  70) 	struct v4l2_ctrl *rds_tp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  71) 	struct v4l2_ctrl *rds_ms;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  72) 	/* thread for periodic RDS status checking */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  73) 	struct task_struct *kthread;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  74) 	unsigned long freq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  75) 	u32 audmode;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  76) 	struct snd_miro_aci *aci;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  77) 	struct mutex lock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  78) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  79) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  80) static struct pcm20 pcm20_card = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  81) 	.freq = 87 * 16000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  82) 	.audmode = V4L2_TUNER_MODE_STEREO,
^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) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  86) static int rds_waitread(struct snd_miro_aci *aci)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  87) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  88) 	u8 byte;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  89) 	int i = 2000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  90) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  91) 	do {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  92) 		byte = inb(aci->aci_port + ACI_REG_RDS);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  93) 		i--;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  94) 	} while ((byte & RDS_BUSYMASK) && i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  95) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  96) 	/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  97) 	 * It's magic, but without this the data that you read later on
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  98) 	 * is unreliable and full of bit errors. With this 1 usec delay
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  99) 	 * everything is fine.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) 	 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) 	udelay(1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) 	return i ? byte : -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) static int rds_rawwrite(struct snd_miro_aci *aci, u8 byte)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) 	if (rds_waitread(aci) >= 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) 		outb(byte, aci->aci_port + ACI_REG_RDS);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) 		return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) 	return -1;
^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) static int rds_write(struct snd_miro_aci *aci, u8 byte)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) 	u8 sendbuffer[8];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) 	int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) 	for (i = 7; i >= 0; i--)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) 		sendbuffer[7 - i] = (byte & (1 << i)) ? RDS_DATAMASK : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) 	sendbuffer[0] |= RDS_CLOCKMASK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) 	for (i = 0; i < 8; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) 		rds_rawwrite(aci, sendbuffer[i]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) 	return 0;
^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 int rds_readcycle_nowait(struct snd_miro_aci *aci)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) 	outb(0, aci->aci_port + ACI_REG_RDS);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) 	return rds_waitread(aci);
^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) static int rds_readcycle(struct snd_miro_aci *aci)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) 	if (rds_rawwrite(aci, 0) < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) 		return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) 	return rds_waitread(aci);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) static int rds_ack(struct snd_miro_aci *aci)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) 	int i = rds_readcycle(aci);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) 	if (i < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) 		return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) 	if (i & RDS_DATAMASK)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) 		return 0;  /* ACK  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) 	return 1;  /* NACK */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) static int rds_cmd(struct snd_miro_aci *aci, u8 cmd, u8 databuffer[], u8 datasize)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) 	int i, j;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) 	rds_write(aci, cmd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) 	/* RDS_RESET doesn't need further processing */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) 	if (cmd == RDS_RESET)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) 		return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) 	if (rds_ack(aci))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) 		return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) 	if (datasize == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) 		return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) 	/* to be able to use rds_readcycle_nowait()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) 	   I have to waitread() here */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) 	if (rds_waitread(aci) < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) 		return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) 	memset(databuffer, 0, datasize);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) 	for (i = 0; i < 8 * datasize; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) 		j = rds_readcycle_nowait(aci);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) 		if (j < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) 			return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) 		databuffer[i / 8] |= RDS_DATA(j) << (7 - (i % 8));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) 	unsigned char freql;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) 	unsigned char freqh;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) 	struct snd_miro_aci *aci = dev->aci;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) 	freq /= 160;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) 	if (!(aci->aci_version == 0x07 || aci->aci_version >= 0xb0))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) 		freq /= 10;  /* I don't know exactly which version
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) 			      * needs this hack */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) 	freql = freq & 0xff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) 	freqh = freq >> 8;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) 	rds_cmd(aci, RDS_RESET, NULL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) 	return snd_aci_cmd(aci, ACI_WRITE_TUNE, freql, freqh);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) static int vidioc_querycap(struct file *file, void *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) 				struct v4l2_capability *v)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) 	struct pcm20 *dev = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) 	strscpy(v->driver, "Miro PCM20", sizeof(v->driver));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) 	strscpy(v->card, "Miro PCM20", sizeof(v->card));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) 	snprintf(v->bus_info, sizeof(v->bus_info), "ISA:%s", dev->v4l2_dev.name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) static bool sanitize(char *p, int size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) 	int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) 	bool ret = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) 	for (i = 0; i < size; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) 		if (p[i] < 32) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) 			p[i] = ' ';
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) 			ret = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) 	return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) static int vidioc_g_tuner(struct file *file, void *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) 				struct v4l2_tuner *v)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) 	struct pcm20 *dev = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) 	int res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) 	u8 buf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) 	if (v->index)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) 	strscpy(v->name, "FM", sizeof(v->name));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) 	v->type = V4L2_TUNER_RADIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) 	v->rangelow = 87*16000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) 	v->rangehigh = 108*16000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) 	res = snd_aci_cmd(dev->aci, ACI_READ_TUNERSTATION, -1, -1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) 	v->signal = (res & 0x80) ? 0 : 0xffff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) 	/* Note: stereo detection does not work if the audio is muted,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) 	   it will default to mono in that case. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) 	res = snd_aci_cmd(dev->aci, ACI_READ_TUNERSTEREO, -1, -1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) 	v->rxsubchans = (res & 0x40) ? V4L2_TUNER_SUB_MONO :
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) 					V4L2_TUNER_SUB_STEREO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) 	v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) 			V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_CONTROLS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) 	v->audmode = dev->audmode;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) 	res = rds_cmd(dev->aci, RDS_RXVALUE, &buf, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) 	if (res >= 0 && buf)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) 		v->rxsubchans |= V4L2_TUNER_SUB_RDS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) static int vidioc_s_tuner(struct file *file, void *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) 				const struct v4l2_tuner *v)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) 	struct pcm20 *dev = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) 	if (v->index)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) 	if (v->audmode > V4L2_TUNER_MODE_STEREO)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) 		dev->audmode = V4L2_TUNER_MODE_STEREO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) 	else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) 		dev->audmode = v->audmode;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) 	snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) 			dev->audmode == V4L2_TUNER_MODE_MONO, -1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) static int vidioc_g_frequency(struct file *file, void *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) 				struct v4l2_frequency *f)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) 	struct pcm20 *dev = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) 	if (f->tuner != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) 	f->type = V4L2_TUNER_RADIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) 	f->frequency = dev->freq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) static int vidioc_s_frequency(struct file *file, void *priv,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) 				const struct v4l2_frequency *f)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) 	struct pcm20 *dev = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) 	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) 	dev->freq = clamp_t(u32, f->frequency, 87 * 16000U, 108 * 16000U);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) 	pcm20_setfreq(dev, dev->freq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) static int pcm20_s_ctrl(struct v4l2_ctrl *ctrl)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) 	struct pcm20 *dev = container_of(ctrl->handler, struct pcm20, ctrl_handler);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) 	switch (ctrl->id) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) 	case V4L2_CID_AUDIO_MUTE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) 		snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, ctrl->val, -1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) 		return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) 	return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) static int pcm20_thread(void *data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) 	struct pcm20 *dev = data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) 	const unsigned no_rds_start_counter = 5;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) 	const unsigned sleep_msecs = 2000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) 	unsigned no_rds_counter = no_rds_start_counter;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) 	for (;;) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) 		char text_buffer[66];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) 		u8 buf;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) 		int res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) 		msleep_interruptible(sleep_msecs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) 		if (kthread_should_stop())
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) 			break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) 		res = rds_cmd(dev->aci, RDS_RXVALUE, &buf, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) 		if (res)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) 			continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) 		if (buf == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) 			if (no_rds_counter == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) 				continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) 			no_rds_counter--;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) 			if (no_rds_counter)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) 				continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) 			/*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) 			 * No RDS seen for no_rds_start_counter * sleep_msecs
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) 			 * milliseconds, clear all RDS controls to their
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) 			 * default values.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) 			 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) 			v4l2_ctrl_s_ctrl_string(dev->rds_ps_name, "");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) 			v4l2_ctrl_s_ctrl(dev->rds_ms, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) 			v4l2_ctrl_s_ctrl(dev->rds_ta, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) 			v4l2_ctrl_s_ctrl(dev->rds_tp, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) 			v4l2_ctrl_s_ctrl(dev->rds_pty, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) 			v4l2_ctrl_s_ctrl_string(dev->rds_radio_test, "");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) 			continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) 		no_rds_counter = no_rds_start_counter;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) 		res = rds_cmd(dev->aci, RDS_STATUS, &buf, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) 		if (res)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) 			continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) 		if ((buf >> 3) & 1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) 			res = rds_cmd(dev->aci, RDS_STATIONNAME, text_buffer, 8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) 			text_buffer[8] = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) 			if (!res && sanitize(text_buffer, 8))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) 				v4l2_ctrl_s_ctrl_string(dev->rds_ps_name, text_buffer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) 		if ((buf >> 6) & 1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) 			u8 pty;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) 			res = rds_cmd(dev->aci, RDS_PTYTATP, &pty, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) 			if (!res) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) 				v4l2_ctrl_s_ctrl(dev->rds_ms, !!(pty & 0x01));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) 				v4l2_ctrl_s_ctrl(dev->rds_ta, !!(pty & 0x02));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) 				v4l2_ctrl_s_ctrl(dev->rds_tp, !!(pty & 0x80));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) 				v4l2_ctrl_s_ctrl(dev->rds_pty, (pty >> 2) & 0x1f);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) 			}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) 		if ((buf >> 4) & 1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) 			res = rds_cmd(dev->aci, RDS_TEXT, text_buffer, 65);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) 			text_buffer[65] = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) 			if (!res && sanitize(text_buffer + 1, 64))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) 				v4l2_ctrl_s_ctrl_string(dev->rds_radio_test, text_buffer + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) static int pcm20_open(struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) 	struct pcm20 *dev = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) 	int res = v4l2_fh_open(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) 	if (!res && v4l2_fh_is_singular_file(file) &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) 	    IS_ERR_OR_NULL(dev->kthread)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) 		dev->kthread = kthread_run(pcm20_thread, dev, "%s",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) 					   dev->v4l2_dev.name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) 		if (IS_ERR(dev->kthread)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) 			v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) 			v4l2_fh_release(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) 			return PTR_ERR(dev->kthread);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) 		}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) 	return res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) static int pcm20_release(struct file *file)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) 	struct pcm20 *dev = video_drvdata(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) 	if (v4l2_fh_is_singular_file(file) && !IS_ERR_OR_NULL(dev->kthread)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) 		kthread_stop(dev->kthread);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) 		dev->kthread = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) 	return v4l2_fh_release(file);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) static const struct v4l2_file_operations pcm20_fops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) 	.owner		= THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) 	.open		= pcm20_open,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) 	.poll		= v4l2_ctrl_poll,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) 	.release	= pcm20_release,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) 	.unlocked_ioctl	= video_ioctl2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) static const struct v4l2_ioctl_ops pcm20_ioctl_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) 	.vidioc_querycap    = vidioc_querycap,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) 	.vidioc_g_tuner     = vidioc_g_tuner,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) 	.vidioc_s_tuner     = vidioc_s_tuner,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) 	.vidioc_g_frequency = vidioc_g_frequency,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) 	.vidioc_s_frequency = vidioc_s_frequency,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) 	.vidioc_log_status  = v4l2_ctrl_log_status,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) 	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) 	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) static const struct v4l2_ctrl_ops pcm20_ctrl_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429) 	.s_ctrl = pcm20_s_ctrl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) static int __init pcm20_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) 	struct pcm20 *dev = &pcm20_card;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) 	struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) 	struct v4l2_ctrl_handler *hdl;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) 	int res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) 	dev->aci = snd_aci_get_aci();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) 	if (dev->aci == NULL) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) 		v4l2_err(v4l2_dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) 			 "you must load the snd-miro driver first!\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) 		return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445) 	strscpy(v4l2_dev->name, "radio-miropcm20", sizeof(v4l2_dev->name));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) 	mutex_init(&dev->lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) 	res = v4l2_device_register(NULL, v4l2_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) 	if (res < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) 		v4l2_err(v4l2_dev, "could not register v4l2_device\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) 	hdl = &dev->ctrl_handler;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) 	v4l2_ctrl_handler_init(hdl, 7);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456) 	v4l2_ctrl_new_std(hdl, &pcm20_ctrl_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) 			V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) 	dev->rds_pty = v4l2_ctrl_new_std(hdl, NULL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) 			V4L2_CID_RDS_RX_PTY, 0, 0x1f, 1, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) 	dev->rds_ps_name = v4l2_ctrl_new_std(hdl, NULL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461) 			V4L2_CID_RDS_RX_PS_NAME, 0, 8, 8, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) 	dev->rds_radio_test = v4l2_ctrl_new_std(hdl, NULL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) 			V4L2_CID_RDS_RX_RADIO_TEXT, 0, 64, 64, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) 	dev->rds_ta = v4l2_ctrl_new_std(hdl, NULL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465) 			V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT, 0, 1, 1, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) 	dev->rds_tp = v4l2_ctrl_new_std(hdl, NULL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) 			V4L2_CID_RDS_RX_TRAFFIC_PROGRAM, 0, 1, 1, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) 	dev->rds_ms = v4l2_ctrl_new_std(hdl, NULL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) 			V4L2_CID_RDS_RX_MUSIC_SPEECH, 0, 1, 1, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) 	v4l2_dev->ctrl_handler = hdl;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) 	if (hdl->error) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) 		res = hdl->error;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) 		v4l2_err(v4l2_dev, "Could not register control\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474) 		goto err_hdl;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) 	strscpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) 	dev->vdev.v4l2_dev = v4l2_dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478) 	dev->vdev.fops = &pcm20_fops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) 	dev->vdev.ioctl_ops = &pcm20_ioctl_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) 	dev->vdev.release = video_device_release_empty;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) 	dev->vdev.lock = &dev->lock;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482) 	dev->vdev.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483) 				V4L2_CAP_RDS_CAPTURE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) 	video_set_drvdata(&dev->vdev, dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485) 	snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486) 			dev->audmode == V4L2_TUNER_MODE_MONO, -1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487) 	pcm20_setfreq(dev, dev->freq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489) 	if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) 		goto err_hdl;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492) 	v4l2_info(v4l2_dev, "Mirosound PCM20 Radio tuner\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) err_hdl:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495) 	v4l2_ctrl_handler_free(hdl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) 	v4l2_device_unregister(v4l2_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) 	return -EINVAL;
^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) MODULE_AUTHOR("Ruurd Reitsma, Krzysztof Helt");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card.");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) static void __exit pcm20_cleanup(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506) 	struct pcm20 *dev = &pcm20_card;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508) 	video_unregister_device(&dev->vdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509) 	snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, 1, -1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) 	v4l2_ctrl_handler_free(&dev->ctrl_handler);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) 	v4l2_device_unregister(&dev->v4l2_dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514) module_init(pcm20_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) module_exit(pcm20_cleanup);