]>
Commit | Line | Data |
---|---|---|
ac247433 HV |
1 | /* |
2 | * vp27smpx - driver version 0.0.1 | |
3 | * | |
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | |
5 | * | |
da80be21 KK |
6 | * Based on a tvaudio patch from Takahiro Adachi <tadachi@tadachi-net.com> |
7 | * and Kazuhiko Kawakami <kazz-0@mail.goo.ne.jp> | |
ac247433 HV |
8 | * |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program; if not, write to the Free Software | |
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
22 | */ | |
23 | ||
24 | #include <linux/module.h> | |
25 | #include <linux/types.h> | |
5a0e3ad6 | 26 | #include <linux/slab.h> |
ac247433 HV |
27 | #include <linux/ioctl.h> |
28 | #include <asm/uaccess.h> | |
29 | #include <linux/i2c.h> | |
30 | #include <linux/i2c-id.h> | |
33b687cf | 31 | #include <linux/videodev2.h> |
d9460f06 | 32 | #include <media/v4l2-device.h> |
ac247433 | 33 | #include <media/v4l2-chip-ident.h> |
f69d419a | 34 | #include <media/v4l2-i2c-drv.h> |
ac247433 HV |
35 | |
36 | MODULE_DESCRIPTION("vp27smpx driver"); | |
37 | MODULE_AUTHOR("Hans Verkuil"); | |
38 | MODULE_LICENSE("GPL"); | |
39 | ||
ac247433 HV |
40 | |
41 | /* ----------------------------------------------------------------------- */ | |
42 | ||
43 | struct vp27smpx_state { | |
d9460f06 | 44 | struct v4l2_subdev sd; |
ac247433 HV |
45 | int radio; |
46 | u32 audmode; | |
47 | }; | |
48 | ||
d9460f06 | 49 | static inline struct vp27smpx_state *to_state(struct v4l2_subdev *sd) |
ac247433 | 50 | { |
d9460f06 HV |
51 | return container_of(sd, struct vp27smpx_state, sd); |
52 | } | |
53 | ||
54 | static void vp27smpx_set_audmode(struct v4l2_subdev *sd, u32 audmode) | |
55 | { | |
56 | struct vp27smpx_state *state = to_state(sd); | |
57 | struct i2c_client *client = v4l2_get_subdevdata(sd); | |
ac247433 HV |
58 | u8 data[3] = { 0x00, 0x00, 0x04 }; |
59 | ||
60 | switch (audmode) { | |
35df38c0 HV |
61 | case V4L2_TUNER_MODE_MONO: |
62 | case V4L2_TUNER_MODE_LANG1: | |
63 | break; | |
64 | case V4L2_TUNER_MODE_STEREO: | |
65 | case V4L2_TUNER_MODE_LANG1_LANG2: | |
66 | data[1] = 0x01; | |
67 | break; | |
68 | case V4L2_TUNER_MODE_LANG2: | |
69 | data[1] = 0x02; | |
70 | break; | |
ac247433 HV |
71 | } |
72 | ||
35df38c0 | 73 | if (i2c_master_send(client, data, sizeof(data)) != sizeof(data)) |
d9460f06 | 74 | v4l2_err(sd, "I/O error setting audmode\n"); |
35df38c0 | 75 | else |
ac247433 | 76 | state->audmode = audmode; |
ac247433 HV |
77 | } |
78 | ||
d9460f06 | 79 | static int vp27smpx_s_radio(struct v4l2_subdev *sd) |
ac247433 | 80 | { |
d9460f06 | 81 | struct vp27smpx_state *state = to_state(sd); |
ac247433 | 82 | |
d9460f06 HV |
83 | state->radio = 1; |
84 | return 0; | |
85 | } | |
ac247433 | 86 | |
d9460f06 HV |
87 | static int vp27smpx_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) |
88 | { | |
89 | struct vp27smpx_state *state = to_state(sd); | |
ac247433 | 90 | |
d9460f06 HV |
91 | state->radio = 0; |
92 | return 0; | |
93 | } | |
ac247433 | 94 | |
d9460f06 HV |
95 | static int vp27smpx_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) |
96 | { | |
97 | struct vp27smpx_state *state = to_state(sd); | |
ac247433 | 98 | |
d9460f06 HV |
99 | if (!state->radio) |
100 | vp27smpx_set_audmode(sd, vt->audmode); | |
101 | return 0; | |
102 | } | |
ac247433 | 103 | |
d9460f06 HV |
104 | static int vp27smpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) |
105 | { | |
106 | struct vp27smpx_state *state = to_state(sd); | |
107 | ||
108 | if (state->radio) | |
109 | return 0; | |
110 | vt->audmode = state->audmode; | |
111 | vt->capability = V4L2_TUNER_CAP_STEREO | | |
112 | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; | |
113 | vt->rxsubchans = V4L2_TUNER_SUB_MONO; | |
114 | return 0; | |
115 | } | |
ac247433 | 116 | |
aecde8b5 | 117 | static int vp27smpx_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) |
d9460f06 HV |
118 | { |
119 | struct i2c_client *client = v4l2_get_subdevdata(sd); | |
120 | ||
121 | return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VP27SMPX, 0); | |
122 | } | |
123 | ||
124 | static int vp27smpx_log_status(struct v4l2_subdev *sd) | |
125 | { | |
126 | struct vp27smpx_state *state = to_state(sd); | |
127 | ||
128 | v4l2_info(sd, "Audio Mode: %u%s\n", state->audmode, | |
129 | state->radio ? " (Radio)" : ""); | |
ac247433 HV |
130 | return 0; |
131 | } | |
132 | ||
d9460f06 HV |
133 | /* ----------------------------------------------------------------------- */ |
134 | ||
135 | static const struct v4l2_subdev_core_ops vp27smpx_core_ops = { | |
136 | .log_status = vp27smpx_log_status, | |
137 | .g_chip_ident = vp27smpx_g_chip_ident, | |
f41737ec | 138 | .s_std = vp27smpx_s_std, |
d9460f06 HV |
139 | }; |
140 | ||
141 | static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = { | |
142 | .s_radio = vp27smpx_s_radio, | |
d9460f06 HV |
143 | .s_tuner = vp27smpx_s_tuner, |
144 | .g_tuner = vp27smpx_g_tuner, | |
145 | }; | |
146 | ||
147 | static const struct v4l2_subdev_ops vp27smpx_ops = { | |
148 | .core = &vp27smpx_core_ops, | |
149 | .tuner = &vp27smpx_tuner_ops, | |
150 | }; | |
151 | ||
ac247433 HV |
152 | /* ----------------------------------------------------------------------- */ |
153 | ||
154 | /* i2c implementation */ | |
155 | ||
156 | /* | |
157 | * Generic i2c probe | |
158 | * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' | |
159 | */ | |
160 | ||
d2653e92 JD |
161 | static int vp27smpx_probe(struct i2c_client *client, |
162 | const struct i2c_device_id *id) | |
ac247433 | 163 | { |
ac247433 | 164 | struct vp27smpx_state *state; |
d9460f06 | 165 | struct v4l2_subdev *sd; |
ac247433 HV |
166 | |
167 | /* Check if the adapter supports the needed features */ | |
45eea276 | 168 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) |
188f3457 | 169 | return -EIO; |
ac247433 | 170 | |
35df38c0 HV |
171 | v4l_info(client, "chip found @ 0x%x (%s)\n", |
172 | client->addr << 1, client->adapter->name); | |
ac247433 HV |
173 | |
174 | state = kzalloc(sizeof(struct vp27smpx_state), GFP_KERNEL); | |
35df38c0 | 175 | if (state == NULL) |
ac247433 | 176 | return -ENOMEM; |
d9460f06 HV |
177 | sd = &state->sd; |
178 | v4l2_i2c_subdev_init(sd, client, &vp27smpx_ops); | |
ac247433 | 179 | state->audmode = V4L2_TUNER_MODE_STEREO; |
ac247433 HV |
180 | |
181 | /* initialize vp27smpx */ | |
d9460f06 | 182 | vp27smpx_set_audmode(sd, state->audmode); |
ac247433 HV |
183 | return 0; |
184 | } | |
185 | ||
45eea276 | 186 | static int vp27smpx_remove(struct i2c_client *client) |
ac247433 | 187 | { |
d9460f06 HV |
188 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
189 | ||
190 | v4l2_device_unregister_subdev(sd); | |
191 | kfree(to_state(sd)); | |
ac247433 HV |
192 | return 0; |
193 | } | |
194 | ||
195 | /* ----------------------------------------------------------------------- */ | |
196 | ||
af294867 JD |
197 | static const struct i2c_device_id vp27smpx_id[] = { |
198 | { "vp27smpx", 0 }, | |
199 | { } | |
200 | }; | |
201 | MODULE_DEVICE_TABLE(i2c, vp27smpx_id); | |
202 | ||
45eea276 HV |
203 | static struct v4l2_i2c_driver_data v4l2_i2c_data = { |
204 | .name = "vp27smpx", | |
45eea276 HV |
205 | .probe = vp27smpx_probe, |
206 | .remove = vp27smpx_remove, | |
af294867 | 207 | .id_table = vp27smpx_id, |
ac247433 | 208 | }; |