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