1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Cirrus Logic cs3308 8-Channel Analog Volume Control |
4 | * |
5 | * Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com> |
6 | * Copyright (C) 2012 Steven Toth <stoth@kernellabs.com> |
7 | * |
8 | * Derived from cs5345.c Copyright (C) 2007 Hans Verkuil |
9 | */ |
10 | |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/i2c.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/videodev2.h> |
17 | #include <media/v4l2-device.h> |
18 | |
19 | MODULE_DESCRIPTION("i2c device driver for cs3308 8-channel volume control" ); |
20 | MODULE_AUTHOR("Devin Heitmueller" ); |
21 | MODULE_LICENSE("GPL" ); |
22 | |
23 | static inline int cs3308_write(struct v4l2_subdev *sd, u8 reg, u8 value) |
24 | { |
25 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
26 | |
27 | return i2c_smbus_write_byte_data(client, command: reg, value); |
28 | } |
29 | |
30 | static inline int cs3308_read(struct v4l2_subdev *sd, u8 reg) |
31 | { |
32 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
33 | |
34 | return i2c_smbus_read_byte_data(client, command: reg); |
35 | } |
36 | |
37 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
38 | static int cs3308_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) |
39 | { |
40 | reg->val = cs3308_read(sd, reg: reg->reg & 0xffff); |
41 | reg->size = 1; |
42 | return 0; |
43 | } |
44 | |
45 | static int cs3308_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) |
46 | { |
47 | cs3308_write(sd, reg: reg->reg & 0xffff, value: reg->val & 0xff); |
48 | return 0; |
49 | } |
50 | #endif |
51 | |
52 | /* ----------------------------------------------------------------------- */ |
53 | |
54 | static const struct v4l2_subdev_core_ops cs3308_core_ops = { |
55 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
56 | .g_register = cs3308_g_register, |
57 | .s_register = cs3308_s_register, |
58 | #endif |
59 | }; |
60 | |
61 | static const struct v4l2_subdev_ops cs3308_ops = { |
62 | .core = &cs3308_core_ops, |
63 | }; |
64 | |
65 | /* ----------------------------------------------------------------------- */ |
66 | |
67 | static int cs3308_probe(struct i2c_client *client) |
68 | { |
69 | struct v4l2_subdev *sd; |
70 | unsigned i; |
71 | |
72 | /* Check if the adapter supports the needed features */ |
73 | if (!i2c_check_functionality(adap: client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) |
74 | return -EIO; |
75 | |
76 | if ((i2c_smbus_read_byte_data(client, command: 0x1c) & 0xf0) != 0xe0) |
77 | return -ENODEV; |
78 | |
79 | v4l_info(client, "chip found @ 0x%x (%s)\n" , |
80 | client->addr << 1, client->adapter->name); |
81 | |
82 | sd = kzalloc(size: sizeof(struct v4l2_subdev), GFP_KERNEL); |
83 | if (sd == NULL) |
84 | return -ENOMEM; |
85 | |
86 | v4l2_i2c_subdev_init(sd, client, ops: &cs3308_ops); |
87 | |
88 | /* Set some reasonable defaults */ |
89 | cs3308_write(sd, reg: 0x0d, value: 0x00); /* Power up all channels */ |
90 | cs3308_write(sd, reg: 0x0e, value: 0x00); /* Master Power */ |
91 | cs3308_write(sd, reg: 0x0b, value: 0x00); /* Device Configuration */ |
92 | /* Set volume for each channel */ |
93 | for (i = 1; i <= 8; i++) |
94 | cs3308_write(sd, reg: i, value: 0xd2); |
95 | cs3308_write(sd, reg: 0x0a, value: 0x00); /* Unmute all channels */ |
96 | return 0; |
97 | } |
98 | |
99 | /* ----------------------------------------------------------------------- */ |
100 | |
101 | static void cs3308_remove(struct i2c_client *client) |
102 | { |
103 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
104 | |
105 | v4l2_device_unregister_subdev(sd); |
106 | kfree(objp: sd); |
107 | } |
108 | |
109 | /* ----------------------------------------------------------------------- */ |
110 | |
111 | static const struct i2c_device_id cs3308_id[] = { |
112 | { "cs3308" , 0 }, |
113 | { } |
114 | }; |
115 | MODULE_DEVICE_TABLE(i2c, cs3308_id); |
116 | |
117 | static struct i2c_driver cs3308_driver = { |
118 | .driver = { |
119 | .name = "cs3308" , |
120 | }, |
121 | .probe = cs3308_probe, |
122 | .remove = cs3308_remove, |
123 | .id_table = cs3308_id, |
124 | }; |
125 | |
126 | module_i2c_driver(cs3308_driver); |
127 | |