aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/wm8775.c
diff options
context:
space:
mode:
authorlawrence rust <lawrence@softsystem.co.uk>2010-10-18 06:06:02 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2010-10-22 23:17:52 -0400
commitfcb9757333df37cf4a7feccef7ef6f5300643864 (patch)
treeb04e8567ef7fd1d6a67c291122dbd2476c38bcc8 /drivers/media/video/wm8775.c
parentcb0ed22270129b980257fa9c83b152f09ecd9eda (diff)
[media] Nova-S-Plus audio line input
This patch adds audio DMA capture and ALSA mixer elements for the line input jack of the Hauppauge Nova-S-plus DVB-S PCI card. The Nova-S-plus has a WM8775 ADC that is currently not detected. This patch enables this chip and exports volume, balance mute and ALC elements for ALSA mixer controls. [mchehab@redhat.com: Fix CodingStyle issues] Signed-off-by: Lawrence Rust <lawrence@softsystem.co.uk> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/wm8775.c')
-rw-r--r--drivers/media/video/wm8775.c104
1 files changed, 65 insertions, 39 deletions
diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c
index fe8ef6419f83..135525649086 100644
--- a/drivers/media/video/wm8775.c
+++ b/drivers/media/video/wm8775.c
@@ -35,6 +35,7 @@
35#include <media/v4l2-device.h> 35#include <media/v4l2-device.h>
36#include <media/v4l2-chip-ident.h> 36#include <media/v4l2-chip-ident.h>
37#include <media/v4l2-ctrls.h> 37#include <media/v4l2-ctrls.h>
38#include <media/wm8775.h>
38 39
39MODULE_DESCRIPTION("wm8775 driver"); 40MODULE_DESCRIPTION("wm8775 driver");
40MODULE_AUTHOR("Ulf Eklund, Hans Verkuil"); 41MODULE_AUTHOR("Ulf Eklund, Hans Verkuil");
@@ -50,10 +51,16 @@ enum {
50 TOT_REGS 51 TOT_REGS
51}; 52};
52 53
54#define ALC_HOLD 0x85 /* R17: use zero cross detection, ALC hold time 42.6 ms */
55#define ALC_EN 0x100 /* R17: ALC enable */
56
53struct wm8775_state { 57struct wm8775_state {
54 struct v4l2_subdev sd; 58 struct v4l2_subdev sd;
55 struct v4l2_ctrl_handler hdl; 59 struct v4l2_ctrl_handler hdl;
56 struct v4l2_ctrl *mute; 60 struct v4l2_ctrl *mute;
61 struct v4l2_ctrl *vol;
62 struct v4l2_ctrl *bal;
63 struct v4l2_ctrl *loud;
57 u8 input; /* Last selected input (0-0xf) */ 64 u8 input; /* Last selected input (0-0xf) */
58}; 65};
59 66
@@ -85,6 +92,30 @@ static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val)
85 return -1; 92 return -1;
86} 93}
87 94
95static void wm8775_set_audio(struct v4l2_subdev *sd, int quietly)
96{
97 struct wm8775_state *state = to_state(sd);
98 u8 vol_l, vol_r;
99 int muted = 0 != state->mute->val;
100 u16 volume = (u16)state->vol->val;
101 u16 balance = (u16)state->bal->val;
102
103 /* normalize ( 65535 to 0 -> 255 to 0 (+24dB to -103dB) ) */
104 vol_l = (min(65536 - balance, 32768) * volume) >> 23;
105 vol_r = (min(balance, (u16)32768) * volume) >> 23;
106
107 /* Mute */
108 if (muted || quietly)
109 wm8775_write(sd, R21, 0x0c0 | state->input);
110
111 wm8775_write(sd, R14, vol_l | 0x100); /* 0x100= Left channel ADC zero cross enable */
112 wm8775_write(sd, R15, vol_r | 0x100); /* 0x100= Right channel ADC zero cross enable */
113
114 /* Un-mute */
115 if (!muted)
116 wm8775_write(sd, R21, state->input);
117}
118
88static int wm8775_s_routing(struct v4l2_subdev *sd, 119static int wm8775_s_routing(struct v4l2_subdev *sd,
89 u32 input, u32 output, u32 config) 120 u32 input, u32 output, u32 config)
90{ 121{
@@ -102,25 +133,26 @@ static int wm8775_s_routing(struct v4l2_subdev *sd,
102 state->input = input; 133 state->input = input;
103 if (!v4l2_ctrl_g_ctrl(state->mute)) 134 if (!v4l2_ctrl_g_ctrl(state->mute))
104 return 0; 135 return 0;
105 wm8775_write(sd, R21, 0x0c0); 136 if (!v4l2_ctrl_g_ctrl(state->vol))
106 wm8775_write(sd, R14, 0x1d4); 137 return 0;
107 wm8775_write(sd, R15, 0x1d4); 138 if (!v4l2_ctrl_g_ctrl(state->bal))
108 wm8775_write(sd, R21, 0x100 + state->input); 139 return 0;
140 wm8775_set_audio(sd, 1);
109 return 0; 141 return 0;
110} 142}
111 143
112static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl) 144static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl)
113{ 145{
114 struct v4l2_subdev *sd = to_sd(ctrl); 146 struct v4l2_subdev *sd = to_sd(ctrl);
115 struct wm8775_state *state = to_state(sd);
116 147
117 switch (ctrl->id) { 148 switch (ctrl->id) {
118 case V4L2_CID_AUDIO_MUTE: 149 case V4L2_CID_AUDIO_MUTE:
119 wm8775_write(sd, R21, 0x0c0); 150 case V4L2_CID_AUDIO_VOLUME:
120 wm8775_write(sd, R14, 0x1d4); 151 case V4L2_CID_AUDIO_BALANCE:
121 wm8775_write(sd, R15, 0x1d4); 152 wm8775_set_audio(sd, 0);
122 if (!ctrl->val) 153 return 0;
123 wm8775_write(sd, R21, 0x100 + state->input); 154 case V4L2_CID_AUDIO_LOUDNESS:
155 wm8775_write(sd, R17, (ctrl->val ? ALC_EN : 0) | ALC_HOLD);
124 return 0; 156 return 0;
125 } 157 }
126 return -EINVAL; 158 return -EINVAL;
@@ -144,16 +176,7 @@ static int wm8775_log_status(struct v4l2_subdev *sd)
144 176
145static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) 177static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
146{ 178{
147 struct wm8775_state *state = to_state(sd); 179 wm8775_set_audio(sd, 0);
148
149 /* If I remove this, then it can happen that I have no
150 sound the first time I tune from static to a valid channel.
151 It's difficult to reproduce and is almost certainly related
152 to the zero cross detect circuit. */
153 wm8775_write(sd, R21, 0x0c0);
154 wm8775_write(sd, R14, 0x1d4);
155 wm8775_write(sd, R15, 0x1d4);
156 wm8775_write(sd, R21, 0x100 + state->input);
157 return 0; 180 return 0;
158} 181}
159 182
@@ -203,6 +226,7 @@ static int wm8775_probe(struct i2c_client *client,
203{ 226{
204 struct wm8775_state *state; 227 struct wm8775_state *state;
205 struct v4l2_subdev *sd; 228 struct v4l2_subdev *sd;
229 int err;
206 230
207 /* Check if the adapter supports the needed features */ 231 /* Check if the adapter supports the needed features */
208 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 232 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -216,15 +240,21 @@ static int wm8775_probe(struct i2c_client *client,
216 return -ENOMEM; 240 return -ENOMEM;
217 sd = &state->sd; 241 sd = &state->sd;
218 v4l2_i2c_subdev_init(sd, client, &wm8775_ops); 242 v4l2_i2c_subdev_init(sd, client, &wm8775_ops);
243 sd->grp_id = WM8775_GID; /* subdev group id */
219 state->input = 2; 244 state->input = 2;
220 245
221 v4l2_ctrl_handler_init(&state->hdl, 1); 246 v4l2_ctrl_handler_init(&state->hdl, 4);
222 state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, 247 state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
223 V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); 248 V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
249 state->vol = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
250 V4L2_CID_AUDIO_VOLUME, 0, 65535, (65535+99)/100, 0xCF00); /* 0dB*/
251 state->bal = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
252 V4L2_CID_AUDIO_BALANCE, 0, 65535, (65535+99)/100, 32768);
253 state->loud = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops,
254 V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 1);
224 sd->ctrl_handler = &state->hdl; 255 sd->ctrl_handler = &state->hdl;
225 if (state->hdl.error) { 256 err = state->hdl.error;
226 int err = state->hdl.error; 257 if (err) {
227
228 v4l2_ctrl_handler_free(&state->hdl); 258 v4l2_ctrl_handler_free(&state->hdl);
229 kfree(state); 259 kfree(state);
230 return err; 260 return err;
@@ -236,29 +266,25 @@ static int wm8775_probe(struct i2c_client *client,
236 wm8775_write(sd, R23, 0x000); 266 wm8775_write(sd, R23, 0x000);
237 /* Disable zero cross detect timeout */ 267 /* Disable zero cross detect timeout */
238 wm8775_write(sd, R7, 0x000); 268 wm8775_write(sd, R7, 0x000);
239 /* Left justified, 24-bit mode */ 269 /* HPF enable, I2S mode, 24-bit */
240 wm8775_write(sd, R11, 0x021); 270 wm8775_write(sd, R11, 0x022);
241 /* Master mode, clock ratio 256fs */ 271 /* Master mode, clock ratio 256fs */
242 wm8775_write(sd, R12, 0x102); 272 wm8775_write(sd, R12, 0x102);
243 /* Powered up */ 273 /* Powered up */
244 wm8775_write(sd, R13, 0x000); 274 wm8775_write(sd, R13, 0x000);
245 /* ADC gain +2.5dB, enable zero cross */ 275 /* ALC stereo, ALC target level -5dB FS, ALC max gain +8dB */
246 wm8775_write(sd, R14, 0x1d4); 276 wm8775_write(sd, R16, 0x1bb);
247 /* ADC gain +2.5dB, enable zero cross */ 277 /* Set ALC mode and hold time */
248 wm8775_write(sd, R15, 0x1d4); 278 wm8775_write(sd, R17, (state->loud->val ? ALC_EN : 0) | ALC_HOLD);
249 /* ALC Stereo, ALC target level -1dB FS max gain +8dB */
250 wm8775_write(sd, R16, 0x1bf);
251 /* Enable gain control, use zero cross detection,
252 ALC hold time 42.6 ms */
253 wm8775_write(sd, R17, 0x185);
254 /* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */ 279 /* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */
255 wm8775_write(sd, R18, 0x0a2); 280 wm8775_write(sd, R18, 0x0a2);
256 /* Enable noise gate, threshold -72dBfs */ 281 /* Enable noise gate, threshold -72dBfs */
257 wm8775_write(sd, R19, 0x005); 282 wm8775_write(sd, R19, 0x005);
258 /* Transient window 4ms, lower PGA gain limit -1dB */ 283 /* Transient window 4ms, ALC min gain -5dB */
259 wm8775_write(sd, R20, 0x07a); 284 wm8775_write(sd, R20, 0x0fb);
260 /* LRBOTH = 1, use input 2. */ 285
261 wm8775_write(sd, R21, 0x102); 286 wm8775_set_audio(sd, 1); /* set volume/mute/mux */
287
262 return 0; 288 return 0;
263} 289}
264 290