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> | |
33b687cf | 30 | #include <linux/videodev2.h> |
d9460f06 | 31 | #include <media/v4l2-device.h> |
ac247433 HV |
32 | #include <media/v4l2-chip-ident.h> |
33 | ||
34 | MODULE_DESCRIPTION("vp27smpx driver"); | |
35 | MODULE_AUTHOR("Hans Verkuil"); | |
36 | MODULE_LICENSE("GPL"); | |
37 | ||
ac247433 HV |
38 | |
39 | /* ----------------------------------------------------------------------- */ | |
40 | ||
41 | struct vp27smpx_state { | |
d9460f06 | 42 | struct v4l2_subdev sd; |
ac247433 HV |
43 | int radio; |
44 | u32 audmode; | |
45 | }; | |
46 | ||
d9460f06 | 47 | static inline struct vp27smpx_state *to_state(struct v4l2_subdev *sd) |
ac247433 | 48 | { |
d9460f06 HV |
49 | return container_of(sd, struct vp27smpx_state, sd); |
50 | } | |
51 | ||
52 | static void vp27smpx_set_audmode(struct v4l2_subdev *sd, u32 audmode) | |
53 | { | |
54 | struct vp27smpx_state *state = to_state(sd); | |
55 | struct i2c_client *client = v4l2_get_subdevdata(sd); | |
ac247433 HV |
56 | u8 data[3] = { 0x00, 0x00, 0x04 }; |
57 | ||
58 | switch (audmode) { | |
35df38c0 HV |
59 | case V4L2_TUNER_MODE_MONO: |
60 | case V4L2_TUNER_MODE_LANG1: | |
61 | break; | |
62 | case V4L2_TUNER_MODE_STEREO: | |
63 | case V4L2_TUNER_MODE_LANG1_LANG2: | |
64 | data[1] = 0x01; | |
65 | break; | |
66 | case V4L2_TUNER_MODE_LANG2: | |
67 | data[1] = 0x02; | |
68 | break; | |
ac247433 HV |
69 | } |
70 | ||
35df38c0 | 71 | if (i2c_master_send(client, data, sizeof(data)) != sizeof(data)) |
d9460f06 | 72 | v4l2_err(sd, "I/O error setting audmode\n"); |
35df38c0 | 73 | else |
ac247433 | 74 | state->audmode = audmode; |
ac247433 HV |
75 | } |
76 | ||
d9460f06 | 77 | static int vp27smpx_s_radio(struct v4l2_subdev *sd) |
ac247433 | 78 | { |
d9460f06 | 79 | struct vp27smpx_state *state = to_state(sd); |
ac247433 | 80 | |
d9460f06 HV |
81 | state->radio = 1; |
82 | return 0; | |
83 | } | |
ac247433 | 84 | |
d9460f06 HV |
85 | static int vp27smpx_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) |
86 | { | |
87 | struct vp27smpx_state *state = to_state(sd); | |
ac247433 | 88 | |
d9460f06 HV |
89 | state->radio = 0; |
90 | return 0; | |
91 | } | |
ac247433 | 92 | |
d9460f06 HV |
93 | static int vp27smpx_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) |
94 | { | |
95 | struct vp27smpx_state *state = to_state(sd); | |
ac247433 | 96 | |
d9460f06 HV |
97 | if (!state->radio) |
98 | vp27smpx_set_audmode(sd, vt->audmode); | |
99 | return 0; | |
100 | } | |
ac247433 | 101 | |
d9460f06 HV |
102 | static int vp27smpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) |
103 | { | |
104 | struct vp27smpx_state *state = to_state(sd); | |
105 | ||
106 | if (state->radio) | |
107 | return 0; | |
108 | vt->audmode = state->audmode; | |
109 | vt->capability = V4L2_TUNER_CAP_STEREO | | |
110 | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; | |
111 | vt->rxsubchans = V4L2_TUNER_SUB_MONO; | |
112 | return 0; | |
113 | } | |
ac247433 | 114 | |
aecde8b5 | 115 | static int vp27smpx_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) |
d9460f06 HV |
116 | { |
117 | struct i2c_client *client = v4l2_get_subdevdata(sd); | |
118 | ||
119 | return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VP27SMPX, 0); | |
120 | } | |
121 | ||
122 | static int vp27smpx_log_status(struct v4l2_subdev *sd) | |
123 | { | |
124 | struct vp27smpx_state *state = to_state(sd); | |
125 | ||
126 | v4l2_info(sd, "Audio Mode: %u%s\n", state->audmode, | |
127 | state->radio ? " (Radio)" : ""); | |
ac247433 HV |
128 | return 0; |
129 | } | |
130 | ||
d9460f06 HV |
131 | /* ----------------------------------------------------------------------- */ |
132 | ||
133 | static const struct v4l2_subdev_core_ops vp27smpx_core_ops = { | |
134 | .log_status = vp27smpx_log_status, | |
135 | .g_chip_ident = vp27smpx_g_chip_ident, | |
f41737ec | 136 | .s_std = vp27smpx_s_std, |
d9460f06 HV |
137 | }; |
138 | ||
139 | static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = { | |
140 | .s_radio = vp27smpx_s_radio, | |
d9460f06 HV |
141 | .s_tuner = vp27smpx_s_tuner, |
142 | .g_tuner = vp27smpx_g_tuner, | |
143 | }; | |
144 | ||
145 | static const struct v4l2_subdev_ops vp27smpx_ops = { | |
146 | .core = &vp27smpx_core_ops, | |
147 | .tuner = &vp27smpx_tuner_ops, | |
148 | }; | |
149 | ||
ac247433 HV |
150 | /* ----------------------------------------------------------------------- */ |
151 | ||
152 | /* i2c implementation */ | |
153 | ||
154 | /* | |
155 | * Generic i2c probe | |
156 | * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' | |
157 | */ | |
158 | ||
d2653e92 JD |
159 | static int vp27smpx_probe(struct i2c_client *client, |
160 | const struct i2c_device_id *id) | |
ac247433 | 161 | { |
ac247433 | 162 | struct vp27smpx_state *state; |
d9460f06 | 163 | struct v4l2_subdev *sd; |
ac247433 HV |
164 | |
165 | /* Check if the adapter supports the needed features */ | |
45eea276 | 166 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) |
188f3457 | 167 | return -EIO; |
ac247433 | 168 | |
35df38c0 HV |
169 | v4l_info(client, "chip found @ 0x%x (%s)\n", |
170 | client->addr << 1, client->adapter->name); | |
ac247433 HV |
171 | |
172 | state = kzalloc(sizeof(struct vp27smpx_state), GFP_KERNEL); | |
35df38c0 | 173 | if (state == NULL) |
ac247433 | 174 | return -ENOMEM; |
d9460f06 HV |
175 | sd = &state->sd; |
176 | v4l2_i2c_subdev_init(sd, client, &vp27smpx_ops); | |
ac247433 | 177 | state->audmode = V4L2_TUNER_MODE_STEREO; |
ac247433 HV |
178 | |
179 | /* initialize vp27smpx */ | |
d9460f06 | 180 | vp27smpx_set_audmode(sd, state->audmode); |
ac247433 HV |
181 | return 0; |
182 | } | |
183 | ||
45eea276 | 184 | static int vp27smpx_remove(struct i2c_client *client) |
ac247433 | 185 | { |
d9460f06 HV |
186 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
187 | ||
188 | v4l2_device_unregister_subdev(sd); | |
189 | kfree(to_state(sd)); | |
ac247433 HV |
190 | return 0; |
191 | } | |
192 | ||
193 | /* ----------------------------------------------------------------------- */ | |
194 | ||
af294867 JD |
195 | static const struct i2c_device_id vp27smpx_id[] = { |
196 | { "vp27smpx", 0 }, | |
197 | { } | |
198 | }; | |
199 | MODULE_DEVICE_TABLE(i2c, vp27smpx_id); | |
200 | ||
1f352667 HV |
201 | static struct i2c_driver vp27smpx_driver = { |
202 | .driver = { | |
203 | .owner = THIS_MODULE, | |
204 | .name = "vp27smpx", | |
205 | }, | |
206 | .probe = vp27smpx_probe, | |
207 | .remove = vp27smpx_remove, | |
208 | .id_table = vp27smpx_id, | |
ac247433 | 209 | }; |
1f352667 | 210 | |
c6e8d86f | 211 | module_i2c_driver(vp27smpx_driver); |