1 // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
2 /*
3 * Copyright (c) 2022 Rockchip Electronics Co., Ltd
4 *
5 * author:
6 * Yandong Lin, yandong.lin@rock-chips.com
7 */
8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9
10 #include <linux/delay.h>
11 #include <linux/module.h>
12 #include <linux/of_platform.h>
13 #include <linux/proc_fs.h>
14 #include <linux/pm_runtime.h>
15 #include <linux/regmap.h>
16 #include <linux/interrupt.h>
17 #include <soc/rockchip/rockchip_dvbm.h>
18
19 #include "rockchip_dvbm.h"
20
21 #define RK_DVBM "rk_dvbm"
22
23 unsigned int dvbm_debug;
24 module_param(dvbm_debug, uint, 0644);
25 MODULE_PARM_DESC(dvbm_debug, "bit switch for dvbm debug information");
26
27 static struct dvbm_ctx *g_ctx;
28
29 #define DVBM_DEBUG 0x00000001
30 #define DVBM_DEBUG_IRQ 0x00000002
31 #define DVBM_DEBUG_REG 0x00000004
32 #define DVBM_DEBUG_DUMP 0x00000008
33 #define DVBM_DEBUG_FRM 0x00000010
34
35 #define dvbm_debug(fmt, args...) \
36 do { \
37 if (unlikely(dvbm_debug & (DVBM_DEBUG))) \
38 pr_info(fmt, ##args); \
39 } while (0)
40
41 #define dvbm_debug_reg(fmt, args...) \
42 do { \
43 if (unlikely(dvbm_debug & (DVBM_DEBUG_REG))) \
44 pr_info(fmt, ##args); \
45 } while (0)
46
47 #define dvbm_debug_irq(fmt, args...) \
48 do { \
49 if (unlikely(dvbm_debug & (DVBM_DEBUG_IRQ))) \
50 pr_info(fmt, ##args); \
51 } while (0)
52
53 #define dvbm_debug_dump(fmt, args...) \
54 do { \
55 if (unlikely(dvbm_debug & (DVBM_DEBUG_DUMP))) \
56 pr_info(fmt, ##args); \
57 } while (0)
58
59 #define dvbm_debug_frm(fmt, args...) \
60 do { \
61 if (unlikely(dvbm_debug & (DVBM_DEBUG_FRM))) \
62 pr_info(fmt, ##args); \
63 } while (0)
64
65 #define dvbm_err(fmt, args...) \
66 pr_err(fmt, ##args)
67
68 enum dvbm_flow {
69 ISP_CFG = 1,
70 ISP_CONNECT = 2,
71 VEPU_CFG = 3,
72 VEPU_CONNECT = 4,
73 };
74 /* dvbm status reg bit value define */
75 #define BUF_OVERFLOW BIT(0)
76 #define RESYNC_FINISH BIT(1)
77 #define ISP_CNCT_TIMEOUT BIT(2)
78 #define VEPU_CNCT_TIMEOUT BIT(3)
79 #define VEPU_HANDSHAKE_TIMEOUT BIT(4)
80 #define ISP_CNCT BIT(5)
81 #define ISP_DISCNCT BIT(6)
82 #define VEPU_CNCT BIT(7)
83 #define VEPU_DISCNCT BIT(8)
84
85 /* dvbm reg addr define */
86 #define DVBM_VERSION 0x0
87 #define DVBM_ISP_CNCT 0x4
88 #define DVBM_VEPU_CNCT 0x8
89 /* cfg regs */
90 #define DVBM_CFG 0xC
91 #define DVBM_WDG_CFG0 0x10
92 #define DVBM_WDG_CFG1 0x14
93 #define DVBM_WDG_CFG2 0x18
94 /* interrupt regs */
95 #define DVBM_INT_EN 0x1c
96 #define DVBM_INT_MSK 0x20
97 #define DVBM_INT_CLR 0x24
98 #define DVBM_INT_ST 0x28
99 /* addr regs */
100 #define DVBM_YBUF_BOT 0x2c
101 #define DVBM_YBUF_TOP 0x30
102 #define DVBM_YBUF_SADR 0x34
103 #define DVBM_YBUF_LSTD 0x38
104 #define DVBM_YBUF_FSTD 0x3c
105 #define DVBM_CBUF_BOT 0x40
106 #define DVBM_CBUF_TOP 0x44
107 #define DVBM_CBUF_SADR 0x48
108 #define DVBM_CBUF_LSTD 0x4c
109 #define DVBM_CBUF_FSTD 0x50
110 #define DVBM_AFUL_THDY 0x54
111 #define DVBM_AFUL_THDC 0x58
112 #define DVBM_OVFL_THDY 0x5c
113 #define DVBM_OVFL_THDC 0x60
114 /* status regs */
115 #define DVBM_ST 0x80
116 #define DVBM_OVFL_ST 0x84
117
118 #define DVBM_REG_OFFSET 0x2c
119
120 #define SOFT_DVBM 1
121 #define UPDATE_LINE_CNT 0
122
rk_dvbm_set_reg(struct dvbm_ctx * ctx,u32 offset,u32 val)123 static void rk_dvbm_set_reg(struct dvbm_ctx *ctx, u32 offset, u32 val)
124 {
125 if (!SOFT_DVBM) {
126 dvbm_debug_reg("write reg[%d] 0x%x = 0x%08x\n", offset >> 2, offset, val);
127 writel(val, ctx->reg_base + offset);
128 }
129 }
130
rk_dvbm_read_reg(struct dvbm_ctx * ctx,u32 offset)131 static u32 rk_dvbm_read_reg(struct dvbm_ctx *ctx, u32 offset)
132 {
133 u32 val = 0;
134
135 if (!SOFT_DVBM) {
136 val = readl(ctx->reg_base + offset);
137 dvbm_debug_reg("read reg[%d] 0x%x = 0x%08x\n", offset >> 2, offset, val);
138 }
139 return val;
140 }
141
port_to_ctx(struct dvbm_port * port)142 static struct dvbm_ctx *port_to_ctx(struct dvbm_port *port)
143 {
144 struct dvbm_ctx *ctx = NULL;
145
146 if (IS_ERR_OR_NULL(port))
147 return g_ctx;
148 if (port->dir == DVBM_ISP_PORT)
149 ctx = container_of(port, struct dvbm_ctx, port_isp);
150 else if (port->dir == DVBM_VEPU_PORT)
151 ctx = container_of(port, struct dvbm_ctx, port_vepu);
152
153 return ctx;
154 }
155
dvbm2enc_callback(struct dvbm_ctx * ctx,enum dvbm_cb_event event,void * arg)156 static void dvbm2enc_callback(struct dvbm_ctx *ctx, enum dvbm_cb_event event, void *arg)
157 {
158 struct dvbm_cb *callback = &ctx->vepu_cb;
159 dvbm_callback cb = callback->cb;
160
161 if (!ctx->port_vepu.linked)
162 return;
163 if (cb)
164 cb(callback->ctx, event, arg);
165 }
166
rk_dvbm_dump_regs(struct dvbm_ctx * ctx)167 static void rk_dvbm_dump_regs(struct dvbm_ctx *ctx)
168 {
169 u32 start = ctx->dump_s;//0x80;
170 u32 end = ctx->dump_e;//0xb8;
171 u32 i;
172 dvbm_debug_dump("=== %s ===\n", __func__);
173 for (i = start; i <= end; i += 4)
174 dvbm_debug_dump("reg[0x%0x] = 0x%08x\n", i, readl(ctx->reg_base + i));
175 dvbm_debug_dump("=== %s ===\n", __func__);
176 }
177
rk_dvbm_clk_on(struct dvbm_ctx * ctx)178 static int rk_dvbm_clk_on(struct dvbm_ctx *ctx)
179 {
180 int ret = 0;
181
182 if (ctx->clk)
183 ret = clk_prepare_enable(ctx->clk);
184 if (ret)
185 dev_err(ctx->dev, "clk on failed\n");
186 return ret;
187 }
188
rk_dvbm_clk_off(struct dvbm_ctx * ctx)189 static int rk_dvbm_clk_off(struct dvbm_ctx *ctx)
190 {
191 if (ctx->clk)
192 clk_disable_unprepare(ctx->clk);
193 return 0;
194 }
195
init_isp_infos(struct dvbm_ctx * ctx)196 static void init_isp_infos(struct dvbm_ctx *ctx)
197 {
198 ctx->isp_frm_start = 0;
199 ctx->isp_frm_end = 0;
200 ctx->isp_frm_time = 0;
201 }
202
rk_dvbm_show_time(struct dvbm_ctx * ctx)203 static void rk_dvbm_show_time(struct dvbm_ctx *ctx)
204 {
205 ktime_t time = ktime_get();
206
207 if (ctx->isp_frm_time)
208 dvbm_debug("isp frame start[%d : %d] times %lld us\n",
209 ctx->isp_frm_start, ctx->isp_frm_end,
210 ktime_us_delta(time, ctx->isp_frm_time));
211 ctx->isp_frm_time = time;
212 }
213
rk_dvbm_update_isp_frm_info(struct dvbm_ctx * ctx,u32 line_cnt)214 static void rk_dvbm_update_isp_frm_info(struct dvbm_ctx *ctx, u32 line_cnt)
215 {
216 #if UPDATE_LINE_CNT
217 struct dvbm_isp_frm_info *frm_info = &ctx->isp_frm_info;
218
219 frm_info->line_cnt = ALIGN(line_cnt, 32);
220 dvbm_debug_frm("dvbm frame %d line %d\n", frm_info->frame_cnt, frm_info->line_cnt);
221 dvbm2enc_callback(ctx, DVBM_VEPU_NOTIFY_FRM_INFO, frm_info);
222 #endif
223 }
224
rk_dvbm_setup_iobuf(struct dvbm_ctx * ctx)225 static int rk_dvbm_setup_iobuf(struct dvbm_ctx *ctx)
226 {
227 u32 *data;
228 u32 i;
229 struct rk_dvbm_base *addr_base = &ctx->regs.addr_base;
230 struct dvbm_isp_cfg_t *cfg = &ctx->isp_cfg;
231
232 addr_base->ybuf_bot = cfg->dma_addr + cfg->ybuf_bot;
233 addr_base->ybuf_top = cfg->dma_addr + cfg->ybuf_top;
234 addr_base->ybuf_sadr = cfg->dma_addr + cfg->ybuf_bot;
235 addr_base->ybuf_fstd = cfg->ybuf_fstd;
236 addr_base->ybuf_lstd = cfg->ybuf_lstd;
237
238 addr_base->cbuf_bot = cfg->dma_addr + cfg->cbuf_bot;
239 addr_base->cbuf_top = cfg->dma_addr + cfg->cbuf_top;
240 addr_base->cbuf_sadr = cfg->dma_addr + cfg->cbuf_bot;
241 addr_base->cbuf_fstd = cfg->cbuf_fstd;
242 addr_base->cbuf_lstd = cfg->cbuf_lstd;
243
244 addr_base->aful_thdy = cfg->ybuf_lstd;
245 addr_base->aful_thdc = cfg->ybuf_lstd;
246 addr_base->oful_thdy = cfg->ybuf_lstd;
247 addr_base->oful_thdc = cfg->ybuf_lstd;
248
249 ctx->isp_max_lcnt = cfg->ybuf_fstd / cfg->ybuf_lstd;
250 ctx->wrap_line = (cfg->ybuf_top - cfg->ybuf_bot) / cfg->ybuf_lstd;
251 ctx->isp_frm_info.frame_cnt = 0;
252 ctx->isp_frm_info.line_cnt = 0;
253 ctx->isp_frm_info.max_line_cnt = ALIGN(ctx->isp_max_lcnt, 32);
254 ctx->isp_frm_info.wrap_line = ctx->wrap_line;
255 dvbm_debug("dma_addr %pad y_lstd %d y_fstd %d\n",
256 &cfg->dma_addr, cfg->ybuf_lstd, cfg->ybuf_fstd);
257 dvbm_debug("ybot 0x%x top 0x%x cbuf bot 0x%x top 0x%x\n",
258 addr_base->ybuf_bot, addr_base->ybuf_top,
259 addr_base->cbuf_bot, addr_base->cbuf_top);
260
261 data = (u32 *)addr_base;
262 for (i = 0; i < sizeof(struct rk_dvbm_base) / sizeof(u32); i++)
263 rk_dvbm_set_reg(ctx, i * sizeof(u32) + DVBM_REG_OFFSET, data[i]);
264
265 for (i = 1; i < 65536; i++)
266 if (!((addr_base->ybuf_fstd * i) % (cfg->ybuf_top - cfg->ybuf_bot)))
267 break;
268 ctx->loopcnt = i;
269 return 0;
270 }
271
rk_dvbm_reg_init(struct dvbm_ctx * ctx)272 static void rk_dvbm_reg_init(struct dvbm_ctx *ctx)
273 {
274 struct rk_dvbm_regs *reg = &ctx->regs;
275 u32 *val = (u32 *)reg;
276
277 reg->int_en.buf_ovfl = 1;
278 reg->int_en.isp_cnct = 1;
279 reg->int_en.vepu_cnct = 1;
280 reg->int_en.vepu_discnct = 1;
281 reg->int_en.isp_discnct = 1;
282 reg->int_en.resync_finish = 1;
283 reg->int_en.isp_cnct_timeout = 1;
284 reg->int_en.vepu_cnct_timeout = 1;
285 reg->int_en.vepu_handshake_timeout = 1;
286
287 reg->dvbm_cfg.fmt = 0;
288 reg->dvbm_cfg.auto_resyn = 0;
289 reg->dvbm_cfg.ignore_vepu_cnct_ack = 0;
290 reg->dvbm_cfg.start_point_after_vepu_cnct = 0;
291
292 reg->wdg_cfg0.wdg_isp_cnct_timeout = 0xfffff;
293 reg->wdg_cfg1.wdg_vepu_cnct_timeout = 0xfffff;
294 reg->wdg_cfg2.wdg_vepu_handshake_timeout = 0xfffff;
295
296 rk_dvbm_set_reg(ctx, DVBM_WDG_CFG0, val[DVBM_WDG_CFG0 >> 2]);
297 rk_dvbm_set_reg(ctx, DVBM_WDG_CFG1, val[DVBM_WDG_CFG1 >> 2]);
298 rk_dvbm_set_reg(ctx, DVBM_WDG_CFG2, val[DVBM_WDG_CFG2 >> 2]);
299 rk_dvbm_set_reg(ctx, DVBM_CFG, val[DVBM_CFG >> 2]);
300 rk_dvbm_set_reg(ctx, DVBM_INT_EN, val[DVBM_INT_EN >> 2]);
301 }
302
rk_dvbm_get_port(struct platform_device * pdev,enum dvbm_port_dir dir)303 struct dvbm_port *rk_dvbm_get_port(struct platform_device *pdev,
304 enum dvbm_port_dir dir)
305 {
306 struct dvbm_ctx *ctx = NULL;
307 struct dvbm_port *port = NULL;
308
309 if (WARN_ON(!pdev))
310 return NULL;
311
312 ctx = (struct dvbm_ctx *)platform_get_drvdata(pdev);
313 WARN_ON(!ctx);
314 dvbm_debug("%s dir %d\n", __func__, dir);
315 if (dir == DVBM_ISP_PORT)
316 port = &ctx->port_isp;
317 else if (dir == DVBM_VEPU_PORT)
318 port = &ctx->port_vepu;
319
320 return port;
321 }
322 EXPORT_SYMBOL(rk_dvbm_get_port);
323
rk_dvbm_put(struct dvbm_port * port)324 int rk_dvbm_put(struct dvbm_port *port)
325 {
326 struct dvbm_ctx *ctx = NULL;
327
328 if (WARN_ON(!port))
329 return -EINVAL;
330
331 ctx = port_to_ctx(port);
332
333 if (!ctx)
334 return -EINVAL;
335 return 0;
336 }
337 EXPORT_SYMBOL(rk_dvbm_put);
338
rk_dvbm_link(struct dvbm_port * port)339 int rk_dvbm_link(struct dvbm_port *port)
340 {
341 struct dvbm_ctx *ctx;
342 enum dvbm_port_dir dir;
343 struct rk_dvbm_regs *reg;
344 int ret = 0;
345
346 if (WARN_ON(!port))
347 return -EINVAL;
348
349 ctx = port_to_ctx(port);
350 dir = port->dir;
351 reg = &ctx->regs;
352
353 if (dir == DVBM_ISP_PORT) {
354 if (port->linked) {
355 rk_dvbm_unlink(port);
356 udelay(5);
357 }
358 reg->isp_cnct.isp_cnct = 1;
359 rk_dvbm_set_reg(ctx, DVBM_ISP_CNCT, 0x1);
360 } else if (dir == DVBM_VEPU_PORT) {
361 if (!port->linked) {
362 reg->vepu_cnct.vepu_cnct = 1;
363 rk_dvbm_set_reg(ctx, DVBM_VEPU_CNCT, 0x1);
364 }
365 port->linked = 1;
366 dvbm_debug_dump("=== vepu link ===\n");
367 rk_dvbm_dump_regs(ctx);
368 dvbm_debug_dump("=== vepu link ===\n");
369 }
370
371 dvbm_debug("%s connect frm_cnt[%d : %d]\n",
372 dir == DVBM_ISP_PORT ? "isp" : "vepu",
373 ctx->isp_frm_start, ctx->isp_frm_end);
374
375 return ret;
376 }
377 EXPORT_SYMBOL(rk_dvbm_link);
378
rk_dvbm_unlink(struct dvbm_port * port)379 int rk_dvbm_unlink(struct dvbm_port *port)
380 {
381 struct dvbm_ctx *ctx;
382 enum dvbm_port_dir dir;
383 struct rk_dvbm_regs *reg;
384
385 if (WARN_ON(!port))
386 return -EINVAL;
387
388 ctx = port_to_ctx(port);
389 dir = port->dir;
390 reg = &ctx->regs;
391
392 if (dir == DVBM_ISP_PORT) {
393 reg->isp_cnct.isp_cnct = 0;
394 rk_dvbm_set_reg(ctx, DVBM_ISP_CNCT, 0);
395 } else if (dir == DVBM_VEPU_PORT) {
396 reg->vepu_cnct.vepu_cnct = 0;
397 port->linked = 0;
398 rk_dvbm_set_reg(ctx, DVBM_VEPU_CNCT, 0);
399 if (!ctx->regs.dvbm_cfg.auto_resyn) {
400 u32 connect = 0;
401
402 dvbm2enc_callback(ctx, DVBM_VEPU_REQ_CONNECT, &connect);
403 }
404 }
405 dvbm_debug("%s disconnect\n", dir == DVBM_ISP_PORT ? "isp" : "vepu");
406
407 return 0;
408 }
409 EXPORT_SYMBOL(rk_dvbm_unlink);
410
rk_dvbm_set_cb(struct dvbm_port * port,struct dvbm_cb * cb)411 int rk_dvbm_set_cb(struct dvbm_port *port, struct dvbm_cb *cb)
412 {
413 struct dvbm_ctx *ctx;
414 enum dvbm_port_dir dir;
415
416 if (WARN_ON(!port) || WARN_ON(!cb))
417 return -EINVAL;
418
419 ctx = port_to_ctx(port);
420 dir = port->dir;
421
422 if (dir == DVBM_ISP_PORT) {
423
424 } else if (dir == DVBM_VEPU_PORT) {
425 ctx->vepu_cb.cb = cb->cb;
426 ctx->vepu_cb.ctx = cb->ctx;
427 }
428
429 return 0;
430 }
431 EXPORT_SYMBOL(rk_dvbm_set_cb);
432
rk_dvbm_update_next_adr(struct dvbm_ctx * ctx)433 static void rk_dvbm_update_next_adr(struct dvbm_ctx *ctx)
434 {
435 u32 frame_cnt = ctx->isp_frm_start;
436 struct dvbm_isp_cfg_t *isp_cfg = &ctx->isp_cfg;
437 struct dvbm_addr_cfg *vepu_cfg = &ctx->vepu_cfg;
438 u32 y_wrap_size = isp_cfg->ybuf_top - isp_cfg->ybuf_bot;
439 u32 c_wrap_size = isp_cfg->cbuf_top - isp_cfg->cbuf_bot;
440 u32 s_off;
441
442 frame_cnt = (frame_cnt + 1) % (ctx->loopcnt);
443 s_off = (frame_cnt * isp_cfg->ybuf_fstd) % y_wrap_size;
444 vepu_cfg->ybuf_sadr = isp_cfg->dma_addr + isp_cfg->ybuf_bot + s_off;
445
446 s_off = (frame_cnt * isp_cfg->cbuf_fstd) % c_wrap_size;
447 vepu_cfg->cbuf_sadr = isp_cfg->dma_addr + isp_cfg->cbuf_bot + s_off;
448 }
449
rk_dvbm_ctrl(struct dvbm_port * port,enum dvbm_cmd cmd,void * arg)450 int rk_dvbm_ctrl(struct dvbm_port *port, enum dvbm_cmd cmd, void *arg)
451 {
452 struct dvbm_ctx *ctx;
453 struct rk_dvbm_regs *reg;
454
455 if ((cmd < DVBM_ISP_CMD_BASE) || (cmd > DVBM_VEPU_CMD_BUTT)) {
456 dvbm_err("%s input cmd invalid\n", __func__);
457 return -EINVAL;
458 }
459
460 ctx = port_to_ctx(port);
461 reg = &ctx->regs;
462
463 switch (cmd) {
464 case DVBM_ISP_SET_CFG: {
465 struct dvbm_isp_cfg_t *cfg = (struct dvbm_isp_cfg_t *)arg;
466
467 memcpy(&ctx->isp_cfg, cfg, sizeof(struct dvbm_isp_cfg_t));
468 rk_dvbm_setup_iobuf(ctx);
469 init_isp_infos(ctx);
470 rk_dvbm_update_next_adr(ctx);
471 } break;
472 case DVBM_ISP_FRM_START: {
473 rk_dvbm_update_isp_frm_info(ctx, 0);
474 rk_dvbm_show_time(ctx);
475 } break;
476 case DVBM_ISP_FRM_END: {
477 u32 line_cnt = ctx->isp_max_lcnt;
478
479 ctx->isp_frm_end = *(u32 *)arg;
480 /* wrap frame_cnt 0 - 255 */
481 ctx->isp_frm_info.frame_cnt = (ctx->isp_frm_start + 1) % 256;
482 rk_dvbm_update_next_adr(ctx);
483 rk_dvbm_update_isp_frm_info(ctx, line_cnt);
484 ctx->isp_frm_start++;
485 dvbm_debug("isp frame end[%d : %d]\n", ctx->isp_frm_start, ctx->isp_frm_end);
486 } break;
487 case DVBM_ISP_FRM_QUARTER: {
488 u32 line_cnt;
489
490 line_cnt = ctx->isp_max_lcnt >> 2;
491 rk_dvbm_update_isp_frm_info(ctx, line_cnt);
492 } break;
493 case DVBM_ISP_FRM_HALF: {
494 u32 line_cnt;
495
496 line_cnt = ctx->isp_max_lcnt >> 1;
497 rk_dvbm_update_isp_frm_info(ctx, line_cnt);
498 } break;
499 case DVBM_ISP_FRM_THREE_QUARTERS: {
500 u32 line_cnt;
501
502 line_cnt = (ctx->isp_max_lcnt >> 2) * 3;
503 rk_dvbm_update_isp_frm_info(ctx, line_cnt);
504 } break;
505 case DVBM_VEPU_GET_ADR: {
506 struct dvbm_addr_cfg *dvbm_adr = (struct dvbm_addr_cfg *)arg;
507 struct rk_dvbm_base *addr_base = ®->addr_base;
508
509 dvbm_adr->ybuf_top = addr_base->ybuf_top;
510 dvbm_adr->ybuf_bot = addr_base->ybuf_bot;
511 dvbm_adr->cbuf_top = addr_base->cbuf_top;
512 dvbm_adr->cbuf_bot = addr_base->cbuf_bot;
513 dvbm_adr->cbuf_sadr = ctx->vepu_cfg.cbuf_sadr;
514 dvbm_adr->ybuf_sadr = ctx->vepu_cfg.ybuf_sadr;
515 dvbm_adr->overflow = ctx->isp_frm_info.line_cnt >= ctx->wrap_line;
516 dvbm_adr->frame_id = ctx->isp_frm_info.frame_cnt;
517 dvbm_adr->line_cnt = ctx->isp_frm_info.line_cnt;
518 } break;
519 case DVBM_VEPU_GET_FRAME_INFO: {
520 memcpy(arg, &ctx->isp_frm_info, sizeof(struct dvbm_isp_frm_info));
521 } break;
522 case DVBM_VEPU_SET_RESYNC: {
523 reg->dvbm_cfg.auto_resyn = *(u32 *)arg;
524 dev_info(ctx->dev, "change resync %s\n",
525 reg->dvbm_cfg.auto_resyn ? "auto" : "soft");
526 rk_dvbm_set_reg(ctx, DVBM_CFG, ((u32 *)®->dvbm_cfg)[0]);
527 } break;
528 case DVBM_VEPU_SET_CFG: {
529 struct dvbm_vepu_cfg *cfg = (struct dvbm_vepu_cfg *)arg;
530
531 reg->dvbm_cfg.auto_resyn = cfg->auto_resyn;
532 reg->dvbm_cfg.ignore_vepu_cnct_ack = cfg->ignore_vepu_cnct_ack;
533 reg->dvbm_cfg.start_point_after_vepu_cnct = cfg->start_point_after_vepu_cnct;
534
535 rk_dvbm_set_reg(ctx, DVBM_CFG, ((u32 *)®->dvbm_cfg)[0]);
536 } break;
537 case DVBM_VEPU_DUMP_REGS: {
538 rk_dvbm_dump_regs(ctx);
539 } break;
540 default: {
541 } break;
542 }
543
544 return 0;
545 }
546 EXPORT_SYMBOL(rk_dvbm_ctrl);
547
dvbm_check_irq(struct dvbm_ctx * ctx)548 static void dvbm_check_irq(struct dvbm_ctx *ctx)
549 {
550 u32 irq_st = ctx->irq_status;
551 u32 cur_st = ctx->dvbm_status;
552
553 if (irq_st & ISP_CNCT) {
554 dvbm_debug_irq("%s isp connect success! st 0x%08x\n",
555 __func__, cur_st);
556 ctx->port_isp.linked = 1;
557 }
558 if (irq_st & ISP_DISCNCT) {
559 dvbm_debug_irq("%s isp disconnect success!\n", __func__);
560 ctx->port_isp.linked = 0;
561 }
562 if (irq_st & VEPU_CNCT) {
563 dvbm_debug_irq("%s vepu connect success! st 0x%08x\n",
564 __func__, cur_st);
565 ctx->port_vepu.linked = 1;
566 }
567 if (irq_st & VEPU_DISCNCT) {
568 dvbm_debug_irq("%s vepu disconnect success! st 0x%08x\n", __func__, cur_st);
569 ctx->port_vepu.linked = 0;
570 }
571 if (irq_st & BUF_OVERFLOW) {
572 dvbm_debug_irq("%s buf overflow st 0x%08x auto_resync %d ignore %d\n",
573 __func__, cur_st, ctx->regs.dvbm_cfg.auto_resyn, ctx->ignore_ovfl);
574
575 if (!ctx->regs.dvbm_cfg.auto_resyn && !ctx->ignore_ovfl)
576 rk_dvbm_unlink(&ctx->port_vepu);
577 }
578 if (irq_st & (ISP_CNCT_TIMEOUT | VEPU_CNCT_TIMEOUT))
579 rk_dvbm_dump_regs(ctx);
580 }
581
rk_dvbm_irq(int irq,void * param)582 static irqreturn_t rk_dvbm_irq(int irq, void *param)
583 {
584 struct dvbm_ctx *ctx = param;
585 u32 irq_st = 0;
586 u32 cur_st = 0;
587
588 if (ctx->reg_base) {
589 /* read irq st */
590 irq_st = rk_dvbm_read_reg(ctx, DVBM_INT_ST);
591 cur_st = rk_dvbm_read_reg(ctx, DVBM_ST);
592 if (irq_st & BUF_OVERFLOW) {
593 dvbm_debug_dump("=== dvbm overflow! dump reg st: 0x%08x===\n", irq_st);
594 rk_dvbm_dump_regs(ctx);
595 dvbm2enc_callback(ctx, DVBM_VEPU_NOTIFY_DUMP, NULL);
596 dvbm_debug_dump("=== dvbm overflow! dump reg end===\n");
597 }
598 /* clr irq */
599 rk_dvbm_set_reg(ctx, DVBM_INT_CLR, irq_st);
600 rk_dvbm_set_reg(ctx, DVBM_INT_ST, 0);
601 }
602 ctx->irq_status = irq_st;
603 ctx->dvbm_status = cur_st;
604
605 dvbm_debug_irq("%s irq status 0x%08x\n", __func__, irq_st);
606
607 return IRQ_WAKE_THREAD;
608 }
609
rk_dvbm_isr(int irq,void * param)610 static irqreturn_t rk_dvbm_isr(int irq, void *param)
611 {
612 struct dvbm_ctx *ctx = param;
613
614 dvbm_check_irq(ctx);
615
616 return IRQ_HANDLED;
617 }
618
rk_dvbm_probe(struct platform_device * pdev)619 static int rk_dvbm_probe(struct platform_device *pdev)
620 {
621 int ret;
622 struct dvbm_ctx *ctx = NULL;
623 struct device *dev = &pdev->dev;
624 struct resource *res = NULL;
625
626 dev_info(dev, "probe start\n");
627 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
628 if (!ctx)
629 return -ENOMEM;
630 dev_info(dev, "dvbm ctx %p\n", ctx);
631
632 ctx->dev = dev;
633
634 atomic_set(&ctx->isp_ref, 0);
635 atomic_set(&ctx->vepu_ref, 0);
636 ctx->port_isp.dir = DVBM_ISP_PORT;
637 ctx->port_vepu.dir = DVBM_VEPU_PORT;
638
639 platform_set_drvdata(pdev, ctx);
640
641 pm_runtime_enable(dev);
642
643 /* get irq */
644 ctx->irq = platform_get_irq(pdev, 0);
645 if (ctx->irq < 0) {
646 dev_err(&pdev->dev, "no interrupt resource found\n");
647 ret = -ENODEV;
648 goto failed;
649 }
650 /* get mem resource */
651 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
652 if (!res) {
653 dev_err(&pdev->dev, "no memory resource defined\n");
654 ret = -ENODEV;
655 goto failed;
656 }
657
658 ctx->reg_base = devm_ioremap_resource(dev, res);
659 if (IS_ERR_OR_NULL(ctx->reg_base)) {
660 dev_err(dev, "ioremap failed for resource %pR\n", res);
661 ret = -ENODEV;
662 goto failed;
663 }
664
665 ctx->clk = devm_clk_get(ctx->dev, "clk_core");
666 if (IS_ERR_OR_NULL(ctx->clk)) {
667 dev_err(dev, "clk_get failed for resource %pR\n", res);
668 ret = -ENODEV;
669 goto failed;
670 }
671 ctx->rst = devm_reset_control_get(ctx->dev, "dvbm_rst");
672 if (IS_ERR_OR_NULL(ctx->rst)) {
673 dev_err(dev, "clk_rst failed for resource %pR\n", res);
674 ret = -ENODEV;
675 goto failed;
676 }
677 if (!SOFT_DVBM) {
678 ret = pm_runtime_get_sync(dev);
679 if (ret)
680 dev_err(dev, "pm get failed!\n");
681 ret = rk_dvbm_clk_on(ctx);
682 if (ret)
683 goto failed;
684 }
685 g_ctx = ctx;
686 rk_dvbm_reg_init(ctx);
687 ctx->ignore_ovfl = 1;
688 ctx->dump_s = 0x80;
689 ctx->dump_e = 0xb8;
690 ret = devm_request_threaded_irq(dev, ctx->irq,
691 rk_dvbm_irq, rk_dvbm_isr,
692 IRQF_ONESHOT, dev_name(dev), ctx);
693 if (ret) {
694 dev_err(dev, "register interrupter failed\n");
695 goto failed;
696 }
697 dev_info(dev, "probe success\n");
698
699 return 0;
700
701 failed:
702 pm_runtime_disable(dev);
703
704 return ret;
705 }
706
rk_dvbm_remove(struct platform_device * pdev)707 static int rk_dvbm_remove(struct platform_device *pdev)
708 {
709 struct device *dev = &pdev->dev;
710
711 dev_info(dev, "remove device\n");
712 if (!SOFT_DVBM) {
713 rk_dvbm_clk_off(g_ctx);
714 pm_runtime_put(dev);
715 }
716 pm_runtime_disable(dev);
717
718 return 0;
719 }
720
721 static const struct of_device_id rk_dvbm_dt_ids[] = {
722 {
723 .compatible = "rockchip,rk-dvbm",
724 },
725 { },
726 };
727
728 static struct platform_driver rk_dvbm_driver = {
729 .probe = rk_dvbm_probe,
730 .remove = rk_dvbm_remove,
731 .driver = {
732 .name = "rk_dvbm",
733 .of_match_table = of_match_ptr(rk_dvbm_dt_ids),
734 },
735 };
736
rk_dvbm_init(void)737 static int __init rk_dvbm_init(void)
738 {
739 return platform_driver_register(&rk_dvbm_driver);
740 }
741
rk_dvbm_exit(void)742 static __exit void rk_dvbm_exit(void)
743 {
744 platform_driver_unregister(&rk_dvbm_driver);
745 }
746
747 subsys_initcall(rk_dvbm_init);
748 module_exit(rk_dvbm_exit);
749
750 MODULE_LICENSE("Dual MIT/GPL");
751 MODULE_AUTHOR("Yandong Lin yandong.lin@rock-chips.com");
752 MODULE_DESCRIPTION("Rockchip dvbm driver");
753