1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
2 | /* |
3 | tuner-i2c.h - i2c interface for different tuners |
4 | |
5 | Copyright (C) 2007 Michael Krufky (mkrufky@linuxtv.org) |
6 | |
7 | */ |
8 | |
9 | #ifndef __TUNER_I2C_H__ |
10 | #define __TUNER_I2C_H__ |
11 | |
12 | #include <linux/i2c.h> |
13 | #include <linux/slab.h> |
14 | |
15 | struct tuner_i2c_props { |
16 | u8 addr; |
17 | struct i2c_adapter *adap; |
18 | |
19 | /* used for tuner instance management */ |
20 | int count; |
21 | char *name; |
22 | }; |
23 | |
24 | static inline int tuner_i2c_xfer_send(struct tuner_i2c_props *props, |
25 | unsigned char *buf, int len) |
26 | { |
27 | struct i2c_msg msg = { .addr = props->addr, .flags = 0, |
28 | .buf = buf, .len = len }; |
29 | int ret = i2c_transfer(adap: props->adap, msgs: &msg, num: 1); |
30 | |
31 | return (ret == 1) ? len : ret; |
32 | } |
33 | |
34 | static inline int tuner_i2c_xfer_recv(struct tuner_i2c_props *props, |
35 | unsigned char *buf, int len) |
36 | { |
37 | struct i2c_msg msg = { .addr = props->addr, .flags = I2C_M_RD, |
38 | .buf = buf, .len = len }; |
39 | int ret = i2c_transfer(adap: props->adap, msgs: &msg, num: 1); |
40 | |
41 | return (ret == 1) ? len : ret; |
42 | } |
43 | |
44 | static inline int tuner_i2c_xfer_send_recv(struct tuner_i2c_props *props, |
45 | unsigned char *obuf, int olen, |
46 | unsigned char *ibuf, int ilen) |
47 | { |
48 | struct i2c_msg msg[2] = { { .addr = props->addr, .flags = 0, |
49 | .buf = obuf, .len = olen }, |
50 | { .addr = props->addr, .flags = I2C_M_RD, |
51 | .buf = ibuf, .len = ilen } }; |
52 | int ret = i2c_transfer(adap: props->adap, msgs: msg, num: 2); |
53 | |
54 | return (ret == 2) ? ilen : ret; |
55 | } |
56 | |
57 | /* Callers must declare as a global for the module: |
58 | * |
59 | * static LIST_HEAD(hybrid_tuner_instance_list); |
60 | * |
61 | * hybrid_tuner_instance_list should be the third argument |
62 | * passed into hybrid_tuner_request_state(). |
63 | * |
64 | * state structure must contain the following: |
65 | * |
66 | * struct list_head hybrid_tuner_instance_list; |
67 | * struct tuner_i2c_props i2c_props; |
68 | * |
69 | * hybrid_tuner_instance_list (both within state structure and globally) |
70 | * is only required if the driver is using hybrid_tuner_request_state |
71 | * and hybrid_tuner_release_state to manage state sharing between |
72 | * multiple instances of hybrid tuners. |
73 | */ |
74 | |
75 | #define tuner_printk(kernlvl, i2cprops, fmt, arg...) do { \ |
76 | printk(kernlvl "%s %d-%04x: " fmt, i2cprops.name, \ |
77 | i2cprops.adap ? \ |
78 | i2c_adapter_id(i2cprops.adap) : -1, \ |
79 | i2cprops.addr, ##arg); \ |
80 | } while (0) |
81 | |
82 | /* TO DO: convert all callers of these macros to pass in |
83 | * struct tuner_i2c_props, then remove the macro wrappers */ |
84 | |
85 | #define __tuner_warn(i2cprops, fmt, arg...) do { \ |
86 | tuner_printk(KERN_WARNING, i2cprops, fmt, ##arg); \ |
87 | } while (0) |
88 | |
89 | #define __tuner_info(i2cprops, fmt, arg...) do { \ |
90 | tuner_printk(KERN_INFO, i2cprops, fmt, ##arg); \ |
91 | } while (0) |
92 | |
93 | #define __tuner_err(i2cprops, fmt, arg...) do { \ |
94 | tuner_printk(KERN_ERR, i2cprops, fmt, ##arg); \ |
95 | } while (0) |
96 | |
97 | #define __tuner_dbg(i2cprops, fmt, arg...) do { \ |
98 | if ((debug)) \ |
99 | tuner_printk(KERN_DEBUG, i2cprops, fmt, ##arg); \ |
100 | } while (0) |
101 | |
102 | #define tuner_warn(fmt, arg...) __tuner_warn(priv->i2c_props, fmt, ##arg) |
103 | #define tuner_info(fmt, arg...) __tuner_info(priv->i2c_props, fmt, ##arg) |
104 | #define tuner_err(fmt, arg...) __tuner_err(priv->i2c_props, fmt, ##arg) |
105 | #define tuner_dbg(fmt, arg...) __tuner_dbg(priv->i2c_props, fmt, ##arg) |
106 | |
107 | /****************************************************************************/ |
108 | |
109 | /* The return value of hybrid_tuner_request_state indicates the number of |
110 | * instances using this tuner object. |
111 | * |
112 | * 0 - no instances, indicates an error - kzalloc must have failed |
113 | * |
114 | * 1 - one instance, indicates that the tuner object was created successfully |
115 | * |
116 | * 2 (or more) instances, indicates that an existing tuner object was found |
117 | */ |
118 | |
119 | #define hybrid_tuner_request_state(type, state, list, i2cadap, i2caddr, devname)\ |
120 | ({ \ |
121 | int __ret = 0; \ |
122 | list_for_each_entry(state, &list, hybrid_tuner_instance_list) { \ |
123 | if (((i2cadap) && (state->i2c_props.adap)) && \ |
124 | ((i2c_adapter_id(state->i2c_props.adap) == \ |
125 | i2c_adapter_id(i2cadap)) && \ |
126 | (i2caddr == state->i2c_props.addr))) { \ |
127 | __tuner_info(state->i2c_props, \ |
128 | "attaching existing instance\n"); \ |
129 | state->i2c_props.count++; \ |
130 | __ret = state->i2c_props.count; \ |
131 | break; \ |
132 | } \ |
133 | } \ |
134 | if (0 == __ret) { \ |
135 | state = kzalloc(sizeof(type), GFP_KERNEL); \ |
136 | if (!state) { \ |
137 | __ret = -ENOMEM; \ |
138 | goto __fail; \ |
139 | } \ |
140 | state->i2c_props.addr = i2caddr; \ |
141 | state->i2c_props.adap = i2cadap; \ |
142 | state->i2c_props.name = devname; \ |
143 | __tuner_info(state->i2c_props, \ |
144 | "creating new instance\n"); \ |
145 | list_add_tail(&state->hybrid_tuner_instance_list, &list);\ |
146 | state->i2c_props.count++; \ |
147 | __ret = state->i2c_props.count; \ |
148 | } \ |
149 | __fail: \ |
150 | __ret; \ |
151 | }) |
152 | |
153 | #define hybrid_tuner_release_state(state) \ |
154 | ({ \ |
155 | int __ret; \ |
156 | state->i2c_props.count--; \ |
157 | __ret = state->i2c_props.count; \ |
158 | if (!state->i2c_props.count) { \ |
159 | __tuner_info(state->i2c_props, "destroying instance\n");\ |
160 | list_del(&state->hybrid_tuner_instance_list); \ |
161 | kfree(state); \ |
162 | } \ |
163 | __ret; \ |
164 | }) |
165 | |
166 | #define hybrid_tuner_report_instance_count(state) \ |
167 | ({ \ |
168 | int __ret = 0; \ |
169 | if (state) \ |
170 | __ret = state->i2c_props.count; \ |
171 | __ret; \ |
172 | }) |
173 | |
174 | #endif /* __TUNER_I2C_H__ */ |
175 | |