^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);