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