导航

    全志在线开发者论坛

    • 注册
    • 登录
    • 搜索
    • 版块
    • 话题
    • 在线文档
    • 社区主页

    D1s Tina Linux 使用RDA5807报错

    MR Series
    1
    3
    1573
    正在加载更多帖子
    • 从旧到新
    • 从新到旧
    • 最多赞同
    回复
    • 在新帖中回复
    登录后回复
    此主题已被删除。只有拥有主题管理权限的用户可以查看。
    • YuzukiTsuru
      柚木 鉉 LV 9 最后由 编辑

      bf704984-7fcd-4dda-b7de-b76d927adf7a-4353.png

      [    3.184258]sunxi-i2c sunxi-i2c2: sunxi-i2c2 supply twi not found, using dummy regulator
      [    3.202392]sunxi-i2c sunxi-i2c2: probe success
      [    3.208113]sunxi-i2c sunxi-i2c3: sunxi-i2c3 supply twi not found, using dummy regulator
      [    3.226532]radio-rda5807 3-0011: Found FM radio receiver
      [    3.232603]------------[ cut here ]------------
      [    3.237743]WARNING: CPU: 0 PID: 13 at drivers/media/v4l2-core/v4l2-dev.c:863 __video_register_device+0x5e/0xbda
      [    3.249124]Modules linked in:
      [    3.252491]CPU: 0 PID: 13 Comm: kworker/0:1 Not tainted 5.4.61 #206
      [    3.259582]Workqueue: events deferred_probe_work_func
      [    3.265235]sepc: ffffffe00032362c ra : ffffffe00034d6a2 sp : ffffffe003963710
      [    3.273275] gp : ffffffe0007131dc tp : ffffffe003940ac0 t0 : ffffffe002e66540
      [    3.281339] t1 : 0000000000000000 t2 : 000000007d547100 s0 : ffffffe003963790
      [    3.289342] s1 : ffffffe002e66108 a0 : ffffffe002e66108 a1 : 0000000000000002
      [    3.297354] a2 : ffffffffffffffff a3 : 0000000000000001 a4 : 0000000000000000
      [    3.305411] a5 : ffffffe002e66540 a6 : 0000000000000000 a7 : 0000000000000000
      [    3.313421] s2 : 0000000000000000 s3 : 0000000000000000 s4 : 0000000000000000
      [    3.321480] s5 : ffffffe002e52800 s6 : 0000000000000001 s7 : ffffffe002e66540
      [    3.329547] s8 : ffffffe000626e70 s9 : fffffffffffffff7 s10: ffffffe000626e48
      [    3.337561] s11: 0000000000000000 t3 : ffffffffffffffff t4 : ffffffe000704be8
      [    3.345583] t5 : 0000000000000038 t6 : ffffffe002e664c4
      [    3.351490]sstatus: 0000000200000120 sbadaddr: 0000000000000000 scause: 0000000000000003
      [    3.360611]---[ end trace 67bd5ac7bc0e652e ]---
      [    3.365682]radio-rda5807 3-0011: Failed to register video device (-22)
      [    3.373112]radio-rda5807: probe of 3-0011 failed with error -22
      [    3.379816]sunxi-i2c sunxi-i2c3: probe success
      
      1 条回复 最后回复 回复 引用 分享 0
      • YuzukiTsuru
        柚木 鉉 LV 9 最后由 YuzukiTsuru 编辑

        驱动文件

        #include <asm/byteorder.h>
        #include <linux/bitops.h>
        #include <linux/delay.h>
        #include <linux/module.h>
        #include <linux/i2c.h>
        #include <linux/kernel.h>
        #include <linux/of_platform.h>
        #include <linux/pm.h>
        #include <linux/slab.h>
        #include <linux/types.h>
        #include <linux/videodev2.h>
        #include <media/v4l2-ctrls.h>
        #include <media/v4l2-dev.h>
        #include <media/v4l2-device.h>
        #include <media/v4l2-ioctl.h>
        
        /* The driver uses random access I/O to the registers via I2C address 0x11. */
        #define RDA5807_I2C_ADDR		0x11
        
        /* Working current: 1.8, 2.1, 2.5 or 3.0 mA. */
        #define RDA5807_INPUT_LNA_WC_18		(0 << 0)
        #define RDA5807_INPUT_LNA_WC_21		(1 << 0)
        #define RDA5807_INPUT_LNA_WC_25		(2 << 0)
        #define RDA5807_INPUT_LNA_WC_30		(3 << 0)
        /* Use antenna signal connected to LNAN and/or LNAP pin? */
        #define RDA5807_LNA_PORT_N		(1 << 2)
        #define RDA5807_LNA_PORT_P		(1 << 3)
        
        /* Ouput analog audio on LOUT+ROUT pins */
        #define RDA5807_OUTPUT_AUDIO_ANALOG	(1 << 0)
        /* Output digital audio using I2S on GPIO1-3 pins */
        #define RDA5807_OUTPUT_AUDIO_I2S	(1 << 1)
        /* Output stereo indicator signal on GPIO3 pin */
        #define RDA5807_OUTPUT_STEREO_INDICATOR	(1 << 2)
        
        struct rda5807_platform_data {
        	u8 input_flags;
        	u8 output_flags;
        };
        
        enum rda5807_reg {
        	RDA5807_REG_CHIPID		= 0x00,
        	RDA5807_REG_CTRL		= 0x02,
        	RDA5807_REG_CHAN		= 0x03,
        	RDA5807_REG_IOCFG		= 0x04,
        	RDA5807_REG_INTM_THRESH_VOL	= 0x05,
        	RDA5807_REG_SEEK_RESULT		= 0x0A,
        	RDA5807_REG_SIGNAL		= 0x0B,
        };
        
        #define RDA5807_MASK_CTRL_DHIZ		BIT(15)
        #define RDA5807_MASK_CTRL_DMUTE		BIT(14)
        #define RDA5807_MASK_CTRL_MONO		BIT(13)
        #define RDA5807_MASK_CTRL_BASS		BIT(12)
        #define RDA5807_MASK_CTRL_SEEKUP	BIT(9)
        #define RDA5807_MASK_CTRL_SEEK		BIT(8)
        #define RDA5807_MASK_CTRL_SKMODE	BIT(7)
        #define RDA5807_MASK_CTRL_CLKMODE	(7 << 4)
        #define RDA5807_MASK_CTRL_SOFTRESET	BIT(1)
        #define RDA5807_MASK_CTRL_ENABLE	BIT(0)
        
        #define RDA5807_SHIFT_CHAN_WRCHAN	6
        #define RDA5807_MASK_CHAN_WRCHAN	(0x3FF << RDA5807_SHIFT_CHAN_WRCHAN)
        #define RDA5807_MASK_CHAN_TUNE		BIT(4)
        #define RDA5807_SHIFT_CHAN_BAND		2
        #define RDA5807_MASK_CHAN_BAND		(0x3 << RDA5807_SHIFT_CHAN_BAND)
        #define RDA5807_SHIFT_CHAN_SPACE	0
        #define RDA5807_MASK_CHAN_SPACE		(0x3 << RDA5807_SHIFT_CHAN_SPACE)
        
        #define RDA5807_MASK_SEEKRES_COMPLETE	BIT(14)
        #define RDA5807_MASK_SEEKRES_FAIL	BIT(13)
        #define RDA5807_MASK_SEEKRES_STEREO	BIT(10)
        #define RDA5807_MASK_SEEKRES_READCHAN	0x3FF
        
        #define RDA5807_MASK_DEEMPHASIS		BIT(11)
        
        #define RDA5807_SHIFT_VOLUME_DAC	0
        #define RDA5807_MASK_VOLUME_DAC		(0xF << RDA5807_SHIFT_VOLUME_DAC)
        
        #define RDA5807_SHIFT_RSSI		9
        #define RDA5807_MASK_RSSI		(0x7F << RDA5807_SHIFT_RSSI)
        
        #define RDA5807_FREQ_MIN_KHZ  76000
        #define RDA5807_FREQ_MAX_KHZ 108000
        
        static int rda5807_i2c_read(struct i2c_client *client, enum rda5807_reg reg)
        {
        	__u8  reg_buf = reg;
        	__u16 val_buf;
        	struct i2c_msg msgs[] = {
        		{ /* write register number */
        			.addr = client->addr,
        			.flags = 0,
        			.len = sizeof(reg_buf),
        			.buf = &reg_buf,
        		},
        		{ /* read register contents */
        			.addr = client->addr,
        			.flags = I2C_M_RD,
        			.len = sizeof(val_buf),
        			.buf = (__u8 *)&val_buf,
        		},
        	};
        	int err;
        
        	err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
        	if (err < 0) return err;
        	if (err < ARRAY_SIZE(msgs)) return -EIO;
        
        	//dev_info(&client->dev, "reg[%02X] = %04X\n", reg, be16_to_cpu(val_buf));
        	return be16_to_cpu(val_buf);
        }
        
        static int rda5807_i2c_write(struct i2c_client *client, enum rda5807_reg reg,
        			     u16 val)
        {
        	__u8 buf[] = { reg, val >> 8, val & 0xFF };
        	struct i2c_msg msgs[] = {
        		{ /* write register number and contents */
        			.addr = client->addr,
        			.flags = 0,
        			.len = sizeof(buf),
        			.buf = buf,
        		},
        	};
        	int err;
        
        	err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
        	if (err < 0) return err;
        	if (err < ARRAY_SIZE(msgs)) return -EIO;
        
        	//dev_info(&client->dev, "reg[%02X] := %04X\n", reg, val);
        	return 0;
        }
        
        struct rda5807_driver {
        	struct v4l2_ctrl_handler	ctrl_handler;
        	struct video_device		video_dev;
        	struct v4l2_device		v4l2_dev;
        	struct i2c_client		*i2c_client;
        
        	u8 input_flags;
        	u8 output_flags;
        };
        
        static const struct v4l2_file_operations rda5807_fops = {
        	.owner		= THIS_MODULE,
        	.unlocked_ioctl	= video_ioctl2,
        };
        
        static int rda5807_update_reg(struct rda5807_driver *radio,
        			      enum rda5807_reg reg, u16 mask, u16 val)
        {
        	int err = 0;
        	// TODO: Locking.
        	//       Or do locking in the caller, in case we ever need to update
        	//       two registers in one operation?
        	err = rda5807_i2c_read(radio->i2c_client, reg);
        	if (err >= 0) {
        		val |= ((u16)err & ~mask);
        		err = rda5807_i2c_write(radio->i2c_client, reg, val);
        	}
        	return err;
        }
        
        static int rda5807_set_enable(struct rda5807_driver *radio, int enabled)
        {
        	u16 val = enabled ? RDA5807_MASK_CTRL_ENABLE : 0;
        	int err;
        	//dev_info(&radio->i2c_client->dev, "set enabled to %d\n", enabled);
        	err = rda5807_update_reg(radio, RDA5807_REG_CTRL,
        				 RDA5807_MASK_CTRL_ENABLE, val);
        	if (err < 0)
        		return err;
        	/* Tuning is lost when the chip is disabled, so re-tune when enabled. */
        	if (enabled) {
        		err =  rda5807_update_reg(radio, RDA5807_REG_CHAN,
        					  RDA5807_MASK_CHAN_TUNE,
        					  RDA5807_MASK_CHAN_TUNE);
        
        		/* following the rda5807 programming guide, we
        		 * need to wait for 0.5 seconds before tune */
        		if (!err)
        			msleep(500);
        	}
        
        	return err;
        }
        
        static int rda5807_set_mute(struct rda5807_driver *radio, int muted)
        {
        	u16 val = muted ? 0 : RDA5807_MASK_CTRL_DMUTE /* disable mute */;
        	//dev_info(&radio->i2c_client->dev, "set mute to %d\n", muted);
        	return rda5807_update_reg(radio, RDA5807_REG_CTRL,
        				  RDA5807_MASK_CTRL_DMUTE, val);
        }
        
        static int rda5807_set_volume(struct rda5807_driver *radio, int volume)
        {
        	//dev_info(&radio->i2c_client->dev, "set volume to %d\n", volume);
        	return rda5807_update_reg(radio, RDA5807_REG_INTM_THRESH_VOL,
        				  RDA5807_MASK_VOLUME_DAC,
        				  volume << RDA5807_SHIFT_VOLUME_DAC);
        }
        
        static int rda5807_set_preemphasis(struct rda5807_driver *radio,
        				   enum v4l2_preemphasis preemp)
        {
        	//dev_info(&radio->i2c_client->dev, "set preemphasis to %d\n", preemp);
        	return rda5807_update_reg(radio, RDA5807_REG_IOCFG,
        				  RDA5807_MASK_DEEMPHASIS,
        				  preemp == V4L2_PREEMPHASIS_50_uS
        				          ? RDA5807_MASK_DEEMPHASIS : 0);
        }
        
        static int rda5807_get_frequency(struct rda5807_driver *radio)
        {
        	u32 freq_khz;
        	u16 val;
        	int err;
        
        	err = rda5807_i2c_read(radio->i2c_client, RDA5807_REG_SEEK_RESULT);
        	if (err < 0)
        		return err;
        	val = err;
        
        	freq_khz = 50 * (val & RDA5807_MASK_SEEKRES_READCHAN)
        		 + RDA5807_FREQ_MIN_KHZ;
        	//dev_info(&radio->i2c_client->dev, "get freq of %u kHz\n", freq_khz);
        	return freq_khz;
        }
        
        static int rda5807_set_frequency(struct rda5807_driver *radio, u32 freq_khz)
        {
        	u16 mask = 0;
        	u16 val = 0;
        
        	//dev_info(&radio->i2c_client->dev, "set freq to %u kHz\n", freq_khz);
        
        	if (freq_khz < RDA5807_FREQ_MIN_KHZ)
        		return -ERANGE;
        	if (freq_khz > RDA5807_FREQ_MAX_KHZ)
        		return -ERANGE;
        
        	/* select widest band */
        	mask |= RDA5807_MASK_CHAN_BAND;
        	val  |= 2 << RDA5807_SHIFT_CHAN_BAND;
        	/* select 50 kHz channel spacing */
        	mask |= RDA5807_MASK_CHAN_SPACE;
        	val  |= 2 << RDA5807_SHIFT_CHAN_SPACE;
        	/* select frequency */
        	mask |= RDA5807_MASK_CHAN_WRCHAN;
        	val  |= ((freq_khz - RDA5807_FREQ_MIN_KHZ + 25) / 50)
        			<< RDA5807_SHIFT_CHAN_WRCHAN;
        	/* start tune operation */
        	mask |= RDA5807_MASK_CHAN_TUNE;
        	val  |= RDA5807_MASK_CHAN_TUNE;
        
        	return rda5807_update_reg(radio, RDA5807_REG_CHAN, mask, val);
        }
        
        static int rda5807_seek_frequency(struct rda5807_driver *radio,
        				  int upward, int wrap)
        {
        	u16 mask = 0;
        	u16 val = 0;
        	int ret, count = 0;
        
        	/* TODO: Seek threshold is configurable. How should the driver handle
        	 *       this configuration?
        	 */
        	/* seek up or down? */
        	mask |= RDA5807_MASK_CTRL_SEEKUP;
        	if (upward)
        		val |= RDA5807_MASK_CTRL_SEEKUP;
        	/* wrap around at band limit? */
        	mask |= RDA5807_MASK_CTRL_SKMODE;
        	if (!wrap)
        		val |= RDA5807_MASK_CTRL_SKMODE;
        	/* seek command */
        	mask |= RDA5807_MASK_CTRL_SEEK;
        	val  |= RDA5807_MASK_CTRL_SEEK;
        
        	ret = rda5807_update_reg(radio, RDA5807_REG_CTRL, mask, val);
        	if (ret < 0)
        		return ret;
        
        	while (1) {
        		/*
        		 * The programming guide says we should wait for 35 ms for each
        		 * frequency tested.
        		 */
        		msleep(35);
        
        		ret = rda5807_i2c_read(radio->i2c_client,
        				       RDA5807_REG_SEEK_RESULT);
        		if (ret < 0)
        			return ret;
        
        		/* Seek done? */
        		if (ret & RDA5807_MASK_SEEKRES_COMPLETE)
        			return 0;
        
        		/*
        		 * Channel spacing is 100 kHz.
        		 * TODO: Should we support configurable spacing?
        		 */
        		count++;
        		if (count > (RDA5807_FREQ_MAX_KHZ - RDA5807_FREQ_MIN_KHZ) / 100)
        			return -ETIMEDOUT;
        	}
        }
        
        static inline struct rda5807_driver *ctrl_to_radio(struct v4l2_ctrl *ctrl)
        {
        	return container_of(ctrl->handler, struct rda5807_driver, ctrl_handler);
        }
        
        static int rda5807_s_ctrl(struct v4l2_ctrl *ctrl)
        {
        	struct rda5807_driver *radio = ctrl_to_radio(ctrl);
        
        	switch (ctrl->id) {
        	case V4L2_CID_AUDIO_MUTE: {
        		/* Disable the radio while muted, to save power.
        		 * TODO: We can't seek while the radio is disabled;
        		 *       is that a problem?
        		 */
        		int err1 = rda5807_set_enable(radio, !ctrl->val);
        		int err2 = rda5807_set_mute(radio, ctrl->val);
        		return err1 ? err1 : err2;
        	}
        	case V4L2_CID_AUDIO_VOLUME:
        		return rda5807_set_volume(radio, ctrl->val);
        	case V4L2_CID_TUNE_PREEMPHASIS:
        		return rda5807_set_preemphasis(radio, ctrl->val);
        	default:
        		return -EINVAL;
        	}
        }
        
        static const struct v4l2_ctrl_ops rda5807_ctrl_ops = {
        	.s_ctrl = rda5807_s_ctrl,
        };
        
        static int rda5807_vidioc_querycap(struct file *file, void *fh,
        				   struct v4l2_capability *cap)
        {
        	*cap = (struct v4l2_capability) {
        		.driver		= "rda5807",
        		.card		= "RDA5807 FM receiver",
        		.bus_info	= "I2C",
        		.device_caps	= V4L2_CAP_RADIO | V4L2_CAP_TUNER
        					| V4L2_CAP_HW_FREQ_SEEK,
        	};
        	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
        
        	return 0;
        }
        
        static int rda5807_vidioc_g_audio(struct file *file, void *fh,
        				  struct v4l2_audio *a)
        {
        	if (a->index != 0)
        		return -EINVAL;
        
        	*a = (struct v4l2_audio) {
        		.name = "Radio",
        		.capability = V4L2_AUDCAP_STEREO,
        		.mode = 0,
        	};
        
        	return 0;
        }
        
        static int rda5807_vidioc_g_tuner(struct file *file, void *fh,
        				  struct v4l2_tuner *a)
        {
        	struct rda5807_driver *radio = video_drvdata(file);
        	int err;
        	u16 seekres, signal;
        	__u32 rxsubchans;
        
        	if (a->index != 0)
        		return -EINVAL;
        
        	err = rda5807_i2c_read(radio->i2c_client, RDA5807_REG_SEEK_RESULT);
        	if (err < 0)
        		return err;
        	seekres = (u16)err;
        	if ((seekres & (RDA5807_MASK_SEEKRES_COMPLETE
        						| RDA5807_MASK_SEEKRES_FAIL))
        				== RDA5807_MASK_SEEKRES_COMPLETE)
        		/* mono/stereo known */
        		rxsubchans = seekres & RDA5807_MASK_SEEKRES_STEREO
        				? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
        	else
        		/* mono/stereo unknown */
        		rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
        
        	err = rda5807_i2c_read(radio->i2c_client, RDA5807_REG_SIGNAL);
        	if (err < 0)
        		return err;
        	signal = ((u16)err & RDA5807_MASK_RSSI) >> RDA5807_SHIFT_RSSI;
        
        	*a = (struct v4l2_tuner) {
        		.name = "FM",
        		.type = V4L2_TUNER_RADIO,
        		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO,
        		/* unit is 1/16 kHz */
        		.rangelow   = RDA5807_FREQ_MIN_KHZ * 16,
        		.rangehigh  = RDA5807_FREQ_MAX_KHZ * 16,
        		.rxsubchans = rxsubchans,
        		/* TODO: Implement forced mono (RDA5807_MASK_CTRL_MONO). */
        		.audmode = V4L2_TUNER_MODE_STEREO,
        		.signal = signal << (16 - 7),
        		.afc = 0, /* automatic frequency control */
        	};
        
        	return 0;
        }
        
        static int rda5807_vidioc_g_frequency(struct file *file, void *fh,
        				      struct v4l2_frequency *a)
        {
        	struct rda5807_driver *radio = video_drvdata(file);
        	int freq_khz;
        
        	if (a->tuner != 0)
        		return -EINVAL;
        	/* This ioctl ignores the type field. */
        
        	freq_khz = rda5807_get_frequency(radio);
        	if (freq_khz < 0)
        		return freq_khz;
        
        	a->frequency = (__u32)freq_khz * 16;
        	return 0;
        }
        
        static int rda5807_vidioc_s_frequency(struct file *file, void *fh,
        				      const struct v4l2_frequency *a)
        {
        	struct rda5807_driver *radio = video_drvdata(file);
        
        	if (a->tuner != 0)
        		return -EINVAL;
        	if (a->type != V4L2_TUNER_RADIO)
        		return -EINVAL;
        
        	return rda5807_set_frequency(radio, (a->frequency * 625) / 10000);
        }
        
        static int rda5807_vidioc_s_hw_freq_seek(struct file *file, void *fh,
        					 const struct v4l2_hw_freq_seek *a)
        {
        	struct rda5807_driver *radio = video_drvdata(file);
        
        	if (a->tuner != 0)
        		return -EINVAL;
        	if (a->type != V4L2_TUNER_RADIO)
        		return -EINVAL;
        
        	return rda5807_seek_frequency(radio, a->seek_upward, a->wrap_around);
        }
        
        static const struct v4l2_ioctl_ops rda5807_ioctl_ops = {
        	.vidioc_querycap	= rda5807_vidioc_querycap,
        	.vidioc_g_audio		= rda5807_vidioc_g_audio,
        	.vidioc_g_tuner		= rda5807_vidioc_g_tuner,
        	.vidioc_g_frequency	= rda5807_vidioc_g_frequency,
        	.vidioc_s_frequency	= rda5807_vidioc_s_frequency,
        	.vidioc_s_hw_freq_seek  = rda5807_vidioc_s_hw_freq_seek,
        };
        
        static const char *rda5807_name = "RDA5807 FM receiver";
        
        static const u16 rda5807_lna_current[] = { 1800, 2100, 2500, 3000 };
        
        static int rda5807_i2c_probe(struct i2c_client *client,
        			     const struct i2c_device_id *id)
        {
        	struct device_node *np = client->dev.of_node;
        	struct rda5807_platform_data *pdata = client->dev.platform_data;
        	struct rda5807_driver *radio;
        	int err;
        	u16 val;
        
        	radio = devm_kzalloc(&client->dev, sizeof(*radio), GFP_KERNEL);
        	if (!radio) {
        		dev_err(&client->dev, "Failed to allocate driver data\n");
        		return -ENOMEM;
        	}
        
        	radio->i2c_client = client;
        
        	/* Configuration. */
        	if (IS_ENABLED(CONFIG_OF) && np) {
        		u16 lna_current = 2500;
        		size_t i;
        
        		radio->input_flags = 0;
        		of_property_read_u16(np, "lna-current", &lna_current);
        		for (i = 0; i < ARRAY_SIZE(rda5807_lna_current); i++)
        			if (rda5807_lna_current[i] == lna_current)
        				radio->input_flags = i;
        
        		if (of_property_read_bool(np, "lnan"))
        			radio->input_flags |= RDA5807_LNA_PORT_N;
        		if (of_property_read_bool(np, "lnap"))
        			radio->input_flags |= RDA5807_LNA_PORT_P;
        
        		if (of_property_read_bool(np, "i2s-out"))
        			radio->output_flags |= RDA5807_OUTPUT_AUDIO_I2S;
        		if (of_property_read_bool(np, "analog-out"))
        			radio->output_flags |= RDA5807_OUTPUT_AUDIO_ANALOG;
        
        	} else if (pdata) {
        		radio->input_flags = pdata->input_flags;
        		radio->output_flags = pdata->output_flags;
        	} else {
        		radio->input_flags = RDA5807_INPUT_LNA_WC_25;
        		radio->output_flags = 0;
        	}
        	if (!(radio->input_flags & (RDA5807_LNA_PORT_N | RDA5807_LNA_PORT_P)))
        		dev_warn(&client->dev, "Both LNA inputs disabled\n");
        
        	err = rda5807_i2c_read(client, RDA5807_REG_CHIPID);
        	if (err < 0) {
        		dev_err(&client->dev, "Failed to read chip ID (%d)\n", err);
        		return err;
        	}
        	val = err;
        	if ((val & 0xFF00) != 0x5800) {
        		dev_err(&client->dev, "Chip ID mismatch: "
        				      "expected 58xx, got %04X\n", val);
        		return -ENODEV;
        	}
        	dev_info(&client->dev, "Found FM radio receiver\n");
        
        	// TODO: Resetting the chip would be good.
        
        	/* Initialize controls. */
        	v4l2_ctrl_handler_init(&radio->ctrl_handler, 3);
        	v4l2_ctrl_new_std(&radio->ctrl_handler, &rda5807_ctrl_ops,
        			  V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
        	v4l2_ctrl_new_std(&radio->ctrl_handler, &rda5807_ctrl_ops,
        			  V4L2_CID_AUDIO_VOLUME, 0, 15, 1, 8);
        	/* TODO: V4L2_CID_TUNE_PREEMPHASIS is based on V4L2_CID_FM_TX_CLASS_BASE
        	 *       which suggests it is a transmit control rather than a receive
        	 *       control. The register bit we change is called "de-emphasis",
        	 *       but there is no de-emphasis control in V4L2.
        	 */
        	v4l2_ctrl_new_std_menu(&radio->ctrl_handler, &rda5807_ctrl_ops,
        			       V4L2_CID_TUNE_PREEMPHASIS,
        			       V4L2_PREEMPHASIS_75_uS,
        			       BIT(V4L2_PREEMPHASIS_DISABLED),
        			       V4L2_PREEMPHASIS_50_uS);
        	err = radio->ctrl_handler.error;
        	if (err) {
        		dev_err(&client->dev, "Failed to init controls handler (%d)\n",
        			err);
        		goto err_ctrl_free;
        	}
        
        	strlcpy(radio->v4l2_dev.name, rda5807_name,
        		sizeof(radio->v4l2_dev.name));
        	err = v4l2_device_register(NULL, &radio->v4l2_dev);
        	if (err < 0) {
        		dev_err(&client->dev, "Failed to register v4l2 device (%d)\n",
        			err);
        		goto err_ctrl_free;
        	}
        
        	radio->video_dev = (struct video_device) {
        		.name = "RDA5807 FM receiver",
        		.v4l2_dev = &radio->v4l2_dev,
        		.ctrl_handler = &radio->ctrl_handler,
        		.fops = &rda5807_fops,
        		.ioctl_ops = &rda5807_ioctl_ops,
        		.release = video_device_release_empty,
        		//.lock = &radio->lock,
        	};
        	i2c_set_clientdata(client, radio);
        	video_set_drvdata(&radio->video_dev, radio);
        
        	err = video_register_device(&radio->video_dev, VFL_TYPE_RADIO, -1);
        	if (err < 0) {
        		dev_err(&client->dev, "Failed to register video device (%d)\n",
        				      err);
        		goto err_ctrl_free;
        	}
        
        	/* Configure chip inputs. */
        	err = rda5807_update_reg(radio, RDA5807_REG_INTM_THRESH_VOL,
        				 0xF << 4, (radio->input_flags & 0xF) << 4);
        	if (err < 0) {
        		dev_warn(&client->dev, "Failed to configure inputs (%d)\n",
        				       err);
        	}
        	/* Configure chip outputs. */
        	val = 0;
        	if (radio->output_flags & RDA5807_OUTPUT_AUDIO_I2S) {
        		val |= BIT(6);
        	}
        	err = rda5807_update_reg(radio, RDA5807_REG_IOCFG, 0x003F, val);
        	if (err < 0) {
        		dev_warn(&client->dev, "Failed to configure outputs (%d)\n",
        				       err);
        	}
        	val = 0;
        	if (radio->output_flags & RDA5807_OUTPUT_AUDIO_ANALOG) {
        		val |= BIT(15);
        	}
        	err = rda5807_update_reg(radio, RDA5807_REG_CTRL, BIT(15), val);
        	if (err < 0) {
        		dev_warn(&client->dev, "Failed to configure outputs (%d)\n",
        				       err);
        	}
        
        	err = v4l2_ctrl_handler_setup(&radio->ctrl_handler);
        	if (err < 0) {
        		dev_err(&client->dev, "Failed to set default control values"
        				      " (%d)\n", err);
        		goto err_video_unreg;
        	}
        
        	return 0;
        
        err_video_unreg:
        	video_unregister_device(&radio->video_dev);
        
        err_ctrl_free:
        	v4l2_ctrl_handler_free(&radio->ctrl_handler);
        
        /*err_radio_rel:*/
        	video_device_release_empty(&radio->video_dev);
        
        	return err;
        }
        
        static int rda5807_i2c_remove(struct i2c_client *client)
        {
        	struct rda5807_driver *radio = i2c_get_clientdata(client);
        
        	video_unregister_device(&radio->video_dev);
        	v4l2_ctrl_handler_free(&radio->ctrl_handler);
        	video_device_release_empty(&radio->video_dev);
        
        	return 0;
        }
        
        #ifdef CONFIG_PM_SLEEP
        
        static int rda5807_suspend(struct device *dev)
        {
        	struct i2c_client *client = to_i2c_client(dev);
        	struct rda5807_driver *radio = i2c_get_clientdata(client);
        
        	return rda5807_set_enable(radio, 0);
        }
        
        static int rda5807_resume(struct device *dev)
        {
        	struct i2c_client *client = to_i2c_client(dev);
        	struct rda5807_driver *radio = i2c_get_clientdata(client);
        	struct v4l2_ctrl *mute_ctrl = v4l2_ctrl_find(&radio->ctrl_handler,
        						     V4L2_CID_AUDIO_MUTE);
        	s32 mute_val = v4l2_ctrl_g_ctrl(mute_ctrl);
        	int enabled = !mute_val;
        
        	if (enabled)
        		return rda5807_set_enable(radio, enabled);
        	else
        		return 0;
        }
        
        static SIMPLE_DEV_PM_OPS(rda5807_pm_ops, rda5807_suspend, rda5807_resume);
        #define RDA5807_PM_OPS (&rda5807_pm_ops)
        
        #else
        
        #define RDA5807_PM_OPS NULL
        
        #endif
        
        static const struct i2c_device_id rda5807_id[] = {
        	{ "radio-rda5807", 0 },
        	{ }
        };
        MODULE_DEVICE_TABLE(i2c, rda5807_id);
        
        #ifdef CONFIG_OF
        static const struct of_device_id rda5807_dt_ids[] = {
        	{ .compatible = "rdamicro,rda5807" },
        	{ }
        };
        MODULE_DEVICE_TABLE(of, rda5807_dt_ids);
        #endif
        
        static struct i2c_driver rda5807_i2c_driver = {
        	.probe = rda5807_i2c_probe,
        	.remove = rda5807_i2c_remove,
        	.id_table = rda5807_id,
        	.driver = {
        		.name	= "radio-rda5807",
        		.owner	= THIS_MODULE,
        		.pm	= RDA5807_PM_OPS,
        		.of_match_table = of_match_ptr(rda5807_dt_ids),
        	},
        };
        
        module_i2c_driver(rda5807_i2c_driver);
        
        MODULE_AUTHOR("Maarten ter Huurne <maarten@treewalker.org>");
        MODULE_DESCRIPTION("RDA5807 FM tuner driver");
        MODULE_LICENSE("GPL");
        
        1 条回复 最后回复 回复 引用 分享 0
        • YuzukiTsuru
          柚木 鉉 LV 9 最后由 编辑

          应该是V4L2接口有变化,这个驱动是给linux 3.4用的(好像是

          还要看看哪里出的问题

          1 条回复 最后回复 回复 引用 分享 0
          • 1 / 1
          • First post
            Last post

          Copyright © 2024 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号

          行为准则 | 用户协议 | 隐私权政策