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