• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * linux-5.4/drivers/media/platform/sunxi-vin/vin-cci/sunxi_cci.c
3  *
4  * Copyright (c) 2007-2017 Allwinnertech Co., Ltd.
5  *
6  * This software is licensed under the terms of the GNU General Public
7  * License version 2, as published by the Free Software Foundation, and
8  * may be copied, distributed, and modified under those terms.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  */
16 
17 
18 #include <linux/platform_device.h>
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/slab.h>
22 
23 #include "sunxi_cci.h"
24 #include "../platform/platform_cfg.h"
25 
26 #if defined(CONFIG_CCI_MODULE) || defined(CONFIG_CCI) || defined(CONFIG_CCI_TO_TWI)
27 #define USED_TO_CCI_OR_TWI
28 #endif
29 
30 #ifdef USED_TO_CCI_OR_TWI
31 #include "bsp_cci.h"
32 
33 #define CCI_MODULE_NAME "vin_cci"
34 
35 static LIST_HEAD(cci_drv_list);
36 
cci_irq_handler(int this_irq,void * dev)37 static irqreturn_t cci_irq_handler(int this_irq, void *dev)
38 {
39 	unsigned long flags = 0;
40 	struct cci_dev *cci = (struct cci_dev *)dev;
41 
42 	spin_lock_irqsave(&cci->slock, flags);
43 	bsp_cci_irq_process(cci->id);
44 	spin_unlock_irqrestore(&cci->slock, flags);
45 	return IRQ_HANDLED;
46 }
47 
48 
__cci_clk_get(struct cci_dev * dev)49 static int __cci_clk_get(struct cci_dev *dev)
50 {
51 #ifndef FPGA_VER
52 	struct device_node *np = dev->pdev->dev.of_node;
53 
54 	dev->clock = of_clk_get(np, 0);
55 	if (IS_ERR_OR_NULL(dev->clock))
56 		vin_warn("cci get clk failed!\n");
57 #endif
58 	return 0;
59 }
60 
__cci_clk_enable(struct cci_dev * dev,int enable)61 static int __cci_clk_enable(struct cci_dev *dev, int enable)
62 {
63 #ifndef FPGA_VER
64 	if (!IS_ERR_OR_NULL(dev->clock)) {
65 		if (enable) {
66 			if (clk_prepare_enable(dev->clock)) {
67 				vin_err("cci clk enable error!\n");
68 				return -1;
69 			}
70 		} else {
71 			clk_disable_unprepare(dev->clock);
72 		}
73 	}
74 #endif
75 	return 0;
76 }
77 
__cci_clk_release(struct cci_dev * dev)78 static void __cci_clk_release(struct cci_dev *dev)
79 {
80 #ifndef FPGA_VER
81 	if (dev->clock)
82 		clk_put(dev->clock);
83 #endif
84 }
85 
__cci_pin_config(struct cci_dev * dev,int enable)86 static int __cci_pin_config(struct cci_dev *dev, int enable)
87 {
88 #ifndef FPGA_VER
89 	char pinctrl_names[10] = "";
90 
91 	if (!IS_ERR_OR_NULL(dev->pctrl))
92 		devm_pinctrl_put(dev->pctrl);
93 
94 	if (enable)
95 		strcpy(pinctrl_names, "default");
96 	else
97 		strcpy(pinctrl_names, "sleep");
98 
99 	dev->pctrl = devm_pinctrl_get_select(&dev->pdev->dev, pinctrl_names);
100 	if (IS_ERR_OR_NULL(dev->pctrl)) {
101 		vin_err("cci%d request pinctrl handle failed!\n", dev->id);
102 		return -EINVAL;
103 	}
104 	usleep_range(1000, 1200);
105 #endif
106 	return 0;
107 }
108 
__cci_pin_release(struct cci_dev * dev)109 static int __cci_pin_release(struct cci_dev *dev)
110 {
111 #ifndef FPGA_VER
112 	if (!IS_ERR_OR_NULL(dev->pctrl))
113 		devm_pinctrl_put(dev->pctrl);
114 #endif
115 	return 0;
116 }
117 
cci_dev_get(int id)118 static struct cci_dev *cci_dev_get(int id)
119 {
120 	struct cci_dev *cci;
121 
122 	list_for_each_entry(cci, &cci_drv_list, cci_list) {
123 		if (cci->id == id)
124 			return cci;
125 	}
126 	return NULL;
127 }
128 
cci_s_power(unsigned int sel,int on_off)129 void cci_s_power(unsigned int sel, int on_off)
130 {
131 	struct cci_dev *cci = cci_dev_get(sel);
132 
133 	if (cci == NULL) {
134 		vin_err("cci is NULL!\n");
135 		return;
136 	}
137 	vin_log(VIN_LOG_CCI, "%s, %d!\n", __func__, on_off);
138 
139 	if (on_off && (cci->use_cnt)++ > 0)
140 		return;
141 	else if (!on_off && (cci->use_cnt == 0 || --(cci->use_cnt) > 0))
142 		return;
143 
144 	__cci_pin_config(cci, on_off);
145 
146 	if (on_off) {
147 		__cci_clk_enable(cci, 1);
148 		bsp_csi_cci_init_helper(sel);
149 	} else {
150 		bsp_csi_cci_exit(sel);
151 		__cci_clk_enable(cci, 0);
152 	}
153 }
154 
155 #if defined(CONFIG_CCI_TO_TWI)
156 static int
sunxi_i2c_xfer(struct i2c_adapter * adap,struct i2c_msg * msgs,int num)157 sunxi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
158 {
159 	struct cci_dev *cci = (struct cci_dev *)adap->algo_data;
160 	struct cci_msg cci_msg;
161 	unsigned char *buf;
162 	int i;
163 	if (num == 1) {                          /*write*/
164 		cci_msg.bus_fmt.saddr_7bit = msgs->addr;
165 		cci_msg.bus_fmt.wr_flag = 0;
166 		cci_msg.bus_fmt.rs_start = START_WITH_ID_W;
167 		cci_msg.bus_fmt.rs_mode = STOP_START;
168 		if (msgs->len == 0) {
169 			cci_msg.bus_fmt.addr_len = 0;
170 			cci_msg.bus_fmt.data_len = 0;
171 		} else {
172 			cci_msg.bus_fmt.addr_len = 1;
173 			cci_msg.bus_fmt.data_len = msgs->len - 1;
174 		}
175 		cci_msg.pkt_buf = msgs->buf;
176 		cci_msg.pkt_num = 1;
177 
178 		bsp_cci_tx_start_wait_done(cci->id, &cci_msg);
179 		return 1;
180 	} else if (num == 2) {			/*read*/
181 		buf = kzalloc(1 + msgs[1].len, GFP_KERNEL);
182 		buf[0] = msgs[0].buf[0];
183 		cci_msg.bus_fmt.saddr_7bit = msgs[0].addr;
184 		cci_msg.bus_fmt.wr_flag = 1;
185 		cci_msg.bus_fmt.rs_start = START_WITH_ID_W;
186 		cci_msg.bus_fmt.rs_mode = STOP_START;
187 		cci_msg.bus_fmt.addr_len = 1;
188 		cci_msg.bus_fmt.data_len = msgs[1].len;
189 		cci_msg.pkt_buf = buf;
190 		cci_msg.pkt_num = 1;
191 
192 		bsp_cci_tx_start_wait_done(cci->id, &cci_msg);
193 		for (i = 0; i < cci_msg.bus_fmt.data_len; i++)
194 			msgs[1].buf[i] = buf[i+1];
195 		kfree(buf);
196 		return i;
197 	}
198 }
199 
sunxi_i2c_functionality(struct i2c_adapter * adap)200 static unsigned int sunxi_i2c_functionality(struct i2c_adapter *adap)
201 {
202 	return I2C_FUNC_I2C|I2C_FUNC_10BIT_ADDR|I2C_FUNC_SMBUS_EMUL;
203 };
204 
205 static const struct i2c_algorithm sunxi_i2c_algorithm = {
206 	.master_xfer	  = sunxi_i2c_xfer,
207 	.functionality	  = sunxi_i2c_functionality,
208 };
209 #endif
210 
cci_probe(struct platform_device * pdev)211 static int cci_probe(struct platform_device *pdev)
212 {
213 	struct device_node *np = pdev->dev.of_node;
214 	struct cci_dev *cci = NULL;
215 	int ret, irq = 0;
216 
217 	if (np == NULL) {
218 		vin_err("CCI failed to get of node\n");
219 		return -ENODEV;
220 	}
221 	cci = kzalloc(sizeof(struct cci_dev), GFP_KERNEL);
222 	if (!cci) {
223 		ret = -ENOMEM;
224 		goto ekzalloc;
225 	}
226 
227 	of_property_read_u32(np, "device_id", &pdev->id);
228 	if (pdev->id < 0) {
229 		vin_err("CCI failed to get device id\n");
230 		ret = -EINVAL;
231 		goto freedev;
232 	}
233 	cci->id = pdev->id;
234 	cci->pdev = pdev;
235 
236 	irq = irq_of_parse_and_map(np, 0);
237 	if (irq <= 0) {
238 		vin_err("[CCI%d] failed to get irq\n", pdev->id);
239 		ret = -EINVAL;
240 		goto freedev;
241 	}
242 	cci->base = of_iomap(np, 0);
243 	if (!cci->base) {
244 		ret = -EIO;
245 		goto freedev;
246 	}
247 	cci->irq = irq;
248 	spin_lock_init(&cci->slock);
249 
250 	list_add_tail(&cci->cci_list, &cci_drv_list);
251 	init_waitqueue_head(&cci->wait);
252 
253 #if defined(CONFIG_CCI_TO_TWI)
254 	cci->adap.owner   = THIS_MODULE;
255 	cci->adap.nr      = cci->id + 4;
256 	cci->adap.retries = 3;
257 	cci->adap.timeout = 5*HZ;
258 	cci->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
259 	snprintf(cci->adap.name, sizeof(cci->adap.name),
260 				"twi""%u", cci->adap.nr);
261 	pdev->dev.init_name = cci->adap.name;
262 
263 	cci->adap.algo = &sunxi_i2c_algorithm;
264 #endif
265 	ret = request_irq(irq, cci_irq_handler,
266 				IRQF_SHARED, CCI_MODULE_NAME, cci);
267 	if (ret) {
268 		vin_err("[CCI%d] requeset irq failed!\n", cci->id);
269 		goto unmap;
270 	}
271 #if defined(CONFIG_CCI_TO_TWI)
272 	cci->adap.algo_data  = cci;
273 	cci->adap.dev.parent = &pdev->dev;
274 	cci->adap.dev.of_node = pdev->dev.of_node;
275 
276 	ret = i2c_add_numbered_adapter(&cci->adap);
277 	if (ret < 0) {
278 		vin_err("[i2c%d] failed to add adapter\n", cci->id);
279 	}
280 #endif
281 
282 	ret = bsp_csi_cci_set_base_addr(cci->id, (unsigned long)cci->base);
283 	if (ret < 0)
284 		goto freeirq;
285 
286 	if (__cci_clk_get(cci)) {
287 		vin_err("cci clock get failed!\n");
288 		goto freeirq;
289 	}
290 #if defined(CONFIG_CCI_TO_TWI)
291 	cci_s_power(cci->id, 1);
292 #endif
293 	platform_set_drvdata(pdev, cci);
294 	vin_log(VIN_LOG_CCI, "cci probe end cci_sel = %d!\n", cci->id);
295 
296 	return 0;
297 freeirq:
298 	free_irq(irq, cci);
299 unmap:
300 	iounmap(cci->base);
301 freedev:
302 	kfree(cci);
303 ekzalloc:
304 	vin_err("cci probe err!\n");
305 	return ret;
306 }
307 
cci_remove(struct platform_device * pdev)308 static int cci_remove(struct platform_device *pdev)
309 {
310 	struct cci_dev *cci = platform_get_drvdata(pdev);
311 
312 	platform_set_drvdata(pdev, NULL);
313 
314 	__cci_pin_release(cci);
315 	__cci_clk_release(cci);
316 	free_irq(cci->irq, cci);
317 	if (cci->base)
318 		iounmap(cci->base);
319 	list_del(&cci->cci_list);
320 	kfree(cci);
321 	return 0;
322 }
323 
324 static const struct of_device_id sunxi_cci_match[] = {
325 	{.compatible = "allwinner,sunxi-csi_cci",},
326 	{},
327 };
328 
329 MODULE_DEVICE_TABLE(of, sunxi_cci_match);
330 
331 static struct platform_driver cci_platform_driver = {
332 	.probe = cci_probe,
333 	.remove = cci_remove,
334 	.driver = {
335 		   .name = CCI_MODULE_NAME,
336 		   .owner = THIS_MODULE,
337 		   .of_match_table = sunxi_cci_match,
338 		   },
339 };
340 
341 #endif
342 
sunxi_cci_platform_register(void)343 int sunxi_cci_platform_register(void)
344 {
345 #ifdef USED_TO_CCI_OR_TWI
346 	int ret;
347 
348 	ret = platform_driver_register(&cci_platform_driver);
349 	if (ret) {
350 		vin_err("platform driver register failed\n");
351 		return ret;
352 	}
353 	vin_log(VIN_LOG_CCI, "cci_init end\n");
354 #endif
355 	return 0;
356 }
357 
sunxi_cci_platform_unregister(void)358 void sunxi_cci_platform_unregister(void)
359 {
360 #ifdef USED_TO_CCI_OR_TWI
361 	platform_driver_unregister(&cci_platform_driver);
362 	vin_log(VIN_LOG_CCI, "cci_exit end\n");
363 #endif
364 }
365 
366