18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * saa7706.c Philips SAA7706H Car Radio DSP driver
48c2ecf20Sopenharmony_ci * Copyright (c) 2009 Intel Corporation
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci#include <linux/init.h>
98c2ecf20Sopenharmony_ci#include <linux/delay.h>
108c2ecf20Sopenharmony_ci#include <linux/errno.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/i2c.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
168c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define DRIVER_NAME "saa7706h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* the I2C memory map looks like this
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	$1C00 - $FFFF Not Used
238c2ecf20Sopenharmony_ci	$2200 - $3FFF Reserved YRAM (DSP2) space
248c2ecf20Sopenharmony_ci	$2000 - $21FF YRAM (DSP2)
258c2ecf20Sopenharmony_ci	$1FF0 - $1FFF Hardware Registers
268c2ecf20Sopenharmony_ci	$1280 - $1FEF Reserved XRAM (DSP2) space
278c2ecf20Sopenharmony_ci	$1000 - $127F XRAM (DSP2)
288c2ecf20Sopenharmony_ci	$0FFF        DSP CONTROL
298c2ecf20Sopenharmony_ci	$0A00 - $0FFE Reserved
308c2ecf20Sopenharmony_ci	$0980 - $09FF Reserved YRAM (DSP1) space
318c2ecf20Sopenharmony_ci	$0800 - $097F YRAM (DSP1)
328c2ecf20Sopenharmony_ci	$0200 - $07FF Not Used
338c2ecf20Sopenharmony_ci	$0180 - $01FF Reserved XRAM (DSP1) space
348c2ecf20Sopenharmony_ci	$0000 - $017F XRAM (DSP1)
358c2ecf20Sopenharmony_ci*/
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define SAA7706H_REG_CTRL		0x0fff
388c2ecf20Sopenharmony_ci#define SAA7706H_CTRL_BYP_PLL		0x0001
398c2ecf20Sopenharmony_ci#define SAA7706H_CTRL_PLL_DIV_MASK	0x003e
408c2ecf20Sopenharmony_ci#define SAA7706H_CTRL_PLL3_62975MHZ	0x003e
418c2ecf20Sopenharmony_ci#define SAA7706H_CTRL_DSP_TURBO		0x0040
428c2ecf20Sopenharmony_ci#define SAA7706H_CTRL_PC_RESET_DSP1	0x0080
438c2ecf20Sopenharmony_ci#define SAA7706H_CTRL_PC_RESET_DSP2	0x0100
448c2ecf20Sopenharmony_ci#define SAA7706H_CTRL_DSP1_ROM_EN_MASK	0x0600
458c2ecf20Sopenharmony_ci#define SAA7706H_CTRL_DSP1_FUNC_PROM	0x0000
468c2ecf20Sopenharmony_ci#define SAA7706H_CTRL_DSP2_ROM_EN_MASK	0x1800
478c2ecf20Sopenharmony_ci#define SAA7706H_CTRL_DSP2_FUNC_PROM	0x0000
488c2ecf20Sopenharmony_ci#define SAA7706H_CTRL_DIG_SIL_INTERPOL	0x8000
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#define SAA7706H_REG_EVALUATION			0x1ff0
518c2ecf20Sopenharmony_ci#define SAA7706H_EVAL_DISABLE_CHARGE_PUMP	0x000001
528c2ecf20Sopenharmony_ci#define SAA7706H_EVAL_DCS_CLOCK			0x000002
538c2ecf20Sopenharmony_ci#define SAA7706H_EVAL_GNDRC1_ENABLE		0x000004
548c2ecf20Sopenharmony_ci#define SAA7706H_EVAL_GNDRC2_ENABLE		0x000008
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#define SAA7706H_REG_CL_GEN1			0x1ff3
578c2ecf20Sopenharmony_ci#define SAA7706H_CL_GEN1_MIN_LOOPGAIN_MASK	0x00000f
588c2ecf20Sopenharmony_ci#define SAA7706H_CL_GEN1_LOOPGAIN_MASK		0x0000f0
598c2ecf20Sopenharmony_ci#define SAA7706H_CL_GEN1_COARSE_RATION		0xffff00
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#define SAA7706H_REG_CL_GEN2			0x1ff4
628c2ecf20Sopenharmony_ci#define SAA7706H_CL_GEN2_WSEDGE_FALLING		0x000001
638c2ecf20Sopenharmony_ci#define SAA7706H_CL_GEN2_STOP_VCO		0x000002
648c2ecf20Sopenharmony_ci#define SAA7706H_CL_GEN2_FRERUN			0x000004
658c2ecf20Sopenharmony_ci#define SAA7706H_CL_GEN2_ADAPTIVE		0x000008
668c2ecf20Sopenharmony_ci#define SAA7706H_CL_GEN2_FINE_RATIO_MASK	0x0ffff0
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci#define SAA7706H_REG_CL_GEN4		0x1ff6
698c2ecf20Sopenharmony_ci#define SAA7706H_CL_GEN4_BYPASS_PLL1	0x001000
708c2ecf20Sopenharmony_ci#define SAA7706H_CL_GEN4_PLL1_DIV_MASK	0x03e000
718c2ecf20Sopenharmony_ci#define SAA7706H_CL_GEN4_DSP1_TURBO	0x040000
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#define SAA7706H_REG_SEL	0x1ff7
748c2ecf20Sopenharmony_ci#define SAA7706H_SEL_DSP2_SRCA_MASK	0x000007
758c2ecf20Sopenharmony_ci#define SAA7706H_SEL_DSP2_FMTA_MASK	0x000031
768c2ecf20Sopenharmony_ci#define SAA7706H_SEL_DSP2_SRCB_MASK	0x0001c0
778c2ecf20Sopenharmony_ci#define SAA7706H_SEL_DSP2_FMTB_MASK	0x000e00
788c2ecf20Sopenharmony_ci#define SAA7706H_SEL_DSP1_SRC_MASK	0x003000
798c2ecf20Sopenharmony_ci#define SAA7706H_SEL_DSP1_FMT_MASK	0x01c003
808c2ecf20Sopenharmony_ci#define SAA7706H_SEL_SPDIF2		0x020000
818c2ecf20Sopenharmony_ci#define SAA7706H_SEL_HOST_IO_FMT_MASK	0x1c0000
828c2ecf20Sopenharmony_ci#define SAA7706H_SEL_EN_HOST_IO		0x200000
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci#define SAA7706H_REG_IAC		0x1ff8
858c2ecf20Sopenharmony_ci#define SAA7706H_REG_CLK_SET		0x1ff9
868c2ecf20Sopenharmony_ci#define SAA7706H_REG_CLK_COEFF		0x1ffa
878c2ecf20Sopenharmony_ci#define SAA7706H_REG_INPUT_SENS		0x1ffb
888c2ecf20Sopenharmony_ci#define SAA7706H_INPUT_SENS_RDS_VOL_MASK	0x0003f
898c2ecf20Sopenharmony_ci#define SAA7706H_INPUT_SENS_FM_VOL_MASK		0x00fc0
908c2ecf20Sopenharmony_ci#define SAA7706H_INPUT_SENS_FM_MPX		0x01000
918c2ecf20Sopenharmony_ci#define SAA7706H_INPUT_SENS_OFF_FILTER_A_EN	0x02000
928c2ecf20Sopenharmony_ci#define SAA7706H_INPUT_SENS_OFF_FILTER_B_EN	0x04000
938c2ecf20Sopenharmony_ci#define SAA7706H_REG_PHONE_NAV_AUDIO	0x1ffc
948c2ecf20Sopenharmony_ci#define SAA7706H_REG_IO_CONF_DSP2	0x1ffd
958c2ecf20Sopenharmony_ci#define SAA7706H_REG_STATUS_DSP2	0x1ffe
968c2ecf20Sopenharmony_ci#define SAA7706H_REG_PC_DSP2		0x1fff
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci#define SAA7706H_DSP1_MOD0	0x0800
998c2ecf20Sopenharmony_ci#define SAA7706H_DSP1_ROM_VER	0x097f
1008c2ecf20Sopenharmony_ci#define SAA7706H_DSP2_MPTR0	0x1000
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci#define SAA7706H_DSP1_MODPNTR	0x0000
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci#define SAA7706H_DSP2_XMEM_CONTLLCW	0x113e
1058c2ecf20Sopenharmony_ci#define SAA7706H_DSP2_XMEM_BUSAMP	0x114a
1068c2ecf20Sopenharmony_ci#define SAA7706H_DSP2_XMEM_FDACPNTR	0x11f9
1078c2ecf20Sopenharmony_ci#define SAA7706H_DSP2_XMEM_IIS1PNTR	0x11fb
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci#define SAA7706H_DSP2_YMEM_PVGA		0x212a
1108c2ecf20Sopenharmony_ci#define SAA7706H_DSP2_YMEM_PVAT1	0x212b
1118c2ecf20Sopenharmony_ci#define SAA7706H_DSP2_YMEM_PVAT		0x212c
1128c2ecf20Sopenharmony_ci#define SAA7706H_DSP2_YMEM_ROM_VER	0x21ff
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci#define SUPPORTED_DSP1_ROM_VER		0x667
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistruct saa7706h_state {
1178c2ecf20Sopenharmony_ci	struct v4l2_subdev sd;
1188c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler hdl;
1198c2ecf20Sopenharmony_ci	unsigned muted;
1208c2ecf20Sopenharmony_ci};
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic inline struct saa7706h_state *to_state(struct v4l2_subdev *sd)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	return container_of(sd, struct saa7706h_state, sd);
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic int saa7706h_i2c_send(struct i2c_client *client, const u8 *data, int len)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	int err = i2c_master_send(client, data, len);
1308c2ecf20Sopenharmony_ci	if (err == len)
1318c2ecf20Sopenharmony_ci		return 0;
1328c2ecf20Sopenharmony_ci	return err > 0 ? -EIO : err;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic int saa7706h_i2c_transfer(struct i2c_client *client,
1368c2ecf20Sopenharmony_ci	struct i2c_msg *msgs, int num)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	int err = i2c_transfer(client->adapter, msgs, num);
1398c2ecf20Sopenharmony_ci	if (err == num)
1408c2ecf20Sopenharmony_ci		return 0;
1418c2ecf20Sopenharmony_ci	return err > 0 ? -EIO : err;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic int saa7706h_set_reg24(struct v4l2_subdev *sd, u16 reg, u32 val)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
1478c2ecf20Sopenharmony_ci	u8 buf[5];
1488c2ecf20Sopenharmony_ci	int pos = 0;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	buf[pos++] = reg >> 8;
1518c2ecf20Sopenharmony_ci	buf[pos++] = reg;
1528c2ecf20Sopenharmony_ci	buf[pos++] = val >> 16;
1538c2ecf20Sopenharmony_ci	buf[pos++] = val >> 8;
1548c2ecf20Sopenharmony_ci	buf[pos++] = val;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return saa7706h_i2c_send(client, buf, pos);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic int saa7706h_set_reg24_err(struct v4l2_subdev *sd, u16 reg, u32 val,
1608c2ecf20Sopenharmony_ci	int *err)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	return *err ? *err : saa7706h_set_reg24(sd, reg, val);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic int saa7706h_set_reg16(struct v4l2_subdev *sd, u16 reg, u16 val)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
1688c2ecf20Sopenharmony_ci	u8 buf[4];
1698c2ecf20Sopenharmony_ci	int pos = 0;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	buf[pos++] = reg >> 8;
1728c2ecf20Sopenharmony_ci	buf[pos++] = reg;
1738c2ecf20Sopenharmony_ci	buf[pos++] = val >> 8;
1748c2ecf20Sopenharmony_ci	buf[pos++] = val;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	return saa7706h_i2c_send(client, buf, pos);
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic int saa7706h_set_reg16_err(struct v4l2_subdev *sd, u16 reg, u16 val,
1808c2ecf20Sopenharmony_ci	int *err)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	return *err ? *err : saa7706h_set_reg16(sd, reg, val);
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int saa7706h_get_reg16(struct v4l2_subdev *sd, u16 reg)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
1888c2ecf20Sopenharmony_ci	u8 buf[2];
1898c2ecf20Sopenharmony_ci	int err;
1908c2ecf20Sopenharmony_ci	u8 regaddr[] = {reg >> 8, reg};
1918c2ecf20Sopenharmony_ci	struct i2c_msg msg[] = {
1928c2ecf20Sopenharmony_ci					{
1938c2ecf20Sopenharmony_ci						.addr = client->addr,
1948c2ecf20Sopenharmony_ci						.len = sizeof(regaddr),
1958c2ecf20Sopenharmony_ci						.buf = regaddr
1968c2ecf20Sopenharmony_ci					},
1978c2ecf20Sopenharmony_ci					{
1988c2ecf20Sopenharmony_ci						.addr = client->addr,
1998c2ecf20Sopenharmony_ci						.flags = I2C_M_RD,
2008c2ecf20Sopenharmony_ci						.len = sizeof(buf),
2018c2ecf20Sopenharmony_ci						.buf = buf
2028c2ecf20Sopenharmony_ci					}
2038c2ecf20Sopenharmony_ci				};
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	err = saa7706h_i2c_transfer(client, msg, ARRAY_SIZE(msg));
2068c2ecf20Sopenharmony_ci	if (err)
2078c2ecf20Sopenharmony_ci		return err;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	return buf[0] << 8 | buf[1];
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic int saa7706h_unmute(struct v4l2_subdev *sd)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	struct saa7706h_state *state = to_state(sd);
2158c2ecf20Sopenharmony_ci	int err = 0;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	err = saa7706h_set_reg16_err(sd, SAA7706H_REG_CTRL,
2188c2ecf20Sopenharmony_ci		SAA7706H_CTRL_PLL3_62975MHZ | SAA7706H_CTRL_PC_RESET_DSP1 |
2198c2ecf20Sopenharmony_ci		SAA7706H_CTRL_PC_RESET_DSP2, &err);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/* newer versions of the chip requires a small sleep after reset */
2228c2ecf20Sopenharmony_ci	msleep(1);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	err = saa7706h_set_reg16_err(sd, SAA7706H_REG_CTRL,
2258c2ecf20Sopenharmony_ci		SAA7706H_CTRL_PLL3_62975MHZ, &err);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_REG_EVALUATION, 0, &err);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_REG_CL_GEN1, 0x040022, &err);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_REG_CL_GEN2,
2328c2ecf20Sopenharmony_ci		SAA7706H_CL_GEN2_WSEDGE_FALLING, &err);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_REG_CL_GEN4, 0x024080, &err);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_REG_SEL, 0x200080, &err);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_REG_IAC, 0xf4caed, &err);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_REG_CLK_SET, 0x124334, &err);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_REG_CLK_COEFF, 0x004a1a,
2438c2ecf20Sopenharmony_ci		&err);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_REG_INPUT_SENS, 0x0071c7,
2468c2ecf20Sopenharmony_ci		&err);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_REG_PHONE_NAV_AUDIO,
2498c2ecf20Sopenharmony_ci		0x0e22ff, &err);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_REG_IO_CONF_DSP2, 0x001ff8,
2528c2ecf20Sopenharmony_ci		&err);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_REG_STATUS_DSP2, 0x080003,
2558c2ecf20Sopenharmony_ci		&err);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_REG_PC_DSP2, 0x000004, &err);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	err = saa7706h_set_reg16_err(sd, SAA7706H_DSP1_MOD0, 0x0c6c, &err);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_MPTR0, 0x000b4b, &err);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_DSP1_MODPNTR, 0x000600, &err);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_DSP1_MODPNTR, 0x0000c0, &err);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_CONTLLCW, 0x000819,
2688c2ecf20Sopenharmony_ci		&err);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_CONTLLCW, 0x00085a,
2718c2ecf20Sopenharmony_ci		&err);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_BUSAMP, 0x7fffff,
2748c2ecf20Sopenharmony_ci		&err);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_FDACPNTR, 0x2000cb,
2778c2ecf20Sopenharmony_ci		&err);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_IIS1PNTR, 0x2000cb,
2808c2ecf20Sopenharmony_ci		&err);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	err = saa7706h_set_reg16_err(sd, SAA7706H_DSP2_YMEM_PVGA, 0x0f80, &err);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	err = saa7706h_set_reg16_err(sd, SAA7706H_DSP2_YMEM_PVAT1, 0x0800,
2858c2ecf20Sopenharmony_ci		&err);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	err = saa7706h_set_reg16_err(sd, SAA7706H_DSP2_YMEM_PVAT, 0x0800, &err);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_CONTLLCW, 0x000905,
2908c2ecf20Sopenharmony_ci		&err);
2918c2ecf20Sopenharmony_ci	if (!err)
2928c2ecf20Sopenharmony_ci		state->muted = 0;
2938c2ecf20Sopenharmony_ci	return err;
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic int saa7706h_mute(struct v4l2_subdev *sd)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct saa7706h_state *state = to_state(sd);
2998c2ecf20Sopenharmony_ci	int err;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	err = saa7706h_set_reg16(sd, SAA7706H_REG_CTRL,
3028c2ecf20Sopenharmony_ci		SAA7706H_CTRL_PLL3_62975MHZ | SAA7706H_CTRL_PC_RESET_DSP1 |
3038c2ecf20Sopenharmony_ci		SAA7706H_CTRL_PC_RESET_DSP2);
3048c2ecf20Sopenharmony_ci	if (!err)
3058c2ecf20Sopenharmony_ci		state->muted = 1;
3068c2ecf20Sopenharmony_ci	return err;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic int saa7706h_s_ctrl(struct v4l2_ctrl *ctrl)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	struct saa7706h_state *state =
3128c2ecf20Sopenharmony_ci		container_of(ctrl->handler, struct saa7706h_state, hdl);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	switch (ctrl->id) {
3158c2ecf20Sopenharmony_ci	case V4L2_CID_AUDIO_MUTE:
3168c2ecf20Sopenharmony_ci		if (ctrl->val)
3178c2ecf20Sopenharmony_ci			return saa7706h_mute(&state->sd);
3188c2ecf20Sopenharmony_ci		return saa7706h_unmute(&state->sd);
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci	return -EINVAL;
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops saa7706h_ctrl_ops = {
3248c2ecf20Sopenharmony_ci	.s_ctrl = saa7706h_s_ctrl,
3258c2ecf20Sopenharmony_ci};
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops empty_ops = {};
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci/*
3308c2ecf20Sopenharmony_ci * Generic i2c probe
3318c2ecf20Sopenharmony_ci * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
3328c2ecf20Sopenharmony_ci */
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistatic int saa7706h_probe(struct i2c_client *client,
3358c2ecf20Sopenharmony_ci			  const struct i2c_device_id *id)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	struct saa7706h_state *state;
3388c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd;
3398c2ecf20Sopenharmony_ci	int err;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	/* Check if the adapter supports the needed features */
3428c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
3438c2ecf20Sopenharmony_ci		return -EIO;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	v4l_info(client, "chip found @ 0x%02x (%s)\n",
3468c2ecf20Sopenharmony_ci			client->addr << 1, client->adapter->name);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	state = kzalloc(sizeof(struct saa7706h_state), GFP_KERNEL);
3498c2ecf20Sopenharmony_ci	if (state == NULL)
3508c2ecf20Sopenharmony_ci		return -ENOMEM;
3518c2ecf20Sopenharmony_ci	sd = &state->sd;
3528c2ecf20Sopenharmony_ci	v4l2_i2c_subdev_init(sd, client, &empty_ops);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(&state->hdl, 4);
3558c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&state->hdl, &saa7706h_ctrl_ops,
3568c2ecf20Sopenharmony_ci			V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
3578c2ecf20Sopenharmony_ci	sd->ctrl_handler = &state->hdl;
3588c2ecf20Sopenharmony_ci	err = state->hdl.error;
3598c2ecf20Sopenharmony_ci	if (err)
3608c2ecf20Sopenharmony_ci		goto err;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	/* check the rom versions */
3638c2ecf20Sopenharmony_ci	err = saa7706h_get_reg16(sd, SAA7706H_DSP1_ROM_VER);
3648c2ecf20Sopenharmony_ci	if (err < 0)
3658c2ecf20Sopenharmony_ci		goto err;
3668c2ecf20Sopenharmony_ci	if (err != SUPPORTED_DSP1_ROM_VER)
3678c2ecf20Sopenharmony_ci		v4l2_warn(sd, "Unknown DSP1 ROM code version: 0x%x\n", err);
3688c2ecf20Sopenharmony_ci	state->muted = 1;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	/* startup in a muted state */
3718c2ecf20Sopenharmony_ci	err = saa7706h_mute(sd);
3728c2ecf20Sopenharmony_ci	if (err)
3738c2ecf20Sopenharmony_ci		goto err;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	return 0;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cierr:
3788c2ecf20Sopenharmony_ci	v4l2_device_unregister_subdev(sd);
3798c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&state->hdl);
3808c2ecf20Sopenharmony_ci	kfree(to_state(sd));
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	printk(KERN_ERR DRIVER_NAME ": Failed to probe: %d\n", err);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	return err;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic int saa7706h_remove(struct i2c_client *client)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = i2c_get_clientdata(client);
3908c2ecf20Sopenharmony_ci	struct saa7706h_state *state = to_state(sd);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	saa7706h_mute(sd);
3938c2ecf20Sopenharmony_ci	v4l2_device_unregister_subdev(sd);
3948c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&state->hdl);
3958c2ecf20Sopenharmony_ci	kfree(to_state(sd));
3968c2ecf20Sopenharmony_ci	return 0;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic const struct i2c_device_id saa7706h_id[] = {
4008c2ecf20Sopenharmony_ci	{DRIVER_NAME, 0},
4018c2ecf20Sopenharmony_ci	{},
4028c2ecf20Sopenharmony_ci};
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, saa7706h_id);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic struct i2c_driver saa7706h_driver = {
4078c2ecf20Sopenharmony_ci	.driver = {
4088c2ecf20Sopenharmony_ci		.name	= DRIVER_NAME,
4098c2ecf20Sopenharmony_ci	},
4108c2ecf20Sopenharmony_ci	.probe		= saa7706h_probe,
4118c2ecf20Sopenharmony_ci	.remove		= saa7706h_remove,
4128c2ecf20Sopenharmony_ci	.id_table	= saa7706h_id,
4138c2ecf20Sopenharmony_ci};
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cimodule_i2c_driver(saa7706h_driver);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SAA7706H Car Radio DSP driver");
4188c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mocean Laboratories");
4198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
420