blob: d4c83c39892a1ccc23ce7b55128e11506e3b145b [file] [log] [blame]
Thomas Gleixnerc942fdd2019-05-27 08:55:06 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Hans Verkuil0edeea42005-11-08 21:37:25 -08002/*
3 * wm8775 - driver version 0.0.1
4 *
5 * Copyright (C) 2004 Ulf Eklund <ivtv at eklund.to>
6 *
7 * Based on saa7115 driver
8 *
Hans Verkuil1f4b3362005-11-13 16:08:05 -08009 * Copyright (C) 2005 Hans Verkuil <hverkuil@xs4all.nl>
10 * - Cleanup
11 * - V4L2 API update
12 * - sound fixes
Hans Verkuil0edeea42005-11-08 21:37:25 -080013 */
14
Hans Verkuil0edeea42005-11-08 21:37:25 -080015#include <linux/module.h>
16#include <linux/types.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090017#include <linux/slab.h>
Hans Verkuil0edeea42005-11-08 21:37:25 -080018#include <linux/ioctl.h>
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -080019#include <linux/uaccess.h>
Hans Verkuil0edeea42005-11-08 21:37:25 -080020#include <linux/i2c.h>
Hans Verkuil33b687c2008-07-25 05:32:50 -030021#include <linux/videodev2.h>
Hans Verkuilca085fb2008-11-29 13:03:22 -030022#include <media/v4l2-device.h>
Hans Verkuil5aa9ae52010-04-24 08:23:53 -030023#include <media/v4l2-ctrls.h>
Mauro Carvalho Chehabb5dcee222015-11-10 12:01:44 -020024#include <media/i2c/wm8775.h>
Hans Verkuil0edeea42005-11-08 21:37:25 -080025
26MODULE_DESCRIPTION("wm8775 driver");
Hans Verkuil1f4b3362005-11-13 16:08:05 -080027MODULE_AUTHOR("Ulf Eklund, Hans Verkuil");
Hans Verkuil0edeea42005-11-08 21:37:25 -080028MODULE_LICENSE("GPL");
29
Hans Verkuil0edeea42005-11-08 21:37:25 -080030
Hans Verkuild9a53aa2007-09-13 11:08:25 -030031
Hans Verkuil0edeea42005-11-08 21:37:25 -080032/* ----------------------------------------------------------------------- */
33
34enum {
35 R7 = 7, R11 = 11,
36 R12, R13, R14, R15, R16, R17, R18, R19, R20, R21, R23 = 23,
37 TOT_REGS
38};
39
Lawrence Rust69518032011-02-06 17:46:12 -030040#define ALC_HOLD 0x85 /* R17: use zero cross detection, ALC hold time 42.6 ms */
41#define ALC_EN 0x100 /* R17: ALC enable */
42
Hans Verkuil0edeea42005-11-08 21:37:25 -080043struct wm8775_state {
Hans Verkuilca085fb2008-11-29 13:03:22 -030044 struct v4l2_subdev sd;
Hans Verkuil5aa9ae52010-04-24 08:23:53 -030045 struct v4l2_ctrl_handler hdl;
46 struct v4l2_ctrl *mute;
Lawrence Rust69518032011-02-06 17:46:12 -030047 struct v4l2_ctrl *vol;
48 struct v4l2_ctrl *bal;
49 struct v4l2_ctrl *loud;
Hans Verkuil0edeea42005-11-08 21:37:25 -080050 u8 input; /* Last selected input (0-0xf) */
Hans Verkuil0edeea42005-11-08 21:37:25 -080051};
52
Hans Verkuilca085fb2008-11-29 13:03:22 -030053static inline struct wm8775_state *to_state(struct v4l2_subdev *sd)
Hans Verkuil0edeea42005-11-08 21:37:25 -080054{
Hans Verkuilca085fb2008-11-29 13:03:22 -030055 return container_of(sd, struct wm8775_state, sd);
56}
57
Hans Verkuil5aa9ae52010-04-24 08:23:53 -030058static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
59{
60 return &container_of(ctrl->handler, struct wm8775_state, hdl)->sd;
61}
62
Hans Verkuilca085fb2008-11-29 13:03:22 -030063static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val)
64{
65 struct i2c_client *client = v4l2_get_subdevdata(sd);
Hans Verkuil0edeea42005-11-08 21:37:25 -080066 int i;
67
68 if (reg < 0 || reg >= TOT_REGS) {
Hans Verkuilca085fb2008-11-29 13:03:22 -030069 v4l2_err(sd, "Invalid register R%d\n", reg);
Hans Verkuil0edeea42005-11-08 21:37:25 -080070 return -1;
71 }
72
Hans Verkuil842441d2007-11-01 07:35:41 -030073 for (i = 0; i < 3; i++)
74 if (i2c_smbus_write_byte_data(client,
75 (reg << 1) | (val >> 8), val & 0xff) == 0)
Hans Verkuil0edeea42005-11-08 21:37:25 -080076 return 0;
Hans Verkuilca085fb2008-11-29 13:03:22 -030077 v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg);
Hans Verkuil0edeea42005-11-08 21:37:25 -080078 return -1;
79}
80
Lawrence Rust69518032011-02-06 17:46:12 -030081static void wm8775_set_audio(struct v4l2_subdev *sd, int quietly)
82{
83 struct wm8775_state *state = to_state(sd);
84 u8 vol_l, vol_r;
85 int muted = 0 != state->mute->val;
86 u16 volume = (u16)state->vol->val;
87 u16 balance = (u16)state->bal->val;
88
89 /* normalize ( 65535 to 0 -> 255 to 0 (+24dB to -103dB) ) */
90 vol_l = (min(65536 - balance, 32768) * volume) >> 23;
91 vol_r = (min(balance, (u16)32768) * volume) >> 23;
92
93 /* Mute */
94 if (muted || quietly)
95 wm8775_write(sd, R21, 0x0c0 | state->input);
96
97 wm8775_write(sd, R14, vol_l | 0x100); /* 0x100= Left channel ADC zero cross enable */
98 wm8775_write(sd, R15, vol_r | 0x100); /* 0x100= Right channel ADC zero cross enable */
99
100 /* Un-mute */
101 if (!muted)
102 wm8775_write(sd, R21, state->input);
103}
104
Hans Verkuil5325b422009-04-02 11:26:22 -0300105static int wm8775_s_routing(struct v4l2_subdev *sd,
106 u32 input, u32 output, u32 config)
Hans Verkuilca085fb2008-11-29 13:03:22 -0300107{
108 struct wm8775_state *state = to_state(sd);
109
110 /* There are 4 inputs and one output. Zero or more inputs
111 are multiplexed together to the output. Hence there are
112 16 combinations.
113 If only one input is active (the normal case) then the
114 input values 1, 2, 4 or 8 should be used. */
Hans Verkuil5325b422009-04-02 11:26:22 -0300115 if (input > 15) {
116 v4l2_err(sd, "Invalid input %d.\n", input);
Hans Verkuilca085fb2008-11-29 13:03:22 -0300117 return -EINVAL;
118 }
Hans Verkuil5325b422009-04-02 11:26:22 -0300119 state->input = input;
Hans Verkuil3af41a32013-11-11 11:02:52 -0300120 if (v4l2_ctrl_g_ctrl(state->mute))
Hans Verkuilca085fb2008-11-29 13:03:22 -0300121 return 0;
Lawrence Rust69518032011-02-06 17:46:12 -0300122 if (!v4l2_ctrl_g_ctrl(state->vol))
123 return 0;
Lawrence Rust69518032011-02-06 17:46:12 -0300124 wm8775_set_audio(sd, 1);
Hans Verkuilca085fb2008-11-29 13:03:22 -0300125 return 0;
126}
127
Hans Verkuil5aa9ae52010-04-24 08:23:53 -0300128static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl)
Hans Verkuilca085fb2008-11-29 13:03:22 -0300129{
Hans Verkuil5aa9ae52010-04-24 08:23:53 -0300130 struct v4l2_subdev *sd = to_sd(ctrl);
Hans Verkuilca085fb2008-11-29 13:03:22 -0300131
Hans Verkuil5aa9ae52010-04-24 08:23:53 -0300132 switch (ctrl->id) {
133 case V4L2_CID_AUDIO_MUTE:
Lawrence Rust69518032011-02-06 17:46:12 -0300134 case V4L2_CID_AUDIO_VOLUME:
135 case V4L2_CID_AUDIO_BALANCE:
136 wm8775_set_audio(sd, 0);
137 return 0;
138 case V4L2_CID_AUDIO_LOUDNESS:
139 wm8775_write(sd, R17, (ctrl->val ? ALC_EN : 0) | ALC_HOLD);
Hans Verkuil5aa9ae52010-04-24 08:23:53 -0300140 return 0;
141 }
142 return -EINVAL;
Hans Verkuilca085fb2008-11-29 13:03:22 -0300143}
144
Hans Verkuilca085fb2008-11-29 13:03:22 -0300145static int wm8775_log_status(struct v4l2_subdev *sd)
146{
147 struct wm8775_state *state = to_state(sd);
148
Hans Verkuil5aa9ae52010-04-24 08:23:53 -0300149 v4l2_info(sd, "Input: %d\n", state->input);
150 v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
Hans Verkuilca085fb2008-11-29 13:03:22 -0300151 return 0;
152}
153
Hans Verkuilb530a442013-03-19 04:09:26 -0300154static int wm8775_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *freq)
Hans Verkuilca085fb2008-11-29 13:03:22 -0300155{
Lawrence Rust69518032011-02-06 17:46:12 -0300156 wm8775_set_audio(sd, 0);
Hans Verkuilca085fb2008-11-29 13:03:22 -0300157 return 0;
158}
159
Hans Verkuil0edeea42005-11-08 21:37:25 -0800160/* ----------------------------------------------------------------------- */
161
Hans Verkuil5aa9ae52010-04-24 08:23:53 -0300162static const struct v4l2_ctrl_ops wm8775_ctrl_ops = {
163 .s_ctrl = wm8775_s_ctrl,
164};
165
Hans Verkuilca085fb2008-11-29 13:03:22 -0300166static const struct v4l2_subdev_core_ops wm8775_core_ops = {
167 .log_status = wm8775_log_status,
Hans Verkuilca085fb2008-11-29 13:03:22 -0300168};
169
170static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = {
171 .s_frequency = wm8775_s_frequency,
172};
173
174static const struct v4l2_subdev_audio_ops wm8775_audio_ops = {
175 .s_routing = wm8775_s_routing,
176};
177
178static const struct v4l2_subdev_ops wm8775_ops = {
179 .core = &wm8775_core_ops,
180 .tuner = &wm8775_tuner_ops,
181 .audio = &wm8775_audio_ops,
182};
183
184/* ----------------------------------------------------------------------- */
185
Hans Verkuil0edeea42005-11-08 21:37:25 -0800186/* i2c implementation */
187
188/*
189 * Generic i2c probe
190 * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
191 */
192
Jean Delvared2653e92008-04-29 23:11:39 +0200193static int wm8775_probe(struct i2c_client *client,
194 const struct i2c_device_id *id)
Hans Verkuil0edeea42005-11-08 21:37:25 -0800195{
Hans Verkuil0edeea42005-11-08 21:37:25 -0800196 struct wm8775_state *state;
Hans Verkuilca085fb2008-11-29 13:03:22 -0300197 struct v4l2_subdev *sd;
Lawrence Rust69518032011-02-06 17:46:12 -0300198 int err;
199 bool is_nova_s = false;
200
201 if (client->dev.platform_data) {
202 struct wm8775_platform_data *data = client->dev.platform_data;
203 is_nova_s = data->is_nova_s;
204 }
Hans Verkuil0edeea42005-11-08 21:37:25 -0800205
206 /* Check if the adapter supports the needed features */
Hans Verkuild9a53aa2007-09-13 11:08:25 -0300207 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
208 return -EIO;
Hans Verkuil0edeea42005-11-08 21:37:25 -0800209
Hans Verkuilca085fb2008-11-29 13:03:22 -0300210 v4l_info(client, "chip found @ 0x%02x (%s)\n",
Hans Verkuil842441d2007-11-01 07:35:41 -0300211 client->addr << 1, client->adapter->name);
Hans Verkuil0edeea42005-11-08 21:37:25 -0800212
Laurent Pinchartc02b2112013-05-02 08:29:43 -0300213 state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
Hans Verkuil842441d2007-11-01 07:35:41 -0300214 if (state == NULL)
Hans Verkuil0edeea42005-11-08 21:37:25 -0800215 return -ENOMEM;
Hans Verkuilca085fb2008-11-29 13:03:22 -0300216 sd = &state->sd;
217 v4l2_i2c_subdev_init(sd, client, &wm8775_ops);
Hans Verkuil0edeea42005-11-08 21:37:25 -0800218 state->input = 2;
Hans Verkuil5aa9ae52010-04-24 08:23:53 -0300219
Lawrence Rust69518032011-02-06 17:46:12 -0300220 v4l2_ctrl_handler_init(&state->hdl, 4);
Hans Verkuil5aa9ae52010-04-24 08:23:53 -0300221 state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
222 V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
Lawrence Rust69518032011-02-06 17:46:12 -0300223 state->vol = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
224 V4L2_CID_AUDIO_VOLUME, 0, 65535, (65535+99)/100, 0xCF00); /* 0dB*/
225 state->bal = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
226 V4L2_CID_AUDIO_BALANCE, 0, 65535, (65535+99)/100, 32768);
227 state->loud = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
228 V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 1);
Hans Verkuil5aa9ae52010-04-24 08:23:53 -0300229 sd->ctrl_handler = &state->hdl;
Lawrence Rust69518032011-02-06 17:46:12 -0300230 err = state->hdl.error;
231 if (err) {
Hans Verkuil5aa9ae52010-04-24 08:23:53 -0300232 v4l2_ctrl_handler_free(&state->hdl);
Hans Verkuil5aa9ae52010-04-24 08:23:53 -0300233 return err;
234 }
Hans Verkuil0edeea42005-11-08 21:37:25 -0800235
Hans Verkuil842441d2007-11-01 07:35:41 -0300236 /* Initialize wm8775 */
237
238 /* RESET */
Hans Verkuilca085fb2008-11-29 13:03:22 -0300239 wm8775_write(sd, R23, 0x000);
Hans Verkuil842441d2007-11-01 07:35:41 -0300240 /* Disable zero cross detect timeout */
Hans Verkuilca085fb2008-11-29 13:03:22 -0300241 wm8775_write(sd, R7, 0x000);
Lawrence Rust69518032011-02-06 17:46:12 -0300242 /* HPF enable, left justified, 24-bit (Philips) mode */
Mauro Carvalho Chehab46e67ac2011-01-03 09:09:56 -0200243 wm8775_write(sd, R11, 0x021);
Hans Verkuil842441d2007-11-01 07:35:41 -0300244 /* Master mode, clock ratio 256fs */
Hans Verkuilca085fb2008-11-29 13:03:22 -0300245 wm8775_write(sd, R12, 0x102);
Hans Verkuil842441d2007-11-01 07:35:41 -0300246 /* Powered up */
Hans Verkuilca085fb2008-11-29 13:03:22 -0300247 wm8775_write(sd, R13, 0x000);
Lawrence Rust69518032011-02-06 17:46:12 -0300248
249 if (!is_nova_s) {
250 /* ADC gain +2.5dB, enable zero cross */
251 wm8775_write(sd, R14, 0x1d4);
252 /* ADC gain +2.5dB, enable zero cross */
253 wm8775_write(sd, R15, 0x1d4);
254 /* ALC Stereo, ALC target level -1dB FS max gain +8dB */
255 wm8775_write(sd, R16, 0x1bf);
256 /* Enable gain control, use zero cross detection,
257 ALC hold time 42.6 ms */
258 wm8775_write(sd, R17, 0x185);
259 } else {
260 /* ALC stereo, ALC target level -5dB FS, ALC max gain +8dB */
261 wm8775_write(sd, R16, 0x1bb);
262 /* Set ALC mode and hold time */
263 wm8775_write(sd, R17, (state->loud->val ? ALC_EN : 0) | ALC_HOLD);
264 }
Hans Verkuil842441d2007-11-01 07:35:41 -0300265 /* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */
Hans Verkuilca085fb2008-11-29 13:03:22 -0300266 wm8775_write(sd, R18, 0x0a2);
Hans Verkuil842441d2007-11-01 07:35:41 -0300267 /* Enable noise gate, threshold -72dBfs */
Hans Verkuilca085fb2008-11-29 13:03:22 -0300268 wm8775_write(sd, R19, 0x005);
Lawrence Rust69518032011-02-06 17:46:12 -0300269 if (!is_nova_s) {
270 /* Transient window 4ms, lower PGA gain limit -1dB */
271 wm8775_write(sd, R20, 0x07a);
272 /* LRBOTH = 1, use input 2. */
273 wm8775_write(sd, R21, 0x102);
274 } else {
275 /* Transient window 4ms, ALC min gain -5dB */
276 wm8775_write(sd, R20, 0x0fb);
277
278 wm8775_set_audio(sd, 1); /* set volume/mute/mux */
279 }
Hans Verkuil0edeea42005-11-08 21:37:25 -0800280 return 0;
281}
282
Hans Verkuild9a53aa2007-09-13 11:08:25 -0300283static int wm8775_remove(struct i2c_client *client)
Hans Verkuil0edeea42005-11-08 21:37:25 -0800284{
Hans Verkuilca085fb2008-11-29 13:03:22 -0300285 struct v4l2_subdev *sd = i2c_get_clientdata(client);
Hans Verkuil5aa9ae52010-04-24 08:23:53 -0300286 struct wm8775_state *state = to_state(sd);
Hans Verkuilca085fb2008-11-29 13:03:22 -0300287
288 v4l2_device_unregister_subdev(sd);
Hans Verkuil5aa9ae52010-04-24 08:23:53 -0300289 v4l2_ctrl_handler_free(&state->hdl);
Hans Verkuil0edeea42005-11-08 21:37:25 -0800290 return 0;
291}
292
Jean Delvareaf294862008-05-18 20:49:40 +0200293static const struct i2c_device_id wm8775_id[] = {
294 { "wm8775", 0 },
295 { }
296};
297MODULE_DEVICE_TABLE(i2c, wm8775_id);
298
Hans Verkuila5b2c492010-09-15 15:27:48 -0300299static struct i2c_driver wm8775_driver = {
300 .driver = {
Hans Verkuila5b2c492010-09-15 15:27:48 -0300301 .name = "wm8775",
302 },
303 .probe = wm8775_probe,
304 .remove = wm8775_remove,
305 .id_table = wm8775_id,
Hans Verkuil0edeea42005-11-08 21:37:25 -0800306};
Hans Verkuila5b2c492010-09-15 15:27:48 -0300307
Axel Linc6e8d862012-02-12 06:56:32 -0300308module_i2c_driver(wm8775_driver);