1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include <linux/module.h>
25#include <linux/types.h>
26#include <linux/slab.h>
27#include <linux/ioctl.h>
28#include <linux/uaccess.h>
29#include <linux/i2c.h>
30#include <linux/videodev2.h>
31#include <media/v4l2-device.h>
32#include <media/v4l2-ctrls.h>
33#include <media/i2c/wm8775.h>
34
35MODULE_DESCRIPTION("wm8775 driver");
36MODULE_AUTHOR("Ulf Eklund, Hans Verkuil");
37MODULE_LICENSE("GPL");
38
39
40
41
42
43enum {
44 R7 = 7, R11 = 11,
45 R12, R13, R14, R15, R16, R17, R18, R19, R20, R21, R23 = 23,
46 TOT_REGS
47};
48
49#define ALC_HOLD 0x85
50#define ALC_EN 0x100
51
52struct wm8775_state {
53 struct v4l2_subdev sd;
54 struct v4l2_ctrl_handler hdl;
55 struct v4l2_ctrl *mute;
56 struct v4l2_ctrl *vol;
57 struct v4l2_ctrl *bal;
58 struct v4l2_ctrl *loud;
59 u8 input;
60};
61
62static inline struct wm8775_state *to_state(struct v4l2_subdev *sd)
63{
64 return container_of(sd, struct wm8775_state, sd);
65}
66
67static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
68{
69 return &container_of(ctrl->handler, struct wm8775_state, hdl)->sd;
70}
71
72static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val)
73{
74 struct i2c_client *client = v4l2_get_subdevdata(sd);
75 int i;
76
77 if (reg < 0 || reg >= TOT_REGS) {
78 v4l2_err(sd, "Invalid register R%d\n", reg);
79 return -1;
80 }
81
82 for (i = 0; i < 3; i++)
83 if (i2c_smbus_write_byte_data(client,
84 (reg << 1) | (val >> 8), val & 0xff) == 0)
85 return 0;
86 v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg);
87 return -1;
88}
89
90static void wm8775_set_audio(struct v4l2_subdev *sd, int quietly)
91{
92 struct wm8775_state *state = to_state(sd);
93 u8 vol_l, vol_r;
94 int muted = 0 != state->mute->val;
95 u16 volume = (u16)state->vol->val;
96 u16 balance = (u16)state->bal->val;
97
98
99 vol_l = (min(65536 - balance, 32768) * volume) >> 23;
100 vol_r = (min(balance, (u16)32768) * volume) >> 23;
101
102
103 if (muted || quietly)
104 wm8775_write(sd, R21, 0x0c0 | state->input);
105
106 wm8775_write(sd, R14, vol_l | 0x100);
107 wm8775_write(sd, R15, vol_r | 0x100);
108
109
110 if (!muted)
111 wm8775_write(sd, R21, state->input);
112}
113
114static int wm8775_s_routing(struct v4l2_subdev *sd,
115 u32 input, u32 output, u32 config)
116{
117 struct wm8775_state *state = to_state(sd);
118
119
120
121
122
123
124 if (input > 15) {
125 v4l2_err(sd, "Invalid input %d.\n", input);
126 return -EINVAL;
127 }
128 state->input = input;
129 if (v4l2_ctrl_g_ctrl(state->mute))
130 return 0;
131 if (!v4l2_ctrl_g_ctrl(state->vol))
132 return 0;
133 wm8775_set_audio(sd, 1);
134 return 0;
135}
136
137static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl)
138{
139 struct v4l2_subdev *sd = to_sd(ctrl);
140
141 switch (ctrl->id) {
142 case V4L2_CID_AUDIO_MUTE:
143 case V4L2_CID_AUDIO_VOLUME:
144 case V4L2_CID_AUDIO_BALANCE:
145 wm8775_set_audio(sd, 0);
146 return 0;
147 case V4L2_CID_AUDIO_LOUDNESS:
148 wm8775_write(sd, R17, (ctrl->val ? ALC_EN : 0) | ALC_HOLD);
149 return 0;
150 }
151 return -EINVAL;
152}
153
154static int wm8775_log_status(struct v4l2_subdev *sd)
155{
156 struct wm8775_state *state = to_state(sd);
157
158 v4l2_info(sd, "Input: %d\n", state->input);
159 v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
160 return 0;
161}
162
163static int wm8775_s_frequency(struct v4l2_subdev *sd, const struct v4l2_frequency *freq)
164{
165 wm8775_set_audio(sd, 0);
166 return 0;
167}
168
169
170
171static const struct v4l2_ctrl_ops wm8775_ctrl_ops = {
172 .s_ctrl = wm8775_s_ctrl,
173};
174
175static const struct v4l2_subdev_core_ops wm8775_core_ops = {
176 .log_status = wm8775_log_status,
177};
178
179static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = {
180 .s_frequency = wm8775_s_frequency,
181};
182
183static const struct v4l2_subdev_audio_ops wm8775_audio_ops = {
184 .s_routing = wm8775_s_routing,
185};
186
187static const struct v4l2_subdev_ops wm8775_ops = {
188 .core = &wm8775_core_ops,
189 .tuner = &wm8775_tuner_ops,
190 .audio = &wm8775_audio_ops,
191};
192
193
194
195
196
197
198
199
200
201
202static int wm8775_probe(struct i2c_client *client,
203 const struct i2c_device_id *id)
204{
205 struct wm8775_state *state;
206 struct v4l2_subdev *sd;
207 int err;
208 bool is_nova_s = false;
209
210 if (client->dev.platform_data) {
211 struct wm8775_platform_data *data = client->dev.platform_data;
212 is_nova_s = data->is_nova_s;
213 }
214
215
216 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
217 return -EIO;
218
219 v4l_info(client, "chip found @ 0x%02x (%s)\n",
220 client->addr << 1, client->adapter->name);
221
222 state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
223 if (state == NULL)
224 return -ENOMEM;
225 sd = &state->sd;
226 v4l2_i2c_subdev_init(sd, client, &wm8775_ops);
227 state->input = 2;
228
229 v4l2_ctrl_handler_init(&state->hdl, 4);
230 state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
231 V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
232 state->vol = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
233 V4L2_CID_AUDIO_VOLUME, 0, 65535, (65535+99)/100, 0xCF00);
234 state->bal = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
235 V4L2_CID_AUDIO_BALANCE, 0, 65535, (65535+99)/100, 32768);
236 state->loud = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
237 V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 1);
238 sd->ctrl_handler = &state->hdl;
239 err = state->hdl.error;
240 if (err) {
241 v4l2_ctrl_handler_free(&state->hdl);
242 return err;
243 }
244
245
246
247
248 wm8775_write(sd, R23, 0x000);
249
250 wm8775_write(sd, R7, 0x000);
251
252 wm8775_write(sd, R11, 0x021);
253
254 wm8775_write(sd, R12, 0x102);
255
256 wm8775_write(sd, R13, 0x000);
257
258 if (!is_nova_s) {
259
260 wm8775_write(sd, R14, 0x1d4);
261
262 wm8775_write(sd, R15, 0x1d4);
263
264 wm8775_write(sd, R16, 0x1bf);
265
266
267 wm8775_write(sd, R17, 0x185);
268 } else {
269
270 wm8775_write(sd, R16, 0x1bb);
271
272 wm8775_write(sd, R17, (state->loud->val ? ALC_EN : 0) | ALC_HOLD);
273 }
274
275 wm8775_write(sd, R18, 0x0a2);
276
277 wm8775_write(sd, R19, 0x005);
278 if (!is_nova_s) {
279
280 wm8775_write(sd, R20, 0x07a);
281
282 wm8775_write(sd, R21, 0x102);
283 } else {
284
285 wm8775_write(sd, R20, 0x0fb);
286
287 wm8775_set_audio(sd, 1);
288 }
289 return 0;
290}
291
292static int wm8775_remove(struct i2c_client *client)
293{
294 struct v4l2_subdev *sd = i2c_get_clientdata(client);
295 struct wm8775_state *state = to_state(sd);
296
297 v4l2_device_unregister_subdev(sd);
298 v4l2_ctrl_handler_free(&state->hdl);
299 return 0;
300}
301
302static const struct i2c_device_id wm8775_id[] = {
303 { "wm8775", 0 },
304 { }
305};
306MODULE_DEVICE_TABLE(i2c, wm8775_id);
307
308static struct i2c_driver wm8775_driver = {
309 .driver = {
310 .name = "wm8775",
311 },
312 .probe = wm8775_probe,
313 .remove = wm8775_remove,
314 .id_table = wm8775_id,
315};
316
317module_i2c_driver(wm8775_driver);
318