18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * For the STS-Thompson TDA7432 audio processor chip
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Handles audio functions: volume, balance, tone, loudness
68c2ecf20Sopenharmony_ci * This driver will not complain if used with any
78c2ecf20Sopenharmony_ci * other i2c device with the same address.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Muting and tone control by Jonathan Isom <jisom@ematic.com>
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Copyright (c) 2000 Eric Sandeen <eric_sandeen@bigfoot.com>
128c2ecf20Sopenharmony_ci * Copyright (c) 2006 Mauro Carvalho Chehab <mchehab@kernel.org>
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * Based on tda9855.c by Steve VanDeBogart (vandebo@uclink.berkeley.edu)
158c2ecf20Sopenharmony_ci * Which was based on tda8425.c by Greg Alexander (c) 1998
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * OPTIONS:
188c2ecf20Sopenharmony_ci * debug    - set to 1 if you'd like to see debug messages
198c2ecf20Sopenharmony_ci *            set to 2 if you'd like to be inundated with debug messages
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * loudness - set between 0 and 15 for varying degrees of loudness effect
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * maxvol   - set maximum volume to +20db (1), default is 0db(0)
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <linux/module.h>
278c2ecf20Sopenharmony_ci#include <linux/init.h>
288c2ecf20Sopenharmony_ci#include <linux/kernel.h>
298c2ecf20Sopenharmony_ci#include <linux/string.h>
308c2ecf20Sopenharmony_ci#include <linux/timer.h>
318c2ecf20Sopenharmony_ci#include <linux/delay.h>
328c2ecf20Sopenharmony_ci#include <linux/errno.h>
338c2ecf20Sopenharmony_ci#include <linux/slab.h>
348c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
358c2ecf20Sopenharmony_ci#include <linux/i2c.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
388c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h>
398c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h>
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#ifndef VIDEO_AUDIO_BALANCE
428c2ecf20Sopenharmony_ci# define VIDEO_AUDIO_BALANCE 32
438c2ecf20Sopenharmony_ci#endif
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ciMODULE_AUTHOR("Eric Sandeen <eric_sandeen@bigfoot.com>");
468c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("bttv driver for the tda7432 audio processor chip");
478c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic int maxvol;
508c2ecf20Sopenharmony_cistatic int loudness; /* disable loudness by default */
518c2ecf20Sopenharmony_cistatic int debug;	 /* insmod parameter */
528c2ecf20Sopenharmony_cimodule_param(debug, int, S_IRUGO | S_IWUSR);
538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Set debugging level from 0 to 3. Default is off(0).");
548c2ecf20Sopenharmony_cimodule_param(loudness, int, S_IRUGO);
558c2ecf20Sopenharmony_ciMODULE_PARM_DESC(loudness, "Turn loudness on(1) else off(0). Default is off(0).");
568c2ecf20Sopenharmony_cimodule_param(maxvol, int, S_IRUGO | S_IWUSR);
578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(maxvol, "Set maximum volume to +20dB(0) else +0dB(1). Default is +20dB(0).");
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/* Structure of address and subaddresses for the tda7432 */
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistruct tda7432 {
638c2ecf20Sopenharmony_ci	struct v4l2_subdev sd;
648c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler hdl;
658c2ecf20Sopenharmony_ci	struct {
668c2ecf20Sopenharmony_ci		/* bass/treble cluster */
678c2ecf20Sopenharmony_ci		struct v4l2_ctrl *bass;
688c2ecf20Sopenharmony_ci		struct v4l2_ctrl *treble;
698c2ecf20Sopenharmony_ci	};
708c2ecf20Sopenharmony_ci	struct {
718c2ecf20Sopenharmony_ci		/* mute/balance cluster */
728c2ecf20Sopenharmony_ci		struct v4l2_ctrl *mute;
738c2ecf20Sopenharmony_ci		struct v4l2_ctrl *balance;
748c2ecf20Sopenharmony_ci	};
758c2ecf20Sopenharmony_ci};
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic inline struct tda7432 *to_state(struct v4l2_subdev *sd)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	return container_of(sd, struct tda7432, sd);
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	return &container_of(ctrl->handler, struct tda7432, hdl)->sd;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/* The TDA7432 is made by STS-Thompson
888c2ecf20Sopenharmony_ci * http://www.st.com
898c2ecf20Sopenharmony_ci * http://us.st.com/stonline/books/pdf/docs/4056.pdf
908c2ecf20Sopenharmony_ci *
918c2ecf20Sopenharmony_ci * TDA7432: I2C-bus controlled basic audio processor
928c2ecf20Sopenharmony_ci *
938c2ecf20Sopenharmony_ci * The TDA7432 controls basic audio functions like volume, balance,
948c2ecf20Sopenharmony_ci * and tone control (including loudness).  It also has four channel
958c2ecf20Sopenharmony_ci * output (for front and rear).  Since most vidcap cards probably
968c2ecf20Sopenharmony_ci * don't have 4 channel output, this driver will set front & rear
978c2ecf20Sopenharmony_ci * together (no independent control).
988c2ecf20Sopenharmony_ci */
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci		/* Subaddresses for TDA7432 */
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci#define TDA7432_IN	0x00 /* Input select                 */
1038c2ecf20Sopenharmony_ci#define TDA7432_VL	0x01 /* Volume                       */
1048c2ecf20Sopenharmony_ci#define TDA7432_TN	0x02 /* Bass, Treble (Tone)          */
1058c2ecf20Sopenharmony_ci#define TDA7432_LF	0x03 /* Attenuation LF (Left Front)  */
1068c2ecf20Sopenharmony_ci#define TDA7432_LR	0x04 /* Attenuation LR (Left Rear)   */
1078c2ecf20Sopenharmony_ci#define TDA7432_RF	0x05 /* Attenuation RF (Right Front) */
1088c2ecf20Sopenharmony_ci#define TDA7432_RR	0x06 /* Attenuation RR (Right Rear)  */
1098c2ecf20Sopenharmony_ci#define TDA7432_LD	0x07 /* Loudness                     */
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci		/* Masks for bits in TDA7432 subaddresses */
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/* Many of these not used - just for documentation */
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/* Subaddress 0x00 - Input selection and bass control */
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/* Bits 0,1,2 control input:
1198c2ecf20Sopenharmony_ci * 0x00 - Stereo input
1208c2ecf20Sopenharmony_ci * 0x02 - Mono input
1218c2ecf20Sopenharmony_ci * 0x03 - Mute  (Using Attenuators Plays better with modules)
1228c2ecf20Sopenharmony_ci * Mono probably isn't used - I'm guessing only the stereo
1238c2ecf20Sopenharmony_ci * input is connected on most cards, so we'll set it to stereo.
1248c2ecf20Sopenharmony_ci *
1258c2ecf20Sopenharmony_ci * Bit 3 controls bass cut: 0/1 is non-symmetric/symmetric bass cut
1268c2ecf20Sopenharmony_ci * Bit 4 controls bass range: 0/1 is extended/standard bass range
1278c2ecf20Sopenharmony_ci *
1288c2ecf20Sopenharmony_ci * Highest 3 bits not used
1298c2ecf20Sopenharmony_ci */
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci#define TDA7432_STEREO_IN	0
1328c2ecf20Sopenharmony_ci#define TDA7432_MONO_IN		2	/* Probably won't be used */
1338c2ecf20Sopenharmony_ci#define TDA7432_BASS_SYM	1 << 3
1348c2ecf20Sopenharmony_ci#define TDA7432_BASS_NORM	1 << 4
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci/* Subaddress 0x01 - Volume */
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci/* Lower 7 bits control volume from -79dB to +32dB in 1dB steps
1398c2ecf20Sopenharmony_ci * Recommended maximum is +20 dB
1408c2ecf20Sopenharmony_ci *
1418c2ecf20Sopenharmony_ci * +32dB: 0x00
1428c2ecf20Sopenharmony_ci * +20dB: 0x0c
1438c2ecf20Sopenharmony_ci *   0dB: 0x20
1448c2ecf20Sopenharmony_ci * -79dB: 0x6f
1458c2ecf20Sopenharmony_ci *
1468c2ecf20Sopenharmony_ci * MSB (bit 7) controls loudness: 1/0 is loudness on/off
1478c2ecf20Sopenharmony_ci */
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci#define	TDA7432_VOL_0DB		0x20
1508c2ecf20Sopenharmony_ci#define TDA7432_LD_ON		1 << 7
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci/* Subaddress 0x02 - Tone control */
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci/* Bits 0,1,2 control absolute treble gain from 0dB to 14dB
1568c2ecf20Sopenharmony_ci * 0x0 is 14dB, 0x7 is 0dB
1578c2ecf20Sopenharmony_ci *
1588c2ecf20Sopenharmony_ci * Bit 3 controls treble attenuation/gain (sign)
1598c2ecf20Sopenharmony_ci * 1 = gain (+)
1608c2ecf20Sopenharmony_ci * 0 = attenuation (-)
1618c2ecf20Sopenharmony_ci *
1628c2ecf20Sopenharmony_ci * Bits 4,5,6 control absolute bass gain from 0dB to 14dB
1638c2ecf20Sopenharmony_ci * (This is only true for normal base range, set in 0x00)
1648c2ecf20Sopenharmony_ci * 0x0 << 4 is 14dB, 0x7 is 0dB
1658c2ecf20Sopenharmony_ci *
1668c2ecf20Sopenharmony_ci * Bit 7 controls bass attenuation/gain (sign)
1678c2ecf20Sopenharmony_ci * 1 << 7 = gain (+)
1688c2ecf20Sopenharmony_ci * 0 << 7 = attenuation (-)
1698c2ecf20Sopenharmony_ci *
1708c2ecf20Sopenharmony_ci * Example:
1718c2ecf20Sopenharmony_ci * 1 1 0 1 0 1 0 1 is +4dB bass, -4dB treble
1728c2ecf20Sopenharmony_ci */
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci#define TDA7432_TREBLE_0DB		0xf
1758c2ecf20Sopenharmony_ci#define TDA7432_TREBLE			7
1768c2ecf20Sopenharmony_ci#define TDA7432_TREBLE_GAIN		1 << 3
1778c2ecf20Sopenharmony_ci#define TDA7432_BASS_0DB		0xf
1788c2ecf20Sopenharmony_ci#define TDA7432_BASS			7 << 4
1798c2ecf20Sopenharmony_ci#define TDA7432_BASS_GAIN		1 << 7
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/* Subaddress 0x03 - Left  Front attenuation */
1838c2ecf20Sopenharmony_ci/* Subaddress 0x04 - Left  Rear  attenuation */
1848c2ecf20Sopenharmony_ci/* Subaddress 0x05 - Right Front attenuation */
1858c2ecf20Sopenharmony_ci/* Subaddress 0x06 - Right Rear  attenuation */
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci/* Bits 0,1,2,3,4 control attenuation from 0dB to -37.5dB
1888c2ecf20Sopenharmony_ci * in 1.5dB steps.
1898c2ecf20Sopenharmony_ci *
1908c2ecf20Sopenharmony_ci * 0x00 is     0dB
1918c2ecf20Sopenharmony_ci * 0x1f is -37.5dB
1928c2ecf20Sopenharmony_ci *
1938c2ecf20Sopenharmony_ci * Bit 5 mutes that channel when set (1 = mute, 0 = unmute)
1948c2ecf20Sopenharmony_ci * We'll use the mute on the input, though (above)
1958c2ecf20Sopenharmony_ci * Bits 6,7 unused
1968c2ecf20Sopenharmony_ci */
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci#define TDA7432_ATTEN_0DB	0x00
1998c2ecf20Sopenharmony_ci#define TDA7432_MUTE        0x1 << 5
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci/* Subaddress 0x07 - Loudness Control */
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci/* Bits 0,1,2,3 control loudness from 0dB to -15dB in 1dB steps
2058c2ecf20Sopenharmony_ci * when bit 4 is NOT set
2068c2ecf20Sopenharmony_ci *
2078c2ecf20Sopenharmony_ci * 0x0 is   0dB
2088c2ecf20Sopenharmony_ci * 0xf is -15dB
2098c2ecf20Sopenharmony_ci *
2108c2ecf20Sopenharmony_ci * If bit 4 is set, then there is a flat attenuation according to
2118c2ecf20Sopenharmony_ci * the lower 4 bits, as above.
2128c2ecf20Sopenharmony_ci *
2138c2ecf20Sopenharmony_ci * Bits 5,6,7 unused
2148c2ecf20Sopenharmony_ci */
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci/* Begin code */
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic int tda7432_write(struct v4l2_subdev *sd, int subaddr, int val)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
2238c2ecf20Sopenharmony_ci	unsigned char buffer[2];
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	v4l2_dbg(2, debug, sd, "In tda7432_write\n");
2268c2ecf20Sopenharmony_ci	v4l2_dbg(1, debug, sd, "Writing %d 0x%x\n", subaddr, val);
2278c2ecf20Sopenharmony_ci	buffer[0] = subaddr;
2288c2ecf20Sopenharmony_ci	buffer[1] = val;
2298c2ecf20Sopenharmony_ci	if (2 != i2c_master_send(client, buffer, 2)) {
2308c2ecf20Sopenharmony_ci		v4l2_err(sd, "I/O error, trying (write %d 0x%x)\n",
2318c2ecf20Sopenharmony_ci		       subaddr, val);
2328c2ecf20Sopenharmony_ci		return -1;
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci	return 0;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic int tda7432_set(struct v4l2_subdev *sd)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
2408c2ecf20Sopenharmony_ci	unsigned char buf[16];
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	buf[0]  = TDA7432_IN;
2438c2ecf20Sopenharmony_ci	buf[1]  = TDA7432_STEREO_IN |  /* Main (stereo) input   */
2448c2ecf20Sopenharmony_ci		  TDA7432_BASS_SYM  |  /* Symmetric bass cut    */
2458c2ecf20Sopenharmony_ci		  TDA7432_BASS_NORM;   /* Normal bass range     */
2468c2ecf20Sopenharmony_ci	buf[2]  = 0x3b;
2478c2ecf20Sopenharmony_ci	if (loudness)			 /* Turn loudness on?     */
2488c2ecf20Sopenharmony_ci		buf[2] |= TDA7432_LD_ON;
2498c2ecf20Sopenharmony_ci	buf[3]  = TDA7432_TREBLE_0DB | (TDA7432_BASS_0DB << 4);
2508c2ecf20Sopenharmony_ci	buf[4]  = TDA7432_ATTEN_0DB;
2518c2ecf20Sopenharmony_ci	buf[5]  = TDA7432_ATTEN_0DB;
2528c2ecf20Sopenharmony_ci	buf[6]  = TDA7432_ATTEN_0DB;
2538c2ecf20Sopenharmony_ci	buf[7]  = TDA7432_ATTEN_0DB;
2548c2ecf20Sopenharmony_ci	buf[8]  = loudness;
2558c2ecf20Sopenharmony_ci	if (9 != i2c_master_send(client, buf, 9)) {
2568c2ecf20Sopenharmony_ci		v4l2_err(sd, "I/O error, trying tda7432_set\n");
2578c2ecf20Sopenharmony_ci		return -1;
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	return 0;
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic int tda7432_log_status(struct v4l2_subdev *sd)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	struct tda7432 *state = to_state(sd);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
2688c2ecf20Sopenharmony_ci	return 0;
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic int tda7432_s_ctrl(struct v4l2_ctrl *ctrl)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = to_sd(ctrl);
2748c2ecf20Sopenharmony_ci	struct tda7432 *t = to_state(sd);
2758c2ecf20Sopenharmony_ci	u8 bass, treble, volume;
2768c2ecf20Sopenharmony_ci	u8 lf, lr, rf, rr;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	switch (ctrl->id) {
2798c2ecf20Sopenharmony_ci	case V4L2_CID_AUDIO_MUTE:
2808c2ecf20Sopenharmony_ci		if (t->balance->val < 0) {
2818c2ecf20Sopenharmony_ci			/* shifted to left, attenuate right */
2828c2ecf20Sopenharmony_ci			rr = rf = -t->balance->val;
2838c2ecf20Sopenharmony_ci			lr = lf = TDA7432_ATTEN_0DB;
2848c2ecf20Sopenharmony_ci		} else if (t->balance->val > 0) {
2858c2ecf20Sopenharmony_ci			/* shifted to right, attenuate left */
2868c2ecf20Sopenharmony_ci			rr = rf = TDA7432_ATTEN_0DB;
2878c2ecf20Sopenharmony_ci			lr = lf = t->balance->val;
2888c2ecf20Sopenharmony_ci		} else {
2898c2ecf20Sopenharmony_ci			/* centered */
2908c2ecf20Sopenharmony_ci			rr = rf = TDA7432_ATTEN_0DB;
2918c2ecf20Sopenharmony_ci			lr = lf = TDA7432_ATTEN_0DB;
2928c2ecf20Sopenharmony_ci		}
2938c2ecf20Sopenharmony_ci		if (t->mute->val) {
2948c2ecf20Sopenharmony_ci			lf |= TDA7432_MUTE;
2958c2ecf20Sopenharmony_ci			lr |= TDA7432_MUTE;
2968c2ecf20Sopenharmony_ci			rf |= TDA7432_MUTE;
2978c2ecf20Sopenharmony_ci			rr |= TDA7432_MUTE;
2988c2ecf20Sopenharmony_ci		}
2998c2ecf20Sopenharmony_ci		/* Mute & update balance*/
3008c2ecf20Sopenharmony_ci		tda7432_write(sd, TDA7432_LF, lf);
3018c2ecf20Sopenharmony_ci		tda7432_write(sd, TDA7432_LR, lr);
3028c2ecf20Sopenharmony_ci		tda7432_write(sd, TDA7432_RF, rf);
3038c2ecf20Sopenharmony_ci		tda7432_write(sd, TDA7432_RR, rr);
3048c2ecf20Sopenharmony_ci		return 0;
3058c2ecf20Sopenharmony_ci	case V4L2_CID_AUDIO_VOLUME:
3068c2ecf20Sopenharmony_ci		volume = 0x6f - ctrl->val;
3078c2ecf20Sopenharmony_ci		if (loudness)		/* Turn on the loudness bit */
3088c2ecf20Sopenharmony_ci			volume |= TDA7432_LD_ON;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci		tda7432_write(sd, TDA7432_VL, volume);
3118c2ecf20Sopenharmony_ci		return 0;
3128c2ecf20Sopenharmony_ci	case V4L2_CID_AUDIO_BASS:
3138c2ecf20Sopenharmony_ci		bass = t->bass->val;
3148c2ecf20Sopenharmony_ci		treble = t->treble->val;
3158c2ecf20Sopenharmony_ci		if (bass >= 0x8)
3168c2ecf20Sopenharmony_ci			bass = 14 - (bass - 8);
3178c2ecf20Sopenharmony_ci		if (treble >= 0x8)
3188c2ecf20Sopenharmony_ci			treble = 14 - (treble - 8);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci		tda7432_write(sd, TDA7432_TN, 0x10 | (bass << 4) | treble);
3218c2ecf20Sopenharmony_ci		return 0;
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci	return -EINVAL;
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops tda7432_ctrl_ops = {
3298c2ecf20Sopenharmony_ci	.s_ctrl = tda7432_s_ctrl,
3308c2ecf20Sopenharmony_ci};
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops tda7432_core_ops = {
3338c2ecf20Sopenharmony_ci	.log_status = tda7432_log_status,
3348c2ecf20Sopenharmony_ci};
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops tda7432_ops = {
3378c2ecf20Sopenharmony_ci	.core = &tda7432_core_ops,
3388c2ecf20Sopenharmony_ci};
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- */
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci/* *********************** *
3438c2ecf20Sopenharmony_ci * i2c interface functions *
3448c2ecf20Sopenharmony_ci * *********************** */
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic int tda7432_probe(struct i2c_client *client,
3478c2ecf20Sopenharmony_ci			const struct i2c_device_id *id)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct tda7432 *t;
3508c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	v4l_info(client, "chip found @ 0x%02x (%s)\n",
3538c2ecf20Sopenharmony_ci			client->addr << 1, client->adapter->name);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	t = devm_kzalloc(&client->dev, sizeof(*t), GFP_KERNEL);
3568c2ecf20Sopenharmony_ci	if (!t)
3578c2ecf20Sopenharmony_ci		return -ENOMEM;
3588c2ecf20Sopenharmony_ci	sd = &t->sd;
3598c2ecf20Sopenharmony_ci	v4l2_i2c_subdev_init(sd, client, &tda7432_ops);
3608c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(&t->hdl, 5);
3618c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&t->hdl, &tda7432_ctrl_ops,
3628c2ecf20Sopenharmony_ci		V4L2_CID_AUDIO_VOLUME, 0, maxvol ? 0x68 : 0x4f, 1, maxvol ? 0x5d : 0x47);
3638c2ecf20Sopenharmony_ci	t->mute = v4l2_ctrl_new_std(&t->hdl, &tda7432_ctrl_ops,
3648c2ecf20Sopenharmony_ci		V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
3658c2ecf20Sopenharmony_ci	t->balance = v4l2_ctrl_new_std(&t->hdl, &tda7432_ctrl_ops,
3668c2ecf20Sopenharmony_ci		V4L2_CID_AUDIO_BALANCE, -31, 31, 1, 0);
3678c2ecf20Sopenharmony_ci	t->bass = v4l2_ctrl_new_std(&t->hdl, &tda7432_ctrl_ops,
3688c2ecf20Sopenharmony_ci		V4L2_CID_AUDIO_BASS, 0, 14, 1, 7);
3698c2ecf20Sopenharmony_ci	t->treble = v4l2_ctrl_new_std(&t->hdl, &tda7432_ctrl_ops,
3708c2ecf20Sopenharmony_ci		V4L2_CID_AUDIO_TREBLE, 0, 14, 1, 7);
3718c2ecf20Sopenharmony_ci	sd->ctrl_handler = &t->hdl;
3728c2ecf20Sopenharmony_ci	if (t->hdl.error) {
3738c2ecf20Sopenharmony_ci		int err = t->hdl.error;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci		v4l2_ctrl_handler_free(&t->hdl);
3768c2ecf20Sopenharmony_ci		return err;
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci	v4l2_ctrl_cluster(2, &t->bass);
3798c2ecf20Sopenharmony_ci	v4l2_ctrl_cluster(2, &t->mute);
3808c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_setup(&t->hdl);
3818c2ecf20Sopenharmony_ci	if (loudness < 0 || loudness > 15) {
3828c2ecf20Sopenharmony_ci		v4l2_warn(sd, "loudness parameter must be between 0 and 15\n");
3838c2ecf20Sopenharmony_ci		if (loudness < 0)
3848c2ecf20Sopenharmony_ci			loudness = 0;
3858c2ecf20Sopenharmony_ci		if (loudness > 15)
3868c2ecf20Sopenharmony_ci			loudness = 15;
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	tda7432_set(sd);
3908c2ecf20Sopenharmony_ci	return 0;
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic int tda7432_remove(struct i2c_client *client)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd = i2c_get_clientdata(client);
3968c2ecf20Sopenharmony_ci	struct tda7432 *t = to_state(sd);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	tda7432_set(sd);
3998c2ecf20Sopenharmony_ci	v4l2_device_unregister_subdev(sd);
4008c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&t->hdl);
4018c2ecf20Sopenharmony_ci	return 0;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic const struct i2c_device_id tda7432_id[] = {
4058c2ecf20Sopenharmony_ci	{ "tda7432", 0 },
4068c2ecf20Sopenharmony_ci	{ }
4078c2ecf20Sopenharmony_ci};
4088c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tda7432_id);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cistatic struct i2c_driver tda7432_driver = {
4118c2ecf20Sopenharmony_ci	.driver = {
4128c2ecf20Sopenharmony_ci		.name	= "tda7432",
4138c2ecf20Sopenharmony_ci	},
4148c2ecf20Sopenharmony_ci	.probe		= tda7432_probe,
4158c2ecf20Sopenharmony_ci	.remove		= tda7432_remove,
4168c2ecf20Sopenharmony_ci	.id_table	= tda7432_id,
4178c2ecf20Sopenharmony_ci};
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cimodule_i2c_driver(tda7432_driver);
420