diff options
Diffstat (limited to 'drivers/media/platform/st')
32 files changed, 2073 insertions, 3057 deletions
diff --git a/drivers/media/platform/st/Makefile b/drivers/media/platform/st/Makefile index a1f75b2a8225..615a93d62662 100644 --- a/drivers/media/platform/st/Makefile +++ b/drivers/media/platform/st/Makefile @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only obj-y += sti/bdisp/ -obj-y += sti/c8sectpfe/ obj-y += sti/delta/ obj-y += sti/hva/ obj-y += stm32/ diff --git a/drivers/media/platform/st/sti/Kconfig b/drivers/media/platform/st/sti/Kconfig index 60068e8b47b8..91ca0950ff73 100644 --- a/drivers/media/platform/st/sti/Kconfig +++ b/drivers/media/platform/st/sti/Kconfig @@ -1,5 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only source "drivers/media/platform/st/sti/bdisp/Kconfig" -source "drivers/media/platform/st/sti/c8sectpfe/Kconfig" source "drivers/media/platform/st/sti/delta/Kconfig" source "drivers/media/platform/st/sti/hva/Kconfig" diff --git a/drivers/media/platform/st/sti/Makefile b/drivers/media/platform/st/sti/Makefile index f9ce8169b040..3328d50fb6cf 100644 --- a/drivers/media/platform/st/sti/Makefile +++ b/drivers/media/platform/st/sti/Makefile @@ -1,6 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only obj-y += bdisp/ -obj-y += c8sectpfe/ obj-y += delta/ obj-y += hva/ obj-y += stm32/ diff --git a/drivers/media/platform/st/sti/bdisp/bdisp-debug.c b/drivers/media/platform/st/sti/bdisp/bdisp-debug.c index a27f638df11c..f9348aeacc11 100644 --- a/drivers/media/platform/st/sti/bdisp/bdisp-debug.c +++ b/drivers/media/platform/st/sti/bdisp/bdisp-debug.c @@ -455,11 +455,11 @@ static int last_request_show(struct seq_file *s, void *data) seq_printf(s, "Format: %s\t\t\t%s\n", bdisp_fmt_to_str(src), bdisp_fmt_to_str(dst)); - seq_printf(s, "Crop area: %dx%d @ %d,%d ==>\t%dx%d @ %d,%d\n", - src.crop.width, src.crop.height, + seq_printf(s, "Crop area: (%d,%d)/%ux%u ==>\t(%d,%d)/%ux%u\n", src.crop.left, src.crop.top, - dst.crop.width, dst.crop.height, - dst.crop.left, dst.crop.top); + src.crop.width, src.crop.height, + dst.crop.left, dst.crop.top, + dst.crop.width, dst.crop.height); seq_printf(s, "Buff size: %dx%d\t\t%dx%d\n\n", src.width, src.height, dst.width, dst.height); diff --git a/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c index 1328b4eb6b9f..56169b70652d 100644 --- a/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c +++ b/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c @@ -33,7 +33,10 @@ #define BDISP_MIN_H 1 #define BDISP_MAX_H 8191 -#define fh_to_ctx(__fh) container_of(__fh, struct bdisp_ctx, fh) +static inline struct bdisp_ctx *file_to_ctx(struct file *filp) +{ + return container_of(file_to_v4l2_fh(filp), struct bdisp_ctx, fh); +} enum bdisp_dev_flags { ST_M2M_OPEN, /* Driver opened */ @@ -531,8 +534,6 @@ static const struct vb2_ops bdisp_qops = { .queue_setup = bdisp_queue_setup, .buf_prepare = bdisp_buf_prepare, .buf_queue = bdisp_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .stop_streaming = bdisp_stop_streaming, .start_streaming = bdisp_start_streaming, }; @@ -605,8 +606,7 @@ static int bdisp_open(struct file *file) /* Use separate control handler per file handle */ ctx->fh.ctrl_handler = &ctx->ctrl_handler; - file->private_data = &ctx->fh; - v4l2_fh_add(&ctx->fh); + v4l2_fh_add(&ctx->fh, file); /* Default format */ ctx->src = bdisp_dflt_fmt; @@ -632,7 +632,7 @@ static int bdisp_open(struct file *file) error_ctrls: bdisp_ctrls_delete(ctx); - v4l2_fh_del(&ctx->fh); + v4l2_fh_del(&ctx->fh, file); error_fh: v4l2_fh_exit(&ctx->fh); bdisp_hw_free_nodes(ctx); @@ -646,7 +646,7 @@ unlock: static int bdisp_release(struct file *file) { - struct bdisp_ctx *ctx = fh_to_ctx(file->private_data); + struct bdisp_ctx *ctx = file_to_ctx(file); struct bdisp_dev *bdisp = ctx->bdisp_dev; dev_dbg(bdisp->dev, "%s\n", __func__); @@ -657,7 +657,7 @@ static int bdisp_release(struct file *file) bdisp_ctrls_delete(ctx); - v4l2_fh_del(&ctx->fh); + v4l2_fh_del(&ctx->fh, file); v4l2_fh_exit(&ctx->fh); if (--bdisp->m2m.refcnt <= 0) @@ -684,7 +684,7 @@ static const struct v4l2_file_operations bdisp_fops = { static int bdisp_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { - struct bdisp_ctx *ctx = fh_to_ctx(fh); + struct bdisp_ctx *ctx = file_to_ctx(file); struct bdisp_dev *bdisp = ctx->bdisp_dev; strscpy(cap->driver, bdisp->pdev->name, sizeof(cap->driver)); @@ -696,7 +696,7 @@ static int bdisp_querycap(struct file *file, void *fh, static int bdisp_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) { - struct bdisp_ctx *ctx = fh_to_ctx(fh); + struct bdisp_ctx *ctx = file_to_ctx(file); const struct bdisp_fmt *fmt; if (f->index >= ARRAY_SIZE(bdisp_formats)) @@ -716,7 +716,7 @@ static int bdisp_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) static int bdisp_g_fmt(struct file *file, void *fh, struct v4l2_format *f) { - struct bdisp_ctx *ctx = fh_to_ctx(fh); + struct bdisp_ctx *ctx = file_to_ctx(file); struct v4l2_pix_format *pix; struct bdisp_frame *frame = ctx_get_frame(ctx, f->type); @@ -740,7 +740,7 @@ static int bdisp_g_fmt(struct file *file, void *fh, struct v4l2_format *f) static int bdisp_try_fmt(struct file *file, void *fh, struct v4l2_format *f) { - struct bdisp_ctx *ctx = fh_to_ctx(fh); + struct bdisp_ctx *ctx = file_to_ctx(file); struct v4l2_pix_format *pix = &f->fmt.pix; const struct bdisp_fmt *format; u32 in_w, in_h; @@ -790,7 +790,7 @@ static int bdisp_try_fmt(struct file *file, void *fh, struct v4l2_format *f) static int bdisp_s_fmt(struct file *file, void *fh, struct v4l2_format *f) { - struct bdisp_ctx *ctx = fh_to_ctx(fh); + struct bdisp_ctx *ctx = file_to_ctx(file); struct vb2_queue *vq; struct bdisp_frame *frame; struct v4l2_pix_format *pix; @@ -843,8 +843,8 @@ static int bdisp_s_fmt(struct file *file, void *fh, struct v4l2_format *f) static int bdisp_g_selection(struct file *file, void *fh, struct v4l2_selection *s) { + struct bdisp_ctx *ctx = file_to_ctx(file); struct bdisp_frame *frame; - struct bdisp_ctx *ctx = fh_to_ctx(fh); frame = ctx_get_frame(ctx, s->type); if (IS_ERR(frame)) { @@ -921,8 +921,8 @@ static int is_rect_enclosed(struct v4l2_rect *a, struct v4l2_rect *b) static int bdisp_s_selection(struct file *file, void *fh, struct v4l2_selection *s) { + struct bdisp_ctx *ctx = file_to_ctx(file); struct bdisp_frame *frame; - struct bdisp_ctx *ctx = fh_to_ctx(fh); struct v4l2_rect *in, out; bool valid = false; @@ -955,8 +955,8 @@ static int bdisp_s_selection(struct file *file, void *fh, if ((out.left < 0) || (out.left >= frame->width) || (out.top < 0) || (out.top >= frame->height)) { dev_err(ctx->bdisp_dev->dev, - "Invalid crop: %dx%d@(%d,%d) vs frame: %dx%d\n", - out.width, out.height, out.left, out.top, + "Invalid crop: (%d,%d)/%ux%u vs frame: %dx%d\n", + out.left, out.top, out.width, out.height, frame->width, frame->height); return -EINVAL; } @@ -968,8 +968,8 @@ static int bdisp_s_selection(struct file *file, void *fh, if (((out.left + out.width) > frame->width) || ((out.top + out.height) > frame->height)) { dev_err(ctx->bdisp_dev->dev, - "Invalid crop: %dx%d@(%d,%d) vs frame: %dx%d\n", - out.width, out.height, out.left, out.top, + "Invalid crop: (%d,%d)/%ux%u vs frame: %dx%d\n", + out.left, out.top, out.width, out.height, frame->width, frame->height); return -EINVAL; } @@ -984,9 +984,9 @@ static int bdisp_s_selection(struct file *file, void *fh, if ((out.left != in->left) || (out.top != in->top) || (out.width != in->width) || (out.height != in->height)) { dev_dbg(ctx->bdisp_dev->dev, - "%s crop updated: %dx%d@(%d,%d) -> %dx%d@(%d,%d)\n", - __func__, in->width, in->height, in->left, in->top, - out.width, out.height, out.left, out.top); + "%s crop updated: (%d,%d)/%ux%u -> (%d,%d)/%ux%u\n", + __func__, in->left, in->top, in->width, in->height, + out.left, out.top, out.width, out.height); *in = out; } @@ -999,7 +999,7 @@ static int bdisp_s_selection(struct file *file, void *fh, static int bdisp_streamon(struct file *file, void *fh, enum v4l2_buf_type type) { - struct bdisp_ctx *ctx = fh_to_ctx(fh); + struct bdisp_ctx *ctx = file_to_ctx(file); if ((type == V4L2_BUF_TYPE_VIDEO_OUTPUT) && !bdisp_ctx_state_is_set(BDISP_SRC_FMT, ctx)) { @@ -1160,7 +1160,7 @@ static void bdisp_irq_timeout(struct work_struct *ptr) static int bdisp_m2m_suspend(struct bdisp_dev *bdisp) { unsigned long flags; - int timeout; + long time_left; spin_lock_irqsave(&bdisp->slock, flags); if (!test_bit(ST_M2M_RUNNING, &bdisp->state)) { @@ -1171,13 +1171,13 @@ static int bdisp_m2m_suspend(struct bdisp_dev *bdisp) set_bit(ST_M2M_SUSPENDING, &bdisp->state); spin_unlock_irqrestore(&bdisp->slock, flags); - timeout = wait_event_timeout(bdisp->irq_queue, - test_bit(ST_M2M_SUSPENDED, &bdisp->state), - BDISP_WORK_TIMEOUT); + time_left = wait_event_timeout(bdisp->irq_queue, + test_bit(ST_M2M_SUSPENDED, &bdisp->state), + BDISP_WORK_TIMEOUT); clear_bit(ST_M2M_SUSPENDING, &bdisp->state); - if (!timeout) { + if (!time_left) { dev_err(bdisp->dev, "%s IRQ timeout\n", __func__); return -EAGAIN; } @@ -1411,7 +1411,7 @@ MODULE_DEVICE_TABLE(of, bdisp_match_types); static struct platform_driver bdisp_driver = { .probe = bdisp_probe, - .remove_new = bdisp_remove, + .remove = bdisp_remove, .driver = { .name = BDISP_NAME, .of_match_table = bdisp_match_types, diff --git a/drivers/media/platform/st/sti/c8sectpfe/Kconfig b/drivers/media/platform/st/sti/c8sectpfe/Kconfig deleted file mode 100644 index 01c33d9c9ec3..000000000000 --- a/drivers/media/platform/st/sti/c8sectpfe/Kconfig +++ /dev/null @@ -1,28 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config DVB_C8SECTPFE - tristate "STMicroelectronics C8SECTPFE DVB support" - depends on DVB_PLATFORM_DRIVERS - depends on PINCTRL && DVB_CORE && I2C - depends on ARCH_STI || ARCH_MULTIPLATFORM || COMPILE_TEST - select FW_LOADER - select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT - select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT - select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT - select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT - select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT - select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT - select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT - - help - This adds support for DVB front-end cards connected - to TS inputs of STiH407/410 SoC. - - The driver currently supports C8SECTPFE's TS input block, - memdma engine, and HW PID filtering. - - Supported DVB front-end cards are: - - STMicroelectronics DVB-T B2100A (STV0367 + TDA18212) - - STMicroelectronics DVB-S/S2 STV0903 + STV6110 + LNBP24 board - - To compile this driver as a module, choose M here: the - module will be called c8sectpfe. diff --git a/drivers/media/platform/st/sti/c8sectpfe/Makefile b/drivers/media/platform/st/sti/c8sectpfe/Makefile deleted file mode 100644 index 99425137ee0a..000000000000 --- a/drivers/media/platform/st/sti/c8sectpfe/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -c8sectpfe-y += c8sectpfe-core.o c8sectpfe-common.o c8sectpfe-dvb.o - -ifneq ($(CONFIG_DEBUG_FS),) -c8sectpfe-y += c8sectpfe-debugfs.o -endif - -obj-$(CONFIG_DVB_C8SECTPFE) += c8sectpfe.o - -ccflags-y += -I $(srctree)/drivers/media/dvb-frontends/ -ccflags-y += -I $(srctree)/drivers/media/tuners/ diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.c deleted file mode 100644 index 5df67da25525..000000000000 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.c +++ /dev/null @@ -1,262 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * c8sectpfe-common.c - C8SECTPFE STi DVB driver - * - * Copyright (c) STMicroelectronics 2015 - * - * Author: Peter Griffin <peter.griffin@linaro.org> - * - */ -#include <linux/completion.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/dvb/dmx.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/ioport.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/time.h> -#include <linux/wait.h> - -#include <media/dmxdev.h> -#include <media/dvbdev.h> -#include <media/dvb_demux.h> -#include <media/dvb_frontend.h> -#include <media/dvb_net.h> - -#include "c8sectpfe-common.h" -#include "c8sectpfe-core.h" -#include "c8sectpfe-dvb.h" - -static int register_dvb(struct stdemux *demux, struct dvb_adapter *adap, - void *start_feed, void *stop_feed, - struct c8sectpfei *fei) -{ - int result; - - demux->dvb_demux.dmx.capabilities = DMX_TS_FILTERING | - DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING; - - demux->dvb_demux.priv = demux; - demux->dvb_demux.filternum = C8SECTPFE_MAXCHANNEL; - demux->dvb_demux.feednum = C8SECTPFE_MAXCHANNEL; - - demux->dvb_demux.start_feed = start_feed; - demux->dvb_demux.stop_feed = stop_feed; - demux->dvb_demux.write_to_decoder = NULL; - - result = dvb_dmx_init(&demux->dvb_demux); - if (result < 0) { - dev_err(fei->dev, "dvb_dmx_init failed (errno = %d)\n", - result); - goto err_dmx; - } - - demux->dmxdev.filternum = demux->dvb_demux.filternum; - demux->dmxdev.demux = &demux->dvb_demux.dmx; - demux->dmxdev.capabilities = 0; - - result = dvb_dmxdev_init(&demux->dmxdev, adap); - if (result < 0) { - dev_err(fei->dev, "dvb_dmxdev_init failed (errno = %d)\n", - result); - - goto err_dmxdev; - } - - demux->hw_frontend.source = DMX_FRONTEND_0 + demux->tsin_index; - - result = demux->dvb_demux.dmx.add_frontend(&demux->dvb_demux.dmx, - &demux->hw_frontend); - if (result < 0) { - dev_err(fei->dev, "add_frontend failed (errno = %d)\n", result); - goto err_fe_hw; - } - - demux->mem_frontend.source = DMX_MEMORY_FE; - result = demux->dvb_demux.dmx.add_frontend(&demux->dvb_demux.dmx, - &demux->mem_frontend); - if (result < 0) { - dev_err(fei->dev, "add_frontend failed (%d)\n", result); - goto err_fe_mem; - } - - result = demux->dvb_demux.dmx.connect_frontend(&demux->dvb_demux.dmx, - &demux->hw_frontend); - if (result < 0) { - dev_err(fei->dev, "connect_frontend (%d)\n", result); - goto err_fe_con; - } - - return 0; - -err_fe_con: - demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx, - &demux->mem_frontend); -err_fe_mem: - demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx, - &demux->hw_frontend); -err_fe_hw: - dvb_dmxdev_release(&demux->dmxdev); -err_dmxdev: - dvb_dmx_release(&demux->dvb_demux); -err_dmx: - return result; - -} - -static void unregister_dvb(struct stdemux *demux) -{ - - demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx, - &demux->mem_frontend); - - demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx, - &demux->hw_frontend); - - dvb_dmxdev_release(&demux->dmxdev); - - dvb_dmx_release(&demux->dvb_demux); -} - -static struct c8sectpfe *c8sectpfe_create(struct c8sectpfei *fei, - void *start_feed, - void *stop_feed) -{ - struct c8sectpfe *c8sectpfe; - int result; - int i, j; - - short int ids[] = { -1 }; - - c8sectpfe = kzalloc(sizeof(struct c8sectpfe), GFP_KERNEL); - if (!c8sectpfe) - goto err1; - - mutex_init(&c8sectpfe->lock); - - c8sectpfe->device = fei->dev; - - result = dvb_register_adapter(&c8sectpfe->adapter, "STi c8sectpfe", - THIS_MODULE, fei->dev, ids); - if (result < 0) { - dev_err(fei->dev, "dvb_register_adapter failed (errno = %d)\n", - result); - goto err2; - } - - c8sectpfe->adapter.priv = fei; - - for (i = 0; i < fei->tsin_count; i++) { - - c8sectpfe->demux[i].tsin_index = i; - c8sectpfe->demux[i].c8sectpfei = fei; - - result = register_dvb(&c8sectpfe->demux[i], &c8sectpfe->adapter, - start_feed, stop_feed, fei); - if (result < 0) { - dev_err(fei->dev, - "register_dvb feed=%d failed (errno = %d)\n", - result, i); - - /* we take a all or nothing approach */ - for (j = 0; j < i; j++) - unregister_dvb(&c8sectpfe->demux[j]); - goto err3; - } - } - - c8sectpfe->num_feeds = fei->tsin_count; - - return c8sectpfe; -err3: - dvb_unregister_adapter(&c8sectpfe->adapter); -err2: - kfree(c8sectpfe); -err1: - return NULL; -}; - -static void c8sectpfe_delete(struct c8sectpfe *c8sectpfe) -{ - int i; - - if (!c8sectpfe) - return; - - for (i = 0; i < c8sectpfe->num_feeds; i++) - unregister_dvb(&c8sectpfe->demux[i]); - - dvb_unregister_adapter(&c8sectpfe->adapter); - - kfree(c8sectpfe); -}; - -void c8sectpfe_tuner_unregister_frontend(struct c8sectpfe *c8sectpfe, - struct c8sectpfei *fei) -{ - int n; - struct channel_info *tsin; - - for (n = 0; n < fei->tsin_count; n++) { - - tsin = fei->channel_data[n]; - - if (tsin) { - if (tsin->frontend) { - dvb_unregister_frontend(tsin->frontend); - dvb_frontend_detach(tsin->frontend); - } - - i2c_put_adapter(tsin->i2c_adapter); - - if (tsin->i2c_client) { - module_put(tsin->i2c_client->dev.driver->owner); - i2c_unregister_device(tsin->i2c_client); - } - } - } - - c8sectpfe_delete(c8sectpfe); -}; - -int c8sectpfe_tuner_register_frontend(struct c8sectpfe **c8sectpfe, - struct c8sectpfei *fei, - void *start_feed, - void *stop_feed) -{ - struct channel_info *tsin; - struct dvb_frontend *frontend; - int n, res; - - *c8sectpfe = c8sectpfe_create(fei, start_feed, stop_feed); - if (!*c8sectpfe) - return -ENOMEM; - - for (n = 0; n < fei->tsin_count; n++) { - tsin = fei->channel_data[n]; - - res = c8sectpfe_frontend_attach(&frontend, *c8sectpfe, tsin, n); - if (res) - goto err; - - res = dvb_register_frontend(&c8sectpfe[0]->adapter, frontend); - if (res < 0) { - dev_err(fei->dev, "dvb_register_frontend failed (%d)\n", - res); - goto err; - } - - tsin->frontend = frontend; - } - - return 0; - -err: - c8sectpfe_tuner_unregister_frontend(*c8sectpfe, fei); - return res; -} diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.h deleted file mode 100644 index f8d97841f366..000000000000 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.h +++ /dev/null @@ -1,60 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * c8sectpfe-common.h - C8SECTPFE STi DVB driver - * - * Copyright (c) STMicroelectronics 2015 - * - * Author: Peter Griffin <peter.griffin@linaro.org> - * - */ -#ifndef _C8SECTPFE_COMMON_H_ -#define _C8SECTPFE_COMMON_H_ - -#include <linux/dvb/dmx.h> -#include <linux/dvb/frontend.h> -#include <linux/gpio.h> - -#include <media/dmxdev.h> -#include <media/dvb_demux.h> -#include <media/dvb_frontend.h> -#include <media/dvb_net.h> - -/* Maximum number of channels */ -#define C8SECTPFE_MAXADAPTER (4) -#define C8SECTPFE_MAXCHANNEL 64 -#define STPTI_MAXCHANNEL 64 - -#define MAX_INPUTBLOCKS 7 - -struct c8sectpfe; -struct stdemux; - -struct stdemux { - struct dvb_demux dvb_demux; - struct dmxdev dmxdev; - struct dmx_frontend hw_frontend; - struct dmx_frontend mem_frontend; - int tsin_index; - int running_feed_count; - struct c8sectpfei *c8sectpfei; -}; - -struct c8sectpfe { - struct stdemux demux[MAX_INPUTBLOCKS]; - struct mutex lock; - struct dvb_adapter adapter; - struct device *device; - int mapping; - int num_feeds; -}; - -/* Channel registration */ -int c8sectpfe_tuner_register_frontend(struct c8sectpfe **c8sectpfe, - struct c8sectpfei *fei, - void *start_feed, - void *stop_feed); - -void c8sectpfe_tuner_unregister_frontend(struct c8sectpfe *c8sectpfe, - struct c8sectpfei *fei); - -#endif diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c deleted file mode 100644 index 2f58a0d0df85..000000000000 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c +++ /dev/null @@ -1,1169 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * c8sectpfe-core.c - C8SECTPFE STi DVB driver - * - * Copyright (c) STMicroelectronics 2015 - * - * Author:Peter Bennett <peter.bennett@st.com> - * Peter Griffin <peter.griffin@linaro.org> - * - */ -#include <linux/atomic.h> -#include <linux/clk.h> -#include <linux/completion.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/dma-mapping.h> -#include <linux/dvb/dmx.h> -#include <linux/dvb/frontend.h> -#include <linux/err.h> -#include <linux/errno.h> -#include <linux/firmware.h> -#include <linux/gpio/consumer.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/module.h> -#include <linux/of_platform.h> -#include <linux/pinctrl/consumer.h> -#include <linux/pinctrl/pinctrl.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/time.h> -#include <linux/usb.h> -#include <linux/wait.h> - -#include "c8sectpfe-common.h" -#include "c8sectpfe-core.h" -#include "c8sectpfe-debugfs.h" - -#include <media/dmxdev.h> -#include <media/dvb_demux.h> -#include <media/dvb_frontend.h> -#include <media/dvb_net.h> - -#define FIRMWARE_MEMDMA "pti_memdma_h407.elf" -MODULE_FIRMWARE(FIRMWARE_MEMDMA); - -#define PID_TABLE_SIZE 1024 -#define POLL_MSECS 50 - -static int load_c8sectpfe_fw(struct c8sectpfei *fei); - -#define TS_PKT_SIZE 188 -#define HEADER_SIZE (4) -#define PACKET_SIZE (TS_PKT_SIZE+HEADER_SIZE) - -#define FEI_ALIGNMENT (32) -/* hw requires minimum of 8*PACKET_SIZE and padded to 8byte boundary */ -#define FEI_BUFFER_SIZE (8*PACKET_SIZE*340) - -#define FIFO_LEN 1024 - -static void c8sectpfe_timer_interrupt(struct timer_list *t) -{ - struct c8sectpfei *fei = from_timer(fei, t, timer); - struct channel_info *channel; - int chan_num; - - /* iterate through input block channels */ - for (chan_num = 0; chan_num < fei->tsin_count; chan_num++) { - channel = fei->channel_data[chan_num]; - - /* is this descriptor initialised and TP enabled */ - if (channel->irec && readl(channel->irec + DMA_PRDS_TPENABLE)) - tasklet_schedule(&channel->tsklet); - } - - fei->timer.expires = jiffies + msecs_to_jiffies(POLL_MSECS); - add_timer(&fei->timer); -} - -static void channel_swdemux_tsklet(struct tasklet_struct *t) -{ - struct channel_info *channel = from_tasklet(channel, t, tsklet); - struct c8sectpfei *fei; - unsigned long wp, rp; - int pos, num_packets, n, size; - u8 *buf; - - if (unlikely(!channel || !channel->irec)) - return; - - fei = channel->fei; - - wp = readl(channel->irec + DMA_PRDS_BUSWP_TP(0)); - rp = readl(channel->irec + DMA_PRDS_BUSRP_TP(0)); - - pos = rp - channel->back_buffer_busaddr; - - /* has it wrapped */ - if (wp < rp) - wp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE; - - size = wp - rp; - num_packets = size / PACKET_SIZE; - - /* manage cache so data is visible to CPU */ - dma_sync_single_for_cpu(fei->dev, - rp, - size, - DMA_FROM_DEVICE); - - buf = channel->back_buffer_aligned; - - dev_dbg(fei->dev, - "chan=%d channel=%p num_packets = %d, buf = %p, pos = 0x%x\n\trp=0x%lx, wp=0x%lx\n", - channel->tsin_id, channel, num_packets, buf, pos, rp, wp); - - for (n = 0; n < num_packets; n++) { - dvb_dmx_swfilter_packets( - &fei->c8sectpfe[0]-> - demux[channel->demux_mapping].dvb_demux, - &buf[pos], 1); - - pos += PACKET_SIZE; - } - - /* advance the read pointer */ - if (wp == (channel->back_buffer_busaddr + FEI_BUFFER_SIZE)) - writel(channel->back_buffer_busaddr, channel->irec + - DMA_PRDS_BUSRP_TP(0)); - else - writel(wp, channel->irec + DMA_PRDS_BUSRP_TP(0)); -} - -static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - struct dvb_demux *demux = dvbdmxfeed->demux; - struct stdemux *stdemux = demux->priv; - struct c8sectpfei *fei = stdemux->c8sectpfei; - struct channel_info *channel; - u32 tmp; - unsigned long *bitmap; - int ret; - - switch (dvbdmxfeed->type) { - case DMX_TYPE_TS: - break; - case DMX_TYPE_SEC: - break; - default: - dev_err(fei->dev, "%s:%d Error bailing\n" - , __func__, __LINE__); - return -EINVAL; - } - - if (dvbdmxfeed->type == DMX_TYPE_TS) { - switch (dvbdmxfeed->pes_type) { - case DMX_PES_VIDEO: - case DMX_PES_AUDIO: - case DMX_PES_TELETEXT: - case DMX_PES_PCR: - case DMX_PES_OTHER: - break; - default: - dev_err(fei->dev, "%s:%d Error bailing\n" - , __func__, __LINE__); - return -EINVAL; - } - } - - if (!atomic_read(&fei->fw_loaded)) { - ret = load_c8sectpfe_fw(fei); - if (ret) - return ret; - } - - mutex_lock(&fei->lock); - - channel = fei->channel_data[stdemux->tsin_index]; - - bitmap = channel->pid_buffer_aligned; - - /* 8192 is a special PID */ - if (dvbdmxfeed->pid == 8192) { - tmp = readl(fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id)); - tmp &= ~C8SECTPFE_PID_ENABLE; - writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id)); - - } else { - bitmap_set(bitmap, dvbdmxfeed->pid, 1); - } - - /* manage cache so PID bitmap is visible to HW */ - dma_sync_single_for_device(fei->dev, - channel->pid_buffer_busaddr, - PID_TABLE_SIZE, - DMA_TO_DEVICE); - - channel->active = 1; - - if (fei->global_feed_count == 0) { - fei->timer.expires = jiffies + - msecs_to_jiffies(msecs_to_jiffies(POLL_MSECS)); - - add_timer(&fei->timer); - } - - if (stdemux->running_feed_count == 0) { - - dev_dbg(fei->dev, "Starting channel=%p\n", channel); - - tasklet_setup(&channel->tsklet, channel_swdemux_tsklet); - - /* Reset the internal inputblock sram pointers */ - writel(channel->fifo, - fei->io + C8SECTPFE_IB_BUFF_STRT(channel->tsin_id)); - writel(channel->fifo + FIFO_LEN - 1, - fei->io + C8SECTPFE_IB_BUFF_END(channel->tsin_id)); - - writel(channel->fifo, - fei->io + C8SECTPFE_IB_READ_PNT(channel->tsin_id)); - writel(channel->fifo, - fei->io + C8SECTPFE_IB_WRT_PNT(channel->tsin_id)); - - - /* reset read / write memdma ptrs for this channel */ - writel(channel->back_buffer_busaddr, channel->irec + - DMA_PRDS_BUSBASE_TP(0)); - - tmp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE - 1; - writel(tmp, channel->irec + DMA_PRDS_BUSTOP_TP(0)); - - writel(channel->back_buffer_busaddr, channel->irec + - DMA_PRDS_BUSWP_TP(0)); - - /* Issue a reset and enable InputBlock */ - writel(C8SECTPFE_SYS_ENABLE | C8SECTPFE_SYS_RESET - , fei->io + C8SECTPFE_IB_SYS(channel->tsin_id)); - - /* and enable the tp */ - writel(0x1, channel->irec + DMA_PRDS_TPENABLE); - - dev_dbg(fei->dev, "%s:%d Starting DMA feed on stdemux=%p\n" - , __func__, __LINE__, stdemux); - } - - stdemux->running_feed_count++; - fei->global_feed_count++; - - mutex_unlock(&fei->lock); - - return 0; -} - -static int c8sectpfe_stop_feed(struct dvb_demux_feed *dvbdmxfeed) -{ - - struct dvb_demux *demux = dvbdmxfeed->demux; - struct stdemux *stdemux = demux->priv; - struct c8sectpfei *fei = stdemux->c8sectpfei; - struct channel_info *channel; - int idlereq; - u32 tmp; - int ret; - unsigned long *bitmap; - - if (!atomic_read(&fei->fw_loaded)) { - ret = load_c8sectpfe_fw(fei); - if (ret) - return ret; - } - - mutex_lock(&fei->lock); - - channel = fei->channel_data[stdemux->tsin_index]; - - bitmap = channel->pid_buffer_aligned; - - if (dvbdmxfeed->pid == 8192) { - tmp = readl(fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id)); - tmp |= C8SECTPFE_PID_ENABLE; - writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id)); - } else { - bitmap_clear(bitmap, dvbdmxfeed->pid, 1); - } - - /* manage cache so data is visible to HW */ - dma_sync_single_for_device(fei->dev, - channel->pid_buffer_busaddr, - PID_TABLE_SIZE, - DMA_TO_DEVICE); - - if (--stdemux->running_feed_count == 0) { - - channel = fei->channel_data[stdemux->tsin_index]; - - /* TP re-configuration on page 168 of functional spec */ - - /* disable IB (prevents more TS data going to memdma) */ - writel(0, fei->io + C8SECTPFE_IB_SYS(channel->tsin_id)); - - /* disable this channels descriptor */ - writel(0, channel->irec + DMA_PRDS_TPENABLE); - - tasklet_disable(&channel->tsklet); - - /* now request memdma channel goes idle */ - idlereq = (1 << channel->tsin_id) | IDLEREQ; - writel(idlereq, fei->io + DMA_IDLE_REQ); - - /* wait for idle irq handler to signal completion */ - ret = wait_for_completion_timeout(&channel->idle_completion, - msecs_to_jiffies(100)); - - if (ret == 0) - dev_warn(fei->dev, - "Timeout waiting for idle irq on tsin%d\n", - channel->tsin_id); - - reinit_completion(&channel->idle_completion); - - /* reset read / write ptrs for this channel */ - - writel(channel->back_buffer_busaddr, - channel->irec + DMA_PRDS_BUSBASE_TP(0)); - - tmp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE - 1; - writel(tmp, channel->irec + DMA_PRDS_BUSTOP_TP(0)); - - writel(channel->back_buffer_busaddr, - channel->irec + DMA_PRDS_BUSWP_TP(0)); - - dev_dbg(fei->dev, - "%s:%d stopping DMA feed on stdemux=%p channel=%d\n", - __func__, __LINE__, stdemux, channel->tsin_id); - - /* turn off all PIDS in the bitmap */ - memset(channel->pid_buffer_aligned, 0, PID_TABLE_SIZE); - - /* manage cache so data is visible to HW */ - dma_sync_single_for_device(fei->dev, - channel->pid_buffer_busaddr, - PID_TABLE_SIZE, - DMA_TO_DEVICE); - - channel->active = 0; - } - - if (--fei->global_feed_count == 0) { - dev_dbg(fei->dev, "%s:%d global_feed_count=%d\n" - , __func__, __LINE__, fei->global_feed_count); - - del_timer(&fei->timer); - } - - mutex_unlock(&fei->lock); - - return 0; -} - -static struct channel_info *find_channel(struct c8sectpfei *fei, int tsin_num) -{ - int i; - - for (i = 0; i < C8SECTPFE_MAX_TSIN_CHAN; i++) { - if (!fei->channel_data[i]) - continue; - - if (fei->channel_data[i]->tsin_id == tsin_num) - return fei->channel_data[i]; - } - - return NULL; -} - -static void c8sectpfe_getconfig(struct c8sectpfei *fei) -{ - struct c8sectpfe_hw *hw = &fei->hw_stats; - - hw->num_ib = readl(fei->io + SYS_CFG_NUM_IB); - hw->num_mib = readl(fei->io + SYS_CFG_NUM_MIB); - hw->num_swts = readl(fei->io + SYS_CFG_NUM_SWTS); - hw->num_tsout = readl(fei->io + SYS_CFG_NUM_TSOUT); - hw->num_ccsc = readl(fei->io + SYS_CFG_NUM_CCSC); - hw->num_ram = readl(fei->io + SYS_CFG_NUM_RAM); - hw->num_tp = readl(fei->io + SYS_CFG_NUM_TP); - - dev_info(fei->dev, "C8SECTPFE hw supports the following:\n"); - dev_info(fei->dev, "Input Blocks: %d\n", hw->num_ib); - dev_info(fei->dev, "Merged Input Blocks: %d\n", hw->num_mib); - dev_info(fei->dev, "Software Transport Stream Inputs: %d\n" - , hw->num_swts); - dev_info(fei->dev, "Transport Stream Output: %d\n", hw->num_tsout); - dev_info(fei->dev, "Cable Card Converter: %d\n", hw->num_ccsc); - dev_info(fei->dev, "RAMs supported by C8SECTPFE: %d\n", hw->num_ram); - dev_info(fei->dev, "Tango TPs supported by C8SECTPFE: %d\n" - , hw->num_tp); -} - -static irqreturn_t c8sectpfe_idle_irq_handler(int irq, void *priv) -{ - struct c8sectpfei *fei = priv; - struct channel_info *chan; - int bit; - unsigned long tmp = readl(fei->io + DMA_IDLE_REQ); - - /* page 168 of functional spec: Clear the idle request - by writing 0 to the C8SECTPFE_DMA_IDLE_REQ register. */ - - /* signal idle completion */ - for_each_set_bit(bit, &tmp, fei->hw_stats.num_ib) { - - chan = find_channel(fei, bit); - - if (chan) - complete(&chan->idle_completion); - } - - writel(0, fei->io + DMA_IDLE_REQ); - - return IRQ_HANDLED; -} - - -static void free_input_block(struct c8sectpfei *fei, struct channel_info *tsin) -{ - if (!fei || !tsin) - return; - - if (tsin->back_buffer_busaddr) - if (!dma_mapping_error(fei->dev, tsin->back_buffer_busaddr)) - dma_unmap_single(fei->dev, tsin->back_buffer_busaddr, - FEI_BUFFER_SIZE, DMA_BIDIRECTIONAL); - - kfree(tsin->back_buffer_start); - - if (tsin->pid_buffer_busaddr) - if (!dma_mapping_error(fei->dev, tsin->pid_buffer_busaddr)) - dma_unmap_single(fei->dev, tsin->pid_buffer_busaddr, - PID_TABLE_SIZE, DMA_BIDIRECTIONAL); - - kfree(tsin->pid_buffer_start); -} - -#define MAX_NAME 20 - -static int configure_memdma_and_inputblock(struct c8sectpfei *fei, - struct channel_info *tsin) -{ - int ret; - u32 tmp; - char tsin_pin_name[MAX_NAME]; - - if (!fei || !tsin) - return -EINVAL; - - dev_dbg(fei->dev, "%s:%d Configuring channel=%p tsin=%d\n" - , __func__, __LINE__, tsin, tsin->tsin_id); - - init_completion(&tsin->idle_completion); - - tsin->back_buffer_start = kzalloc(FEI_BUFFER_SIZE + FEI_ALIGNMENT, GFP_KERNEL); - if (!tsin->back_buffer_start) { - ret = -ENOMEM; - goto err_unmap; - } - - /* Ensure backbuffer is 32byte aligned */ - tsin->back_buffer_aligned = tsin->back_buffer_start + FEI_ALIGNMENT; - - tsin->back_buffer_aligned = PTR_ALIGN(tsin->back_buffer_aligned, FEI_ALIGNMENT); - - tsin->back_buffer_busaddr = dma_map_single(fei->dev, - tsin->back_buffer_aligned, - FEI_BUFFER_SIZE, - DMA_BIDIRECTIONAL); - - if (dma_mapping_error(fei->dev, tsin->back_buffer_busaddr)) { - dev_err(fei->dev, "failed to map back_buffer\n"); - ret = -EFAULT; - goto err_unmap; - } - - /* - * The pid buffer can be configured (in hw) for byte or bit - * per pid. By powers of deduction we conclude stih407 family - * is configured (at SoC design stage) for bit per pid. - */ - tsin->pid_buffer_start = kzalloc(PID_TABLE_SIZE + PID_TABLE_SIZE, GFP_KERNEL); - if (!tsin->pid_buffer_start) { - ret = -ENOMEM; - goto err_unmap; - } - - /* - * PID buffer needs to be aligned to size of the pid table - * which at bit per pid is 1024 bytes (8192 pids / 8). - * PIDF_BASE register enforces this alignment when writing - * the register. - */ - - tsin->pid_buffer_aligned = tsin->pid_buffer_start + PID_TABLE_SIZE; - - tsin->pid_buffer_aligned = PTR_ALIGN(tsin->pid_buffer_aligned, PID_TABLE_SIZE); - - tsin->pid_buffer_busaddr = dma_map_single(fei->dev, - tsin->pid_buffer_aligned, - PID_TABLE_SIZE, - DMA_BIDIRECTIONAL); - - if (dma_mapping_error(fei->dev, tsin->pid_buffer_busaddr)) { - dev_err(fei->dev, "failed to map pid_bitmap\n"); - ret = -EFAULT; - goto err_unmap; - } - - /* manage cache so pid bitmap is visible to HW */ - dma_sync_single_for_device(fei->dev, - tsin->pid_buffer_busaddr, - PID_TABLE_SIZE, - DMA_TO_DEVICE); - - snprintf(tsin_pin_name, MAX_NAME, "tsin%d-%s", tsin->tsin_id, - (tsin->serial_not_parallel ? "serial" : "parallel")); - - tsin->pstate = pinctrl_lookup_state(fei->pinctrl, tsin_pin_name); - if (IS_ERR(tsin->pstate)) { - dev_err(fei->dev, "%s: pinctrl_lookup_state couldn't find %s state\n" - , __func__, tsin_pin_name); - ret = PTR_ERR(tsin->pstate); - goto err_unmap; - } - - ret = pinctrl_select_state(fei->pinctrl, tsin->pstate); - - if (ret) { - dev_err(fei->dev, "%s: pinctrl_select_state failed\n" - , __func__); - goto err_unmap; - } - - /* Enable this input block */ - tmp = readl(fei->io + SYS_INPUT_CLKEN); - tmp |= BIT(tsin->tsin_id); - writel(tmp, fei->io + SYS_INPUT_CLKEN); - - if (tsin->serial_not_parallel) - tmp |= C8SECTPFE_SERIAL_NOT_PARALLEL; - - if (tsin->invert_ts_clk) - tmp |= C8SECTPFE_INVERT_TSCLK; - - if (tsin->async_not_sync) - tmp |= C8SECTPFE_ASYNC_NOT_SYNC; - - tmp |= C8SECTPFE_ALIGN_BYTE_SOP | C8SECTPFE_BYTE_ENDIANNESS_MSB; - - writel(tmp, fei->io + C8SECTPFE_IB_IP_FMT_CFG(tsin->tsin_id)); - - writel(C8SECTPFE_SYNC(0x9) | - C8SECTPFE_DROP(0x9) | - C8SECTPFE_TOKEN(0x47), - fei->io + C8SECTPFE_IB_SYNCLCKDRP_CFG(tsin->tsin_id)); - - writel(TS_PKT_SIZE, fei->io + C8SECTPFE_IB_PKT_LEN(tsin->tsin_id)); - - /* Place the FIFO's at the end of the irec descriptors */ - - tsin->fifo = (tsin->tsin_id * FIFO_LEN); - - writel(tsin->fifo, fei->io + C8SECTPFE_IB_BUFF_STRT(tsin->tsin_id)); - writel(tsin->fifo + FIFO_LEN - 1, - fei->io + C8SECTPFE_IB_BUFF_END(tsin->tsin_id)); - - writel(tsin->fifo, fei->io + C8SECTPFE_IB_READ_PNT(tsin->tsin_id)); - writel(tsin->fifo, fei->io + C8SECTPFE_IB_WRT_PNT(tsin->tsin_id)); - - writel(tsin->pid_buffer_busaddr, - fei->io + PIDF_BASE(tsin->tsin_id)); - - dev_dbg(fei->dev, "chan=%d PIDF_BASE=0x%x pid_bus_addr=%pad\n", - tsin->tsin_id, readl(fei->io + PIDF_BASE(tsin->tsin_id)), - &tsin->pid_buffer_busaddr); - - /* Configure and enable HW PID filtering */ - - /* - * The PID value is created by assembling the first 8 bytes of - * the TS packet into a 64-bit word in big-endian format. A - * slice of that 64-bit word is taken from - * (PID_OFFSET+PID_NUM_BITS-1) to PID_OFFSET. - */ - tmp = (C8SECTPFE_PID_ENABLE | C8SECTPFE_PID_NUMBITS(13) - | C8SECTPFE_PID_OFFSET(40)); - - writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(tsin->tsin_id)); - - dev_dbg(fei->dev, "chan=%d setting wp: %d, rp: %d, buf: %d-%d\n", - tsin->tsin_id, - readl(fei->io + C8SECTPFE_IB_WRT_PNT(tsin->tsin_id)), - readl(fei->io + C8SECTPFE_IB_READ_PNT(tsin->tsin_id)), - readl(fei->io + C8SECTPFE_IB_BUFF_STRT(tsin->tsin_id)), - readl(fei->io + C8SECTPFE_IB_BUFF_END(tsin->tsin_id))); - - /* Get base addpress of pointer record block from DMEM */ - tsin->irec = fei->io + DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + - readl(fei->io + DMA_PTRREC_BASE); - - /* fill out pointer record data structure */ - - /* advance pointer record block to our channel */ - tsin->irec += (tsin->tsin_id * DMA_PRDS_SIZE); - - writel(tsin->fifo, tsin->irec + DMA_PRDS_MEMBASE); - - writel(tsin->fifo + FIFO_LEN - 1, tsin->irec + DMA_PRDS_MEMTOP); - - writel((188 + 7)&~7, tsin->irec + DMA_PRDS_PKTSIZE); - - writel(0x1, tsin->irec + DMA_PRDS_TPENABLE); - - /* read/write pointers with physical bus address */ - - writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSBASE_TP(0)); - - tmp = tsin->back_buffer_busaddr + FEI_BUFFER_SIZE - 1; - writel(tmp, tsin->irec + DMA_PRDS_BUSTOP_TP(0)); - - writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSWP_TP(0)); - writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSRP_TP(0)); - - /* initialize tasklet */ - tasklet_setup(&tsin->tsklet, channel_swdemux_tsklet); - - return 0; - -err_unmap: - free_input_block(fei, tsin); - return ret; -} - -static irqreturn_t c8sectpfe_error_irq_handler(int irq, void *priv) -{ - struct c8sectpfei *fei = priv; - - dev_err(fei->dev, "%s: error handling not yet implemented\n" - , __func__); - - /* - * TODO FIXME we should detect some error conditions here - * and ideally do something about them! - */ - - return IRQ_HANDLED; -} - -static int c8sectpfe_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *child, *np = dev->of_node; - struct c8sectpfei *fei; - struct resource *res; - int ret, index = 0; - struct channel_info *tsin; - - /* Allocate the c8sectpfei structure */ - fei = devm_kzalloc(dev, sizeof(struct c8sectpfei), GFP_KERNEL); - if (!fei) - return -ENOMEM; - - fei->dev = dev; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "c8sectpfe"); - fei->io = devm_ioremap_resource(dev, res); - if (IS_ERR(fei->io)) - return PTR_ERR(fei->io); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "c8sectpfe-ram"); - fei->sram = devm_ioremap_resource(dev, res); - if (IS_ERR(fei->sram)) - return PTR_ERR(fei->sram); - - fei->sram_size = resource_size(res); - - fei->idle_irq = platform_get_irq_byname(pdev, "c8sectpfe-idle-irq"); - if (fei->idle_irq < 0) - return fei->idle_irq; - - fei->error_irq = platform_get_irq_byname(pdev, "c8sectpfe-error-irq"); - if (fei->error_irq < 0) - return fei->error_irq; - - platform_set_drvdata(pdev, fei); - - fei->c8sectpfeclk = devm_clk_get_enabled(dev, "c8sectpfe"); - if (IS_ERR(fei->c8sectpfeclk)) { - dev_err(dev, "Failed to enable c8sectpfe clock\n"); - return PTR_ERR(fei->c8sectpfeclk); - } - - /* to save power disable all IP's (on by default) */ - writel(0, fei->io + SYS_INPUT_CLKEN); - - /* Enable memdma clock */ - writel(MEMDMAENABLE, fei->io + SYS_OTHER_CLKEN); - - /* clear internal sram */ - memset_io(fei->sram, 0x0, fei->sram_size); - - c8sectpfe_getconfig(fei); - - ret = devm_request_irq(dev, fei->idle_irq, c8sectpfe_idle_irq_handler, - 0, "c8sectpfe-idle-irq", fei); - if (ret) { - dev_err(dev, "Can't register c8sectpfe-idle-irq IRQ.\n"); - return ret; - } - - ret = devm_request_irq(dev, fei->error_irq, - c8sectpfe_error_irq_handler, 0, - "c8sectpfe-error-irq", fei); - if (ret) { - dev_err(dev, "Can't register c8sectpfe-error-irq IRQ.\n"); - return ret; - } - - fei->tsin_count = of_get_child_count(np); - - if (fei->tsin_count > C8SECTPFE_MAX_TSIN_CHAN || - fei->tsin_count > fei->hw_stats.num_ib) { - - dev_err(dev, "More tsin declared than exist on SoC!\n"); - return -EINVAL; - } - - fei->pinctrl = devm_pinctrl_get(dev); - - if (IS_ERR(fei->pinctrl)) { - dev_err(dev, "Error getting tsin pins\n"); - return PTR_ERR(fei->pinctrl); - } - - for_each_child_of_node(np, child) { - struct device_node *i2c_bus; - - fei->channel_data[index] = devm_kzalloc(dev, - sizeof(struct channel_info), - GFP_KERNEL); - - if (!fei->channel_data[index]) { - ret = -ENOMEM; - goto err_node_put; - } - - tsin = fei->channel_data[index]; - - tsin->fei = fei; - - ret = of_property_read_u32(child, "tsin-num", &tsin->tsin_id); - if (ret) { - dev_err(&pdev->dev, "No tsin_num found\n"); - goto err_node_put; - } - - /* sanity check value */ - if (tsin->tsin_id > fei->hw_stats.num_ib) { - dev_err(&pdev->dev, - "tsin-num %d specified greater than number\n\tof input block hw in SoC! (%d)", - tsin->tsin_id, fei->hw_stats.num_ib); - ret = -EINVAL; - goto err_node_put; - } - - tsin->invert_ts_clk = of_property_read_bool(child, - "invert-ts-clk"); - - tsin->serial_not_parallel = of_property_read_bool(child, - "serial-not-parallel"); - - tsin->async_not_sync = of_property_read_bool(child, - "async-not-sync"); - - ret = of_property_read_u32(child, "dvb-card", - &tsin->dvb_card); - if (ret) { - dev_err(&pdev->dev, "No dvb-card found\n"); - goto err_node_put; - } - - i2c_bus = of_parse_phandle(child, "i2c-bus", 0); - if (!i2c_bus) { - dev_err(&pdev->dev, "No i2c-bus found\n"); - ret = -ENODEV; - goto err_node_put; - } - tsin->i2c_adapter = - of_find_i2c_adapter_by_node(i2c_bus); - if (!tsin->i2c_adapter) { - dev_err(&pdev->dev, "No i2c adapter found\n"); - of_node_put(i2c_bus); - ret = -ENODEV; - goto err_node_put; - } - of_node_put(i2c_bus); - - /* Acquire reset GPIO and activate it */ - tsin->rst_gpio = devm_fwnode_gpiod_get(dev, - of_fwnode_handle(child), - "reset", GPIOD_OUT_HIGH, - "NIM reset"); - ret = PTR_ERR_OR_ZERO(tsin->rst_gpio); - if (ret && ret != -EBUSY) { - dev_err(dev, "Can't request tsin%d reset gpio\n", - fei->channel_data[index]->tsin_id); - goto err_node_put; - } - - if (!ret) { - /* wait for the chip to reset */ - usleep_range(3500, 5000); - /* release the reset line */ - gpiod_set_value_cansleep(tsin->rst_gpio, 0); - usleep_range(3000, 5000); - } - - tsin->demux_mapping = index; - - dev_dbg(fei->dev, - "channel=%p n=%d tsin_num=%d, invert-ts-clk=%d\n\tserial-not-parallel=%d pkt-clk-valid=%d dvb-card=%d\n", - fei->channel_data[index], index, - tsin->tsin_id, tsin->invert_ts_clk, - tsin->serial_not_parallel, tsin->async_not_sync, - tsin->dvb_card); - - index++; - } - - /* Setup timer interrupt */ - timer_setup(&fei->timer, c8sectpfe_timer_interrupt, 0); - - mutex_init(&fei->lock); - - /* Get the configuration information about the tuners */ - ret = c8sectpfe_tuner_register_frontend(&fei->c8sectpfe[0], - (void *)fei, - c8sectpfe_start_feed, - c8sectpfe_stop_feed); - if (ret) { - dev_err(dev, "c8sectpfe_tuner_register_frontend failed (%d)\n", - ret); - return ret; - } - - c8sectpfe_debugfs_init(fei); - - return 0; - -err_node_put: - of_node_put(child); - return ret; -} - -static void c8sectpfe_remove(struct platform_device *pdev) -{ - struct c8sectpfei *fei = platform_get_drvdata(pdev); - struct channel_info *channel; - int i; - - wait_for_completion(&fei->fw_ack); - - c8sectpfe_tuner_unregister_frontend(fei->c8sectpfe[0], fei); - - /* - * Now loop through and un-configure each of the InputBlock resources - */ - for (i = 0; i < fei->tsin_count; i++) { - channel = fei->channel_data[i]; - free_input_block(fei, channel); - } - - c8sectpfe_debugfs_exit(fei); - - dev_info(fei->dev, "Stopping memdma SLIM core\n"); - if (readl(fei->io + DMA_CPU_RUN)) - writel(0x0, fei->io + DMA_CPU_RUN); - - /* unclock all internal IP's */ - if (readl(fei->io + SYS_INPUT_CLKEN)) - writel(0, fei->io + SYS_INPUT_CLKEN); - - if (readl(fei->io + SYS_OTHER_CLKEN)) - writel(0, fei->io + SYS_OTHER_CLKEN); -} - - -static int configure_channels(struct c8sectpfei *fei) -{ - int index = 0, ret; - struct device_node *child, *np = fei->dev->of_node; - - /* iterate round each tsin and configure memdma descriptor and IB hw */ - for_each_child_of_node(np, child) { - ret = configure_memdma_and_inputblock(fei, - fei->channel_data[index]); - if (ret) { - dev_err(fei->dev, - "configure_memdma_and_inputblock failed\n"); - of_node_put(child); - goto err_unmap; - } - index++; - } - - return 0; - -err_unmap: - while (--index >= 0) - free_input_block(fei, fei->channel_data[index]); - - return ret; -} - -static int -c8sectpfe_elf_sanity_check(struct c8sectpfei *fei, const struct firmware *fw) -{ - struct elf32_hdr *ehdr; - char class; - - if (!fw) { - dev_err(fei->dev, "failed to load %s\n", FIRMWARE_MEMDMA); - return -EINVAL; - } - - if (fw->size < sizeof(struct elf32_hdr)) { - dev_err(fei->dev, "Image is too small\n"); - return -EINVAL; - } - - ehdr = (struct elf32_hdr *)fw->data; - - /* We only support ELF32 at this point */ - class = ehdr->e_ident[EI_CLASS]; - if (class != ELFCLASS32) { - dev_err(fei->dev, "Unsupported class: %d\n", class); - return -EINVAL; - } - - if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { - dev_err(fei->dev, "Unsupported firmware endianness\n"); - return -EINVAL; - } - - if (fw->size < ehdr->e_shoff + sizeof(struct elf32_shdr)) { - dev_err(fei->dev, "Image is too small\n"); - return -EINVAL; - } - - if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) { - dev_err(fei->dev, "Image is corrupted (bad magic)\n"); - return -EINVAL; - } - - /* Check ELF magic */ - ehdr = (Elf32_Ehdr *)fw->data; - if (ehdr->e_ident[EI_MAG0] != ELFMAG0 || - ehdr->e_ident[EI_MAG1] != ELFMAG1 || - ehdr->e_ident[EI_MAG2] != ELFMAG2 || - ehdr->e_ident[EI_MAG3] != ELFMAG3) { - dev_err(fei->dev, "Invalid ELF magic\n"); - return -EINVAL; - } - - if (ehdr->e_type != ET_EXEC) { - dev_err(fei->dev, "Unsupported ELF header type\n"); - return -EINVAL; - } - - if (ehdr->e_phoff > fw->size) { - dev_err(fei->dev, "Firmware size is too small\n"); - return -EINVAL; - } - - return 0; -} - - -static void load_imem_segment(struct c8sectpfei *fei, Elf32_Phdr *phdr, - const struct firmware *fw, u8 __iomem *dest, - int seg_num) -{ - const u8 *imem_src = fw->data + phdr->p_offset; - int i; - - /* - * For IMEM segments, the segment contains 24-bit - * instructions which must be padded to 32-bit - * instructions before being written. The written - * segment is padded with NOP instructions. - */ - - dev_dbg(fei->dev, - "Loading IMEM segment %d 0x%08x\n\t (0x%x bytes) -> 0x%p (0x%x bytes)\n", - seg_num, phdr->p_paddr, phdr->p_filesz, dest, - phdr->p_memsz + phdr->p_memsz / 3); - - for (i = 0; i < phdr->p_filesz; i++) { - - writeb(readb((void __iomem *)imem_src), (void __iomem *)dest); - - /* Every 3 bytes, add an additional - * padding zero in destination */ - if (i % 3 == 2) { - dest++; - writeb(0x00, (void __iomem *)dest); - } - - dest++; - imem_src++; - } -} - -static void load_dmem_segment(struct c8sectpfei *fei, Elf32_Phdr *phdr, - const struct firmware *fw, u8 __iomem *dst, int seg_num) -{ - /* - * For DMEM segments copy the segment data from the ELF - * file and pad segment with zeroes - */ - - dev_dbg(fei->dev, - "Loading DMEM segment %d 0x%08x\n\t(0x%x bytes) -> 0x%p (0x%x bytes)\n", - seg_num, phdr->p_paddr, phdr->p_filesz, - dst, phdr->p_memsz); - - memcpy((void __force *)dst, (void *)fw->data + phdr->p_offset, - phdr->p_filesz); - - memset((void __force *)dst + phdr->p_filesz, 0, - phdr->p_memsz - phdr->p_filesz); -} - -static int load_slim_core_fw(const struct firmware *fw, struct c8sectpfei *fei) -{ - Elf32_Ehdr *ehdr; - Elf32_Phdr *phdr; - u8 __iomem *dst; - int err = 0, i; - - if (!fw || !fei) - return -EINVAL; - - ehdr = (Elf32_Ehdr *)fw->data; - phdr = (Elf32_Phdr *)(fw->data + ehdr->e_phoff); - - /* go through the available ELF segments */ - for (i = 0; i < ehdr->e_phnum; i++, phdr++) { - - /* Only consider LOAD segments */ - if (phdr->p_type != PT_LOAD) - continue; - - /* - * Check segment is contained within the fw->data buffer - */ - if (phdr->p_offset + phdr->p_filesz > fw->size) { - dev_err(fei->dev, - "Segment %d is outside of firmware file\n", i); - err = -EINVAL; - break; - } - - /* - * MEMDMA IMEM has executable flag set, otherwise load - * this segment into DMEM. - * - */ - - if (phdr->p_flags & PF_X) { - dst = (u8 __iomem *) fei->io + DMA_MEMDMA_IMEM; - /* - * The Slim ELF file uses 32-bit word addressing for - * load offsets. - */ - dst += (phdr->p_paddr & 0xFFFFF) * sizeof(unsigned int); - load_imem_segment(fei, phdr, fw, dst, i); - } else { - dst = (u8 __iomem *) fei->io + DMA_MEMDMA_DMEM; - /* - * The Slim ELF file uses 32-bit word addressing for - * load offsets. - */ - dst += (phdr->p_paddr & 0xFFFFF) * sizeof(unsigned int); - load_dmem_segment(fei, phdr, fw, dst, i); - } - } - - return err; -} - -static int load_c8sectpfe_fw(struct c8sectpfei *fei) -{ - const struct firmware *fw; - int err; - - dev_info(fei->dev, "Loading firmware: %s\n", FIRMWARE_MEMDMA); - - err = request_firmware(&fw, FIRMWARE_MEMDMA, fei->dev); - if (err) - return err; - - err = c8sectpfe_elf_sanity_check(fei, fw); - if (err) { - dev_err(fei->dev, "c8sectpfe_elf_sanity_check failed err=(%d)\n" - , err); - release_firmware(fw); - return err; - } - - err = load_slim_core_fw(fw, fei); - release_firmware(fw); - if (err) { - dev_err(fei->dev, "load_slim_core_fw failed err=(%d)\n", err); - return err; - } - - /* now the firmware is loaded configure the input blocks */ - err = configure_channels(fei); - if (err) { - dev_err(fei->dev, "configure_channels failed err=(%d)\n", err); - return err; - } - - /* - * STBus target port can access IMEM and DMEM ports - * without waiting for CPU - */ - writel(0x1, fei->io + DMA_PER_STBUS_SYNC); - - dev_info(fei->dev, "Boot the memdma SLIM core\n"); - writel(0x1, fei->io + DMA_CPU_RUN); - - atomic_set(&fei->fw_loaded, 1); - - return 0; -} - -static const struct of_device_id c8sectpfe_match[] = { - { .compatible = "st,stih407-c8sectpfe" }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, c8sectpfe_match); - -static struct platform_driver c8sectpfe_driver = { - .driver = { - .name = "c8sectpfe", - .of_match_table = c8sectpfe_match, - }, - .probe = c8sectpfe_probe, - .remove_new = c8sectpfe_remove, -}; - -module_platform_driver(c8sectpfe_driver); - -MODULE_AUTHOR("Peter Bennett <peter.bennett@st.com>"); -MODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>"); -MODULE_DESCRIPTION("C8SECTPFE STi DVB Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h deleted file mode 100644 index bf377cc82225..000000000000 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h +++ /dev/null @@ -1,287 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * c8sectpfe-core.h - C8SECTPFE STi DVB driver - * - * Copyright (c) STMicroelectronics 2015 - * - * Author:Peter Bennett <peter.bennett@st.com> - * Peter Griffin <peter.griffin@linaro.org> - * - */ -#ifndef _C8SECTPFE_CORE_H_ -#define _C8SECTPFE_CORE_H_ - -#define C8SECTPFEI_MAXCHANNEL 16 -#define C8SECTPFEI_MAXADAPTER 3 - -#define C8SECTPFE_MAX_TSIN_CHAN 8 - -struct gpio_desc; - -struct channel_info { - - int tsin_id; - bool invert_ts_clk; - bool serial_not_parallel; - bool async_not_sync; - int i2c; - int dvb_card; - - struct gpio_desc *rst_gpio; - - struct i2c_adapter *i2c_adapter; - struct i2c_adapter *tuner_i2c; - struct i2c_adapter *lnb_i2c; - struct i2c_client *i2c_client; - struct dvb_frontend *frontend; - - struct pinctrl_state *pstate; - - int demux_mapping; - int active; - - void *back_buffer_start; - void *back_buffer_aligned; - dma_addr_t back_buffer_busaddr; - - void *pid_buffer_start; - void *pid_buffer_aligned; - dma_addr_t pid_buffer_busaddr; - - unsigned long fifo; - - struct completion idle_completion; - struct tasklet_struct tsklet; - - struct c8sectpfei *fei; - void __iomem *irec; - -}; - -struct c8sectpfe_hw { - int num_ib; - int num_mib; - int num_swts; - int num_tsout; - int num_ccsc; - int num_ram; - int num_tp; -}; - -struct c8sectpfei { - - struct device *dev; - struct pinctrl *pinctrl; - - struct dentry *root; - struct debugfs_regset32 *regset; - struct completion fw_ack; - atomic_t fw_loaded; - - int tsin_count; - - struct c8sectpfe_hw hw_stats; - - struct c8sectpfe *c8sectpfe[C8SECTPFEI_MAXADAPTER]; - - int mapping[C8SECTPFEI_MAXCHANNEL]; - - struct mutex lock; - - struct timer_list timer; /* timer interrupts for outputs */ - - void __iomem *io; - void __iomem *sram; - - unsigned long sram_size; - - struct channel_info *channel_data[C8SECTPFE_MAX_TSIN_CHAN]; - - struct clk *c8sectpfeclk; - int nima_rst_gpio; - int nimb_rst_gpio; - - int idle_irq; - int error_irq; - - int global_feed_count; -}; - -/* C8SECTPFE SYS Regs list */ - -#define SYS_INPUT_ERR_STATUS 0x0 -#define SYS_OTHER_ERR_STATUS 0x8 -#define SYS_INPUT_ERR_MASK 0x10 -#define SYS_OTHER_ERR_MASK 0x18 -#define SYS_DMA_ROUTE 0x20 -#define SYS_INPUT_CLKEN 0x30 -#define IBENABLE_MASK 0x7F - -#define SYS_OTHER_CLKEN 0x38 -#define TSDMAENABLE BIT(1) -#define MEMDMAENABLE BIT(0) - -#define SYS_CFG_NUM_IB 0x200 -#define SYS_CFG_NUM_MIB 0x204 -#define SYS_CFG_NUM_SWTS 0x208 -#define SYS_CFG_NUM_TSOUT 0x20C -#define SYS_CFG_NUM_CCSC 0x210 -#define SYS_CFG_NUM_RAM 0x214 -#define SYS_CFG_NUM_TP 0x218 - -/* Input Block Regs */ - -#define C8SECTPFE_INPUTBLK_OFFSET 0x1000 -#define C8SECTPFE_CHANNEL_OFFSET(x) ((x*0x40) + C8SECTPFE_INPUTBLK_OFFSET) - -#define C8SECTPFE_IB_IP_FMT_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x00) -#define C8SECTPFE_IGNORE_ERR_AT_SOP BIT(7) -#define C8SECTPFE_IGNORE_ERR_IN_PKT BIT(6) -#define C8SECTPFE_IGNORE_ERR_IN_BYTE BIT(5) -#define C8SECTPFE_INVERT_TSCLK BIT(4) -#define C8SECTPFE_ALIGN_BYTE_SOP BIT(3) -#define C8SECTPFE_ASYNC_NOT_SYNC BIT(2) -#define C8SECTPFE_BYTE_ENDIANNESS_MSB BIT(1) -#define C8SECTPFE_SERIAL_NOT_PARALLEL BIT(0) - -#define C8SECTPFE_IB_SYNCLCKDRP_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x04) -#define C8SECTPFE_SYNC(x) (x & 0xf) -#define C8SECTPFE_DROP(x) ((x<<4) & 0xf) -#define C8SECTPFE_TOKEN(x) ((x<<8) & 0xff00) -#define C8SECTPFE_SLDENDIANNESS BIT(16) - -#define C8SECTPFE_IB_TAGBYTES_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x08) -#define C8SECTPFE_TAG_HEADER(x) (x << 16) -#define C8SECTPFE_TAG_COUNTER(x) ((x<<1) & 0x7fff) -#define C8SECTPFE_TAG_ENABLE BIT(0) - -#define C8SECTPFE_IB_PID_SET(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x0C) -#define C8SECTPFE_PID_OFFSET(x) (x & 0x3f) -#define C8SECTPFE_PID_NUMBITS(x) ((x << 6) & 0xfff) -#define C8SECTPFE_PID_ENABLE BIT(31) - -#define C8SECTPFE_IB_PKT_LEN(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x10) - -#define C8SECTPFE_IB_BUFF_STRT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x14) -#define C8SECTPFE_IB_BUFF_END(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x18) -#define C8SECTPFE_IB_READ_PNT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x1C) -#define C8SECTPFE_IB_WRT_PNT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x20) - -#define C8SECTPFE_IB_PRI_THRLD(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x24) -#define C8SECTPFE_PRI_VALUE(x) (x & 0x7fffff) -#define C8SECTPFE_PRI_LOWPRI(x) ((x & 0xf) << 24) -#define C8SECTPFE_PRI_HIGHPRI(x) ((x & 0xf) << 28) - -#define C8SECTPFE_IB_STAT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x28) -#define C8SECTPFE_STAT_FIFO_OVERFLOW(x) (x & 0x1) -#define C8SECTPFE_STAT_BUFFER_OVERFLOW(x) (x & 0x2) -#define C8SECTPFE_STAT_OUTOFORDERRP(x) (x & 0x4) -#define C8SECTPFE_STAT_PID_OVERFLOW(x) (x & 0x8) -#define C8SECTPFE_STAT_PKT_OVERFLOW(x) (x & 0x10) -#define C8SECTPFE_STAT_ERROR_PACKETS(x) ((x >> 8) & 0xf) -#define C8SECTPFE_STAT_SHORT_PACKETS(x) ((x >> 12) & 0xf) - -#define C8SECTPFE_IB_MASK(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x2C) -#define C8SECTPFE_MASK_FIFO_OVERFLOW BIT(0) -#define C8SECTPFE_MASK_BUFFER_OVERFLOW BIT(1) -#define C8SECTPFE_MASK_OUTOFORDERRP(x) BIT(2) -#define C8SECTPFE_MASK_PID_OVERFLOW(x) BIT(3) -#define C8SECTPFE_MASK_PKT_OVERFLOW(x) BIT(4) -#define C8SECTPFE_MASK_ERROR_PACKETS(x) ((x & 0xf) << 8) -#define C8SECTPFE_MASK_SHORT_PACKETS(x) ((x & 0xf) >> 12) - -#define C8SECTPFE_IB_SYS(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x30) -#define C8SECTPFE_SYS_RESET BIT(1) -#define C8SECTPFE_SYS_ENABLE BIT(0) - -/* - * Pointer record data structure required for each input block - * see Table 82 on page 167 of functional specification. - */ - -#define DMA_PRDS_MEMBASE 0x0 /* Internal sram base address */ -#define DMA_PRDS_MEMTOP 0x4 /* Internal sram top address */ - -/* - * TS packet size, including tag bytes added by input block, - * rounded up to the next multiple of 8 bytes. The packet size, - * including any tagging bytes and rounded up to the nearest - * multiple of 8 bytes must be less than 255 bytes. - */ -#define DMA_PRDS_PKTSIZE 0x8 -#define DMA_PRDS_TPENABLE 0xc - -#define TP0_OFFSET 0x10 -#define DMA_PRDS_BUSBASE_TP(x) ((0x10*x) + TP0_OFFSET) -#define DMA_PRDS_BUSTOP_TP(x) ((0x10*x) + TP0_OFFSET + 0x4) -#define DMA_PRDS_BUSWP_TP(x) ((0x10*x) + TP0_OFFSET + 0x8) -#define DMA_PRDS_BUSRP_TP(x) ((0x10*x) + TP0_OFFSET + 0xc) - -#define DMA_PRDS_SIZE (0x20) - -#define DMA_MEMDMA_OFFSET 0x4000 -#define DMA_IMEM_OFFSET 0x0 -#define DMA_DMEM_OFFSET 0x4000 -#define DMA_CPU 0x8000 -#define DMA_PER_OFFSET 0xb000 - -#define DMA_MEMDMA_DMEM (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET) -#define DMA_MEMDMA_IMEM (DMA_MEMDMA_OFFSET + DMA_IMEM_OFFSET) - -/* XP70 Slim core regs */ -#define DMA_CPU_ID (DMA_MEMDMA_OFFSET + DMA_CPU + 0x0) -#define DMA_CPU_VCR (DMA_MEMDMA_OFFSET + DMA_CPU + 0x4) -#define DMA_CPU_RUN (DMA_MEMDMA_OFFSET + DMA_CPU + 0x8) -#define DMA_CPU_CLOCKGATE (DMA_MEMDMA_OFFSET + DMA_CPU + 0xc) -#define DMA_CPU_PC (DMA_MEMDMA_OFFSET + DMA_CPU + 0x20) - -/* Enable Interrupt for a IB */ -#define DMA_PER_TPn_DREQ_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xd00) -/* Ack interrupt by setting corresponding bit */ -#define DMA_PER_TPn_DACK_SET (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xd80) -#define DMA_PER_TPn_DREQ (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xe00) -#define DMA_PER_TPn_DACK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xe80) -#define DMA_PER_DREQ_MODE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf80) -#define DMA_PER_STBUS_SYNC (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf88) -#define DMA_PER_STBUS_ACCESS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf8c) -#define DMA_PER_STBUS_ADDRESS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf90) -#define DMA_PER_IDLE_INT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfa8) -#define DMA_PER_PRIORITY (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfac) -#define DMA_PER_MAX_OPCODE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfb0) -#define DMA_PER_MAX_CHUNK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfb4) -#define DMA_PER_PAGE_SIZE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfbc) -#define DMA_PER_MBOX_STATUS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfc0) -#define DMA_PER_MBOX_SET (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfc8) -#define DMA_PER_MBOX_CLEAR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfd0) -#define DMA_PER_MBOX_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfd8) -#define DMA_PER_INJECT_PKT_SRC (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe0) -#define DMA_PER_INJECT_PKT_DEST (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe4) -#define DMA_PER_INJECT_PKT_ADDR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe8) -#define DMA_PER_INJECT_PKT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfec) -#define DMA_PER_PAT_PTR_INIT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff0) -#define DMA_PER_PAT_PTR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff4) -#define DMA_PER_SLEEP_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff8) -#define DMA_PER_SLEEP_COUNTER (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xffc) -/* #define DMA_RF_CPUREGn DMA_RFBASEADDR n=0 to 15) slim regsa */ - -/* The following are from DMA_DMEM_BaseAddress */ -#define DMA_FIRMWARE_VERSION (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x0) -#define DMA_PTRREC_BASE (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x4) -#define DMA_PTRREC_INPUT_OFFSET (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x8) -#define DMA_ERRREC_BASE (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0xc) -#define DMA_ERROR_RECORD(n) ((n*4) + DMA_ERRREC_BASE + 0x4) -#define DMA_IDLE_REQ (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x10) -#define IDLEREQ BIT(31) - -#define DMA_FIRMWARE_CONFIG (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x14) - -/* Regs for PID Filter */ - -#define PIDF_OFFSET 0x2800 -#define PIDF_BASE(n) ((n*4) + PIDF_OFFSET) -#define PIDF_LEAK_ENABLE (PIDF_OFFSET + 0x100) -#define PIDF_LEAK_STATUS (PIDF_OFFSET + 0x108) -#define PIDF_LEAK_COUNT_RESET (PIDF_OFFSET + 0x110) -#define PIDF_LEAK_COUNTER (PIDF_OFFSET + 0x114) - -#endif /* _C8SECTPFE_CORE_H_ */ diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.c deleted file mode 100644 index 301fa10f419b..000000000000 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.c +++ /dev/null @@ -1,244 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * c8sectpfe-debugfs.c - C8SECTPFE STi DVB driver - * - * Copyright (c) STMicroelectronics 2015 - * - * Author: Peter Griffin <peter.griffin@linaro.org> - * - */ -#include <linux/debugfs.h> -#include <linux/device.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/kernel.h> -#include <linux/seq_file.h> -#include <linux/slab.h> -#include <linux/types.h> - -#include "c8sectpfe-debugfs.h" - -#define dump_register(nm ...) \ -{ \ - .name = #nm, \ - .offset = nm, \ -} - -static const struct debugfs_reg32 fei_sys_regs[] = { - dump_register(SYS_INPUT_ERR_STATUS), - dump_register(SYS_OTHER_ERR_STATUS), - dump_register(SYS_INPUT_ERR_MASK), - dump_register(SYS_DMA_ROUTE), - dump_register(SYS_INPUT_CLKEN), - dump_register(IBENABLE_MASK), - dump_register(SYS_OTHER_CLKEN), - dump_register(SYS_CFG_NUM_IB), - dump_register(SYS_CFG_NUM_MIB), - dump_register(SYS_CFG_NUM_SWTS), - dump_register(SYS_CFG_NUM_TSOUT), - dump_register(SYS_CFG_NUM_CCSC), - dump_register(SYS_CFG_NUM_RAM), - dump_register(SYS_CFG_NUM_TP), - - dump_register(C8SECTPFE_IB_IP_FMT_CFG(0)), - dump_register(C8SECTPFE_IB_TAGBYTES_CFG(0)), - dump_register(C8SECTPFE_IB_PID_SET(0)), - dump_register(C8SECTPFE_IB_PKT_LEN(0)), - dump_register(C8SECTPFE_IB_BUFF_STRT(0)), - dump_register(C8SECTPFE_IB_BUFF_END(0)), - dump_register(C8SECTPFE_IB_READ_PNT(0)), - dump_register(C8SECTPFE_IB_WRT_PNT(0)), - dump_register(C8SECTPFE_IB_PRI_THRLD(0)), - dump_register(C8SECTPFE_IB_STAT(0)), - dump_register(C8SECTPFE_IB_MASK(0)), - dump_register(C8SECTPFE_IB_SYS(0)), - - dump_register(C8SECTPFE_IB_IP_FMT_CFG(1)), - dump_register(C8SECTPFE_IB_TAGBYTES_CFG(1)), - dump_register(C8SECTPFE_IB_PID_SET(1)), - dump_register(C8SECTPFE_IB_PKT_LEN(1)), - dump_register(C8SECTPFE_IB_BUFF_STRT(1)), - dump_register(C8SECTPFE_IB_BUFF_END(1)), - dump_register(C8SECTPFE_IB_READ_PNT(1)), - dump_register(C8SECTPFE_IB_WRT_PNT(1)), - dump_register(C8SECTPFE_IB_PRI_THRLD(1)), - dump_register(C8SECTPFE_IB_STAT(1)), - dump_register(C8SECTPFE_IB_MASK(1)), - dump_register(C8SECTPFE_IB_SYS(1)), - - dump_register(C8SECTPFE_IB_IP_FMT_CFG(2)), - dump_register(C8SECTPFE_IB_TAGBYTES_CFG(2)), - dump_register(C8SECTPFE_IB_PID_SET(2)), - dump_register(C8SECTPFE_IB_PKT_LEN(2)), - dump_register(C8SECTPFE_IB_BUFF_STRT(2)), - dump_register(C8SECTPFE_IB_BUFF_END(2)), - dump_register(C8SECTPFE_IB_READ_PNT(2)), - dump_register(C8SECTPFE_IB_WRT_PNT(2)), - dump_register(C8SECTPFE_IB_PRI_THRLD(2)), - dump_register(C8SECTPFE_IB_STAT(2)), - dump_register(C8SECTPFE_IB_MASK(2)), - dump_register(C8SECTPFE_IB_SYS(2)), - - dump_register(C8SECTPFE_IB_IP_FMT_CFG(3)), - dump_register(C8SECTPFE_IB_TAGBYTES_CFG(3)), - dump_register(C8SECTPFE_IB_PID_SET(3)), - dump_register(C8SECTPFE_IB_PKT_LEN(3)), - dump_register(C8SECTPFE_IB_BUFF_STRT(3)), - dump_register(C8SECTPFE_IB_BUFF_END(3)), - dump_register(C8SECTPFE_IB_READ_PNT(3)), - dump_register(C8SECTPFE_IB_WRT_PNT(3)), - dump_register(C8SECTPFE_IB_PRI_THRLD(3)), - dump_register(C8SECTPFE_IB_STAT(3)), - dump_register(C8SECTPFE_IB_MASK(3)), - dump_register(C8SECTPFE_IB_SYS(3)), - - dump_register(C8SECTPFE_IB_IP_FMT_CFG(4)), - dump_register(C8SECTPFE_IB_TAGBYTES_CFG(4)), - dump_register(C8SECTPFE_IB_PID_SET(4)), - dump_register(C8SECTPFE_IB_PKT_LEN(4)), - dump_register(C8SECTPFE_IB_BUFF_STRT(4)), - dump_register(C8SECTPFE_IB_BUFF_END(4)), - dump_register(C8SECTPFE_IB_READ_PNT(4)), - dump_register(C8SECTPFE_IB_WRT_PNT(4)), - dump_register(C8SECTPFE_IB_PRI_THRLD(4)), - dump_register(C8SECTPFE_IB_STAT(4)), - dump_register(C8SECTPFE_IB_MASK(4)), - dump_register(C8SECTPFE_IB_SYS(4)), - - dump_register(C8SECTPFE_IB_IP_FMT_CFG(5)), - dump_register(C8SECTPFE_IB_TAGBYTES_CFG(5)), - dump_register(C8SECTPFE_IB_PID_SET(5)), - dump_register(C8SECTPFE_IB_PKT_LEN(5)), - dump_register(C8SECTPFE_IB_BUFF_STRT(5)), - dump_register(C8SECTPFE_IB_BUFF_END(5)), - dump_register(C8SECTPFE_IB_READ_PNT(5)), - dump_register(C8SECTPFE_IB_WRT_PNT(5)), - dump_register(C8SECTPFE_IB_PRI_THRLD(5)), - dump_register(C8SECTPFE_IB_STAT(5)), - dump_register(C8SECTPFE_IB_MASK(5)), - dump_register(C8SECTPFE_IB_SYS(5)), - - dump_register(C8SECTPFE_IB_IP_FMT_CFG(6)), - dump_register(C8SECTPFE_IB_TAGBYTES_CFG(6)), - dump_register(C8SECTPFE_IB_PID_SET(6)), - dump_register(C8SECTPFE_IB_PKT_LEN(6)), - dump_register(C8SECTPFE_IB_BUFF_STRT(6)), - dump_register(C8SECTPFE_IB_BUFF_END(6)), - dump_register(C8SECTPFE_IB_READ_PNT(6)), - dump_register(C8SECTPFE_IB_WRT_PNT(6)), - dump_register(C8SECTPFE_IB_PRI_THRLD(6)), - dump_register(C8SECTPFE_IB_STAT(6)), - dump_register(C8SECTPFE_IB_MASK(6)), - dump_register(C8SECTPFE_IB_SYS(6)), - - dump_register(DMA_CPU_ID), - dump_register(DMA_CPU_VCR), - dump_register(DMA_CPU_RUN), - dump_register(DMA_CPU_PC), - - dump_register(DMA_PER_TPn_DREQ_MASK), - dump_register(DMA_PER_TPn_DACK_SET), - dump_register(DMA_PER_TPn_DREQ), - dump_register(DMA_PER_TPn_DACK), - dump_register(DMA_PER_DREQ_MODE), - dump_register(DMA_PER_STBUS_SYNC), - dump_register(DMA_PER_STBUS_ACCESS), - dump_register(DMA_PER_STBUS_ADDRESS), - dump_register(DMA_PER_IDLE_INT), - dump_register(DMA_PER_PRIORITY), - dump_register(DMA_PER_MAX_OPCODE), - dump_register(DMA_PER_MAX_CHUNK), - dump_register(DMA_PER_PAGE_SIZE), - dump_register(DMA_PER_MBOX_STATUS), - dump_register(DMA_PER_MBOX_SET), - dump_register(DMA_PER_MBOX_CLEAR), - dump_register(DMA_PER_MBOX_MASK), - dump_register(DMA_PER_INJECT_PKT_SRC), - dump_register(DMA_PER_INJECT_PKT_DEST), - dump_register(DMA_PER_INJECT_PKT_ADDR), - dump_register(DMA_PER_INJECT_PKT), - dump_register(DMA_PER_PAT_PTR_INIT), - dump_register(DMA_PER_PAT_PTR), - dump_register(DMA_PER_SLEEP_MASK), - dump_register(DMA_PER_SLEEP_COUNTER), - - dump_register(DMA_FIRMWARE_VERSION), - dump_register(DMA_PTRREC_BASE), - dump_register(DMA_PTRREC_INPUT_OFFSET), - dump_register(DMA_ERRREC_BASE), - - dump_register(DMA_ERROR_RECORD(0)), - dump_register(DMA_ERROR_RECORD(1)), - dump_register(DMA_ERROR_RECORD(2)), - dump_register(DMA_ERROR_RECORD(3)), - dump_register(DMA_ERROR_RECORD(4)), - dump_register(DMA_ERROR_RECORD(5)), - dump_register(DMA_ERROR_RECORD(6)), - dump_register(DMA_ERROR_RECORD(7)), - dump_register(DMA_ERROR_RECORD(8)), - dump_register(DMA_ERROR_RECORD(9)), - dump_register(DMA_ERROR_RECORD(10)), - dump_register(DMA_ERROR_RECORD(11)), - dump_register(DMA_ERROR_RECORD(12)), - dump_register(DMA_ERROR_RECORD(13)), - dump_register(DMA_ERROR_RECORD(14)), - dump_register(DMA_ERROR_RECORD(15)), - dump_register(DMA_ERROR_RECORD(16)), - dump_register(DMA_ERROR_RECORD(17)), - dump_register(DMA_ERROR_RECORD(18)), - dump_register(DMA_ERROR_RECORD(19)), - dump_register(DMA_ERROR_RECORD(20)), - dump_register(DMA_ERROR_RECORD(21)), - dump_register(DMA_ERROR_RECORD(22)), - - dump_register(DMA_IDLE_REQ), - dump_register(DMA_FIRMWARE_CONFIG), - - dump_register(PIDF_BASE(0)), - dump_register(PIDF_BASE(1)), - dump_register(PIDF_BASE(2)), - dump_register(PIDF_BASE(3)), - dump_register(PIDF_BASE(4)), - dump_register(PIDF_BASE(5)), - dump_register(PIDF_BASE(6)), - dump_register(PIDF_BASE(7)), - dump_register(PIDF_BASE(8)), - dump_register(PIDF_BASE(9)), - dump_register(PIDF_BASE(10)), - dump_register(PIDF_BASE(11)), - dump_register(PIDF_BASE(12)), - dump_register(PIDF_BASE(13)), - dump_register(PIDF_BASE(14)), - dump_register(PIDF_BASE(15)), - dump_register(PIDF_BASE(16)), - dump_register(PIDF_BASE(17)), - dump_register(PIDF_BASE(18)), - dump_register(PIDF_BASE(19)), - dump_register(PIDF_BASE(20)), - dump_register(PIDF_BASE(21)), - dump_register(PIDF_BASE(22)), - dump_register(PIDF_LEAK_ENABLE), - dump_register(PIDF_LEAK_STATUS), - dump_register(PIDF_LEAK_COUNT_RESET), - dump_register(PIDF_LEAK_COUNTER), -}; - -void c8sectpfe_debugfs_init(struct c8sectpfei *fei) -{ - fei->regset = devm_kzalloc(fei->dev, sizeof(*fei->regset), GFP_KERNEL); - if (!fei->regset) - return; - - fei->regset->regs = fei_sys_regs; - fei->regset->nregs = ARRAY_SIZE(fei_sys_regs); - fei->regset->base = fei->io; - - fei->root = debugfs_create_dir("c8sectpfe", NULL); - debugfs_create_regset32("registers", S_IRUGO, fei->root, fei->regset); -} - -void c8sectpfe_debugfs_exit(struct c8sectpfei *fei) -{ - debugfs_remove_recursive(fei->root); - fei->root = NULL; -} diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h deleted file mode 100644 index 8e1bfd860524..000000000000 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h +++ /dev/null @@ -1,23 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * c8sectpfe-debugfs.h - C8SECTPFE STi DVB driver debugfs header - * - * Copyright (c) STMicroelectronics 2015 - * - * Authors: Peter Griffin <peter.griffin@linaro.org> - */ - -#ifndef __C8SECTPFE_DEBUG_H -#define __C8SECTPFE_DEBUG_H - -#include "c8sectpfe-core.h" - -#if defined(CONFIG_DEBUG_FS) -void c8sectpfe_debugfs_init(struct c8sectpfei *); -void c8sectpfe_debugfs_exit(struct c8sectpfei *); -#else -static inline void c8sectpfe_debugfs_init(struct c8sectpfei *) {}; -static inline void c8sectpfe_debugfs_exit(struct c8sectpfei *) {}; -#endif - -#endif /* __C8SECTPFE_DEBUG_H */ diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.c deleted file mode 100644 index feb48cb546d7..000000000000 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.c +++ /dev/null @@ -1,235 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * c8sectpfe-dvb.c - C8SECTPFE STi DVB driver - * - * Copyright (c) STMicroelectronics 2015 - * - * Author Peter Griffin <peter.griffin@linaro.org> - * - */ -#include <linux/completion.h> -#include <linux/delay.h> -#include <linux/i2c.h> -#include <linux/interrupt.h> - -#include <dt-bindings/media/c8sectpfe.h> - -#include "c8sectpfe-common.h" -#include "c8sectpfe-core.h" -#include "c8sectpfe-dvb.h" - -#include "dvb-pll.h" -#include "lnbh24.h" -#include "stv0367.h" -#include "stv0367_priv.h" -#include "stv6110x.h" -#include "stv090x.h" -#include "tda18212.h" - -static inline const char *dvb_card_str(unsigned int c) -{ - switch (c) { - case STV0367_TDA18212_NIMA_1: return "STV0367_TDA18212_NIMA_1"; - case STV0367_TDA18212_NIMA_2: return "STV0367_TDA18212_NIMA_2"; - case STV0367_TDA18212_NIMB_1: return "STV0367_TDA18212_NIMB_1"; - case STV0367_TDA18212_NIMB_2: return "STV0367_TDA18212_NIMB_2"; - case STV0903_6110_LNB24_NIMA: return "STV0903_6110_LNB24_NIMA"; - case STV0903_6110_LNB24_NIMB: return "STV0903_6110_LNB24_NIMB"; - default: return "unknown dvb frontend card"; - } -} - -static struct stv090x_config stv090x_config = { - .device = STV0903, - .demod_mode = STV090x_SINGLE, - .clk_mode = STV090x_CLK_EXT, - .xtal = 16000000, - .address = 0x69, - - .ts1_mode = STV090x_TSMODE_SERIAL_CONTINUOUS, - .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS, - - .repeater_level = STV090x_RPTLEVEL_64, - - .tuner_init = NULL, - .tuner_set_mode = NULL, - .tuner_set_frequency = NULL, - .tuner_get_frequency = NULL, - .tuner_set_bandwidth = NULL, - .tuner_get_bandwidth = NULL, - .tuner_set_bbgain = NULL, - .tuner_get_bbgain = NULL, - .tuner_set_refclk = NULL, - .tuner_get_status = NULL, -}; - -static struct stv6110x_config stv6110x_config = { - .addr = 0x60, - .refclk = 16000000, -}; - -#define NIMA 0 -#define NIMB 1 - -static struct stv0367_config stv0367_tda18212_config[] = { - { - .demod_address = 0x1c, - .xtal = 16000000, - .if_khz = 4500, - .if_iq_mode = FE_TER_NORMAL_IF_TUNER, - .ts_mode = STV0367_SERIAL_PUNCT_CLOCK, - .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT, - }, { - .demod_address = 0x1d, - .xtal = 16000000, - .if_khz = 4500, - .if_iq_mode = FE_TER_NORMAL_IF_TUNER, - .ts_mode = STV0367_SERIAL_PUNCT_CLOCK, - .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT, - }, { - .demod_address = 0x1e, - .xtal = 16000000, - .if_khz = 4500, - .if_iq_mode = FE_TER_NORMAL_IF_TUNER, - .ts_mode = STV0367_SERIAL_PUNCT_CLOCK, - .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT, - }, -}; - -static struct tda18212_config tda18212_conf = { - .if_dvbt_6 = 4150, - .if_dvbt_7 = 4150, - .if_dvbt_8 = 4500, - .if_dvbc = 5000, -}; - -int c8sectpfe_frontend_attach(struct dvb_frontend **fe, - struct c8sectpfe *c8sectpfe, - struct channel_info *tsin, int chan_num) -{ - struct tda18212_config *tda18212; - const struct stv6110x_devctl *fe2; - struct i2c_client *client; - struct i2c_board_info tda18212_info = { - .type = "tda18212", - .addr = 0x60, - }; - - if (!tsin) - return -EINVAL; - - switch (tsin->dvb_card) { - - case STV0367_TDA18212_NIMA_1: - case STV0367_TDA18212_NIMA_2: - case STV0367_TDA18212_NIMB_1: - case STV0367_TDA18212_NIMB_2: - if (tsin->dvb_card == STV0367_TDA18212_NIMA_1) - *fe = dvb_attach(stv0367ter_attach, - &stv0367_tda18212_config[0], - tsin->i2c_adapter); - else if (tsin->dvb_card == STV0367_TDA18212_NIMB_1) - *fe = dvb_attach(stv0367ter_attach, - &stv0367_tda18212_config[1], - tsin->i2c_adapter); - else - *fe = dvb_attach(stv0367ter_attach, - &stv0367_tda18212_config[2], - tsin->i2c_adapter); - - if (!*fe) { - dev_err(c8sectpfe->device, - "%s: stv0367ter_attach failed for NIM card %s\n" - , __func__, dvb_card_str(tsin->dvb_card)); - return -ENODEV; - } - - /* - * init the demod so that i2c gate_ctrl - * to the tuner works correctly - */ - (*fe)->ops.init(*fe); - - /* Allocate the tda18212 structure */ - tda18212 = devm_kzalloc(c8sectpfe->device, - sizeof(struct tda18212_config), - GFP_KERNEL); - if (!tda18212) { - dev_err(c8sectpfe->device, - "%s: devm_kzalloc failed\n", __func__); - return -ENOMEM; - } - - memcpy(tda18212, &tda18212_conf, - sizeof(struct tda18212_config)); - - tda18212->fe = (*fe); - - tda18212_info.platform_data = tda18212; - - /* attach tuner */ - request_module("tda18212"); - client = i2c_new_client_device(tsin->i2c_adapter, - &tda18212_info); - if (!i2c_client_has_driver(client)) { - dvb_frontend_detach(*fe); - return -ENODEV; - } - - if (!try_module_get(client->dev.driver->owner)) { - i2c_unregister_device(client); - dvb_frontend_detach(*fe); - return -ENODEV; - } - - tsin->i2c_client = client; - - break; - - case STV0903_6110_LNB24_NIMA: - *fe = dvb_attach(stv090x_attach, &stv090x_config, - tsin->i2c_adapter, STV090x_DEMODULATOR_0); - if (!*fe) { - dev_err(c8sectpfe->device, "%s: stv090x_attach failed\n" - "\tfor NIM card %s\n", - __func__, dvb_card_str(tsin->dvb_card)); - return -ENODEV; - } - - fe2 = dvb_attach(stv6110x_attach, *fe, - &stv6110x_config, tsin->i2c_adapter); - if (!fe2) { - dev_err(c8sectpfe->device, - "%s: stv6110x_attach failed for NIM card %s\n" - , __func__, dvb_card_str(tsin->dvb_card)); - return -ENODEV; - } - - stv090x_config.tuner_init = fe2->tuner_init; - stv090x_config.tuner_set_mode = fe2->tuner_set_mode; - stv090x_config.tuner_set_frequency = fe2->tuner_set_frequency; - stv090x_config.tuner_get_frequency = fe2->tuner_get_frequency; - stv090x_config.tuner_set_bandwidth = fe2->tuner_set_bandwidth; - stv090x_config.tuner_get_bandwidth = fe2->tuner_get_bandwidth; - stv090x_config.tuner_set_bbgain = fe2->tuner_set_bbgain; - stv090x_config.tuner_get_bbgain = fe2->tuner_get_bbgain; - stv090x_config.tuner_set_refclk = fe2->tuner_set_refclk; - stv090x_config.tuner_get_status = fe2->tuner_get_status; - - dvb_attach(lnbh24_attach, *fe, tsin->i2c_adapter, 0, 0, 0x9); - break; - - default: - dev_err(c8sectpfe->device, - "%s: DVB frontend card %s not yet supported\n", - __func__, dvb_card_str(tsin->dvb_card)); - return -ENODEV; - } - - (*fe)->id = chan_num; - - dev_info(c8sectpfe->device, - "DVB frontend card %s successfully attached", - dvb_card_str(tsin->dvb_card)); - return 0; -} diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.h deleted file mode 100644 index 3d87a9ae8702..000000000000 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.h +++ /dev/null @@ -1,17 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * c8sectpfe-common.h - C8SECTPFE STi DVB driver - * - * Copyright (c) STMicroelectronics 2015 - * - * Author: Peter Griffin <peter.griffin@linaro.org> - * - */ -#ifndef _C8SECTPFE_DVB_H_ -#define _C8SECTPFE_DVB_H_ - -int c8sectpfe_frontend_attach(struct dvb_frontend **fe, - struct c8sectpfe *c8sectpfe, struct channel_info *tsin, - int chan_num); - -#endif diff --git a/drivers/media/platform/st/sti/delta/delta-debug.c b/drivers/media/platform/st/sti/delta/delta-debug.c index 4b2eb6b63aa2..6acf46913cda 100644 --- a/drivers/media/platform/st/sti/delta/delta-debug.c +++ b/drivers/media/platform/st/sti/delta/delta-debug.c @@ -16,14 +16,14 @@ char *delta_streaminfo_str(struct delta_streaminfo *s, char *str, return NULL; snprintf(str, len, - "%4.4s %dx%d %s %s dpb=%d %s %s %s%dx%d@(%d,%d) %s%d/%d", + "%4.4s %dx%d %s %s dpb=%d %s %s %s(%d,%d)/%ux%u %s%d/%d", (char *)&s->streamformat, s->width, s->height, s->profile, s->level, s->dpb, (s->field == V4L2_FIELD_NONE) ? "progressive" : "interlaced", s->other, s->flags & DELTA_STREAMINFO_FLAG_CROP ? "crop=" : "", - s->crop.width, s->crop.height, s->crop.left, s->crop.top, + s->crop.width, s->crop.height, s->flags & DELTA_STREAMINFO_FLAG_PIXELASPECT ? "par=" : "", s->pixelaspect.numerator, s->pixelaspect.denominator); @@ -38,13 +38,13 @@ char *delta_frameinfo_str(struct delta_frameinfo *f, char *str, return NULL; snprintf(str, len, - "%4.4s %dx%d aligned %dx%d %s %s%dx%d@(%d,%d) %s%d/%d", + "%4.4s %dx%d aligned %dx%d %s %s(%d,%d)/%ux%u %s%d/%d", (char *)&f->pixelformat, f->width, f->height, f->aligned_width, f->aligned_height, (f->field == V4L2_FIELD_NONE) ? "progressive" : "interlaced", f->flags & DELTA_STREAMINFO_FLAG_CROP ? "crop=" : "", - f->crop.width, f->crop.height, f->crop.left, f->crop.top, + f->crop.width, f->crop.height, f->flags & DELTA_STREAMINFO_FLAG_PIXELASPECT ? "par=" : "", f->pixelaspect.numerator, f->pixelaspect.denominator); diff --git a/drivers/media/platform/st/sti/delta/delta-mjpeg-dec.c b/drivers/media/platform/st/sti/delta/delta-mjpeg-dec.c index 0533d4a083d2..a078f1107300 100644 --- a/drivers/media/platform/st/sti/delta/delta-mjpeg-dec.c +++ b/drivers/media/platform/st/sti/delta/delta-mjpeg-dec.c @@ -239,7 +239,7 @@ static int delta_mjpeg_ipc_open(struct delta_ctx *pctx) return 0; } -static int delta_mjpeg_ipc_decode(struct delta_ctx *pctx, struct delta_au *au) +static int delta_mjpeg_ipc_decode(struct delta_ctx *pctx, dma_addr_t pstart, dma_addr_t pend) { struct delta_dev *delta = pctx->dev; struct delta_mjpeg_ctx *ctx = to_ctx(pctx); @@ -256,8 +256,8 @@ static int delta_mjpeg_ipc_decode(struct delta_ctx *pctx, struct delta_au *au) memset(params, 0, sizeof(*params)); - params->picture_start_addr_p = (u32)(au->paddr); - params->picture_end_addr_p = (u32)(au->paddr + au->size - 1); + params->picture_start_addr_p = pstart; + params->picture_end_addr_p = pend; /* * !WARNING! @@ -374,12 +374,14 @@ static int delta_mjpeg_decode(struct delta_ctx *pctx, struct delta_au *pau) struct delta_dev *delta = pctx->dev; struct delta_mjpeg_ctx *ctx = to_ctx(pctx); int ret; - struct delta_au au = *pau; + void *au_vaddr = pau->vaddr; + dma_addr_t au_dma = pau->paddr; + size_t au_size = pau->size; unsigned int data_offset = 0; struct mjpeg_header *header = &ctx->header_struct; if (!ctx->header) { - ret = delta_mjpeg_read_header(pctx, au.vaddr, au.size, + ret = delta_mjpeg_read_header(pctx, au_vaddr, au_size, header, &data_offset); if (ret) { pctx->stream_errors++; @@ -405,17 +407,17 @@ static int delta_mjpeg_decode(struct delta_ctx *pctx, struct delta_au *pau) goto err; } - ret = delta_mjpeg_read_header(pctx, au.vaddr, au.size, + ret = delta_mjpeg_read_header(pctx, au_vaddr, au_size, ctx->header, &data_offset); if (ret) { pctx->stream_errors++; goto err; } - au.paddr += data_offset; - au.vaddr += data_offset; + au_dma += data_offset; + au_vaddr += data_offset; - ret = delta_mjpeg_ipc_decode(pctx, &au); + ret = delta_mjpeg_ipc_decode(pctx, au_dma, au_dma + au_size - 1); if (ret) goto err; diff --git a/drivers/media/platform/st/sti/delta/delta-v4l2.c b/drivers/media/platform/st/sti/delta/delta-v4l2.c index da402d1e9171..6c1a53c771f7 100644 --- a/drivers/media/platform/st/sti/delta/delta-v4l2.c +++ b/drivers/media/platform/st/sti/delta/delta-v4l2.c @@ -24,7 +24,11 @@ #define DELTA_PREFIX "[---:----]" -#define to_ctx(__fh) container_of(__fh, struct delta_ctx, fh) +static inline struct delta_ctx *file_to_ctx(struct file *filp) +{ + return container_of(file_to_v4l2_fh(filp), struct delta_ctx, fh); +} + #define to_au(__vbuf) container_of(__vbuf, struct delta_au, vbuf) #define to_frame(__vbuf) container_of(__vbuf, struct delta_frame, vbuf) @@ -382,7 +386,7 @@ static int delta_open_decoder(struct delta_ctx *ctx, u32 streamformat, static int delta_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct delta_ctx *ctx = to_ctx(file->private_data); + struct delta_ctx *ctx = file_to_ctx(file); struct delta_dev *delta = ctx->dev; strscpy(cap->driver, DELTA_NAME, sizeof(cap->driver)); @@ -396,7 +400,7 @@ static int delta_querycap(struct file *file, void *priv, static int delta_enum_fmt_stream(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - struct delta_ctx *ctx = to_ctx(file->private_data); + struct delta_ctx *ctx = file_to_ctx(file); struct delta_dev *delta = ctx->dev; if (unlikely(f->index >= delta->nb_of_streamformats)) @@ -410,7 +414,7 @@ static int delta_enum_fmt_stream(struct file *file, void *priv, static int delta_enum_fmt_frame(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - struct delta_ctx *ctx = to_ctx(file->private_data); + struct delta_ctx *ctx = file_to_ctx(file); struct delta_dev *delta = ctx->dev; if (unlikely(f->index >= delta->nb_of_pixelformats)) @@ -424,7 +428,7 @@ static int delta_enum_fmt_frame(struct file *file, void *priv, static int delta_g_fmt_stream(struct file *file, void *fh, struct v4l2_format *f) { - struct delta_ctx *ctx = to_ctx(file->private_data); + struct delta_ctx *ctx = file_to_ctx(file); struct delta_dev *delta = ctx->dev; struct v4l2_pix_format *pix = &f->fmt.pix; struct delta_streaminfo *streaminfo = &ctx->streaminfo; @@ -452,7 +456,7 @@ static int delta_g_fmt_stream(struct file *file, void *fh, static int delta_g_fmt_frame(struct file *file, void *fh, struct v4l2_format *f) { - struct delta_ctx *ctx = to_ctx(file->private_data); + struct delta_ctx *ctx = file_to_ctx(file); struct delta_dev *delta = ctx->dev; struct v4l2_pix_format *pix = &f->fmt.pix; struct delta_frameinfo *frameinfo = &ctx->frameinfo; @@ -491,7 +495,7 @@ static int delta_g_fmt_frame(struct file *file, void *fh, struct v4l2_format *f) static int delta_try_fmt_stream(struct file *file, void *priv, struct v4l2_format *f) { - struct delta_ctx *ctx = to_ctx(file->private_data); + struct delta_ctx *ctx = file_to_ctx(file); struct delta_dev *delta = ctx->dev; struct v4l2_pix_format *pix = &f->fmt.pix; u32 streamformat = pix->pixelformat; @@ -545,7 +549,7 @@ static int delta_try_fmt_stream(struct file *file, void *priv, static int delta_try_fmt_frame(struct file *file, void *priv, struct v4l2_format *f) { - struct delta_ctx *ctx = to_ctx(file->private_data); + struct delta_ctx *ctx = file_to_ctx(file); struct delta_dev *delta = ctx->dev; struct v4l2_pix_format *pix = &f->fmt.pix; u32 pixelformat = pix->pixelformat; @@ -605,7 +609,7 @@ static int delta_try_fmt_frame(struct file *file, void *priv, static int delta_s_fmt_stream(struct file *file, void *fh, struct v4l2_format *f) { - struct delta_ctx *ctx = to_ctx(file->private_data); + struct delta_ctx *ctx = file_to_ctx(file); struct delta_dev *delta = ctx->dev; struct vb2_queue *vq; struct v4l2_pix_format *pix = &f->fmt.pix; @@ -641,7 +645,7 @@ static int delta_s_fmt_stream(struct file *file, void *fh, static int delta_s_fmt_frame(struct file *file, void *fh, struct v4l2_format *f) { - struct delta_ctx *ctx = to_ctx(file->private_data); + struct delta_ctx *ctx = file_to_ctx(file); struct delta_dev *delta = ctx->dev; const struct delta_dec *dec = ctx->dec; struct v4l2_pix_format *pix = &f->fmt.pix; @@ -721,7 +725,7 @@ static int delta_s_fmt_frame(struct file *file, void *fh, struct v4l2_format *f) static int delta_g_selection(struct file *file, void *fh, struct v4l2_selection *s) { - struct delta_ctx *ctx = to_ctx(fh); + struct delta_ctx *ctx = file_to_ctx(file); struct delta_frameinfo *frameinfo = &ctx->frameinfo; struct v4l2_rect crop; @@ -803,7 +807,7 @@ static int delta_try_decoder_cmd(struct file *file, void *fh, return 0; } -static int delta_decoder_stop_cmd(struct delta_ctx *ctx, void *fh) +static int delta_decoder_stop_cmd(struct delta_ctx *ctx) { const struct delta_dec *dec = ctx->dec; struct delta_dev *delta = ctx->dev; @@ -866,14 +870,14 @@ delay_eos: static int delta_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) { - struct delta_ctx *ctx = to_ctx(fh); + struct delta_ctx *ctx = file_to_ctx(file); int ret = 0; ret = delta_try_decoder_cmd(file, fh, cmd); if (ret) return ret; - return delta_decoder_stop_cmd(ctx, fh); + return delta_decoder_stop_cmd(ctx); } static int delta_subscribe_event(struct v4l2_fh *fh, @@ -1559,8 +1563,6 @@ static const struct vb2_ops delta_vb2_au_ops = { .queue_setup = delta_vb2_au_queue_setup, .buf_prepare = delta_vb2_au_prepare, .buf_queue = delta_vb2_au_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = delta_vb2_au_start_streaming, .stop_streaming = delta_vb2_au_stop_streaming, }; @@ -1570,8 +1572,6 @@ static const struct vb2_ops delta_vb2_frame_ops = { .buf_prepare = delta_vb2_frame_prepare, .buf_finish = delta_vb2_frame_finish, .buf_queue = delta_vb2_frame_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .stop_streaming = delta_vb2_frame_stop_streaming, }; @@ -1637,8 +1637,7 @@ static int delta_open(struct file *file) ctx->dev = delta; v4l2_fh_init(&ctx->fh, video_devdata(file)); - file->private_data = &ctx->fh; - v4l2_fh_add(&ctx->fh); + v4l2_fh_add(&ctx->fh, file); INIT_WORK(&ctx->run_work, delta_run_work); mutex_init(&ctx->lock); @@ -1683,7 +1682,7 @@ static int delta_open(struct file *file) return 0; err_fh_del: - v4l2_fh_del(&ctx->fh); + v4l2_fh_del(&ctx->fh, file); v4l2_fh_exit(&ctx->fh); kfree(ctx); err: @@ -1694,7 +1693,7 @@ err: static int delta_release(struct file *file) { - struct delta_ctx *ctx = to_ctx(file->private_data); + struct delta_ctx *ctx = file_to_ctx(file); struct delta_dev *delta = ctx->dev; const struct delta_dec *dec = ctx->dec; @@ -1711,7 +1710,7 @@ static int delta_release(struct file *file) v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); - v4l2_fh_del(&ctx->fh); + v4l2_fh_del(&ctx->fh, file); v4l2_fh_exit(&ctx->fh); /* disable ST231 clocks */ @@ -1954,7 +1953,7 @@ MODULE_DEVICE_TABLE(of, delta_match_types); static struct platform_driver delta_driver = { .probe = delta_probe, - .remove_new = delta_remove, + .remove = delta_remove, .driver = { .name = DELTA_NAME, .of_match_table = delta_match_types, diff --git a/drivers/media/platform/st/sti/hva/hva-v4l2.c b/drivers/media/platform/st/sti/hva/hva-v4l2.c index 161a5c0fbc4e..3581b73a99b8 100644 --- a/drivers/media/platform/st/sti/hva/hva-v4l2.c +++ b/drivers/media/platform/st/sti/hva/hva-v4l2.c @@ -36,7 +36,10 @@ #define to_type_str(type) (type == V4L2_BUF_TYPE_VIDEO_OUTPUT ? \ "frame" : "stream") -#define fh_to_ctx(f) (container_of(f, struct hva_ctx, fh)) +static inline struct hva_ctx *file_to_ctx(struct file *filp) +{ + return container_of(file_to_v4l2_fh(filp), struct hva_ctx, fh); +} /* registry of available encoders */ static const struct hva_enc *hva_encoders[] = { @@ -254,7 +257,7 @@ static void hva_dbg_summary(struct hva_ctx *ctx) static int hva_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_ctx *ctx = file_to_ctx(file); struct hva_dev *hva = ctx_to_hdev(ctx); strscpy(cap->driver, HVA_NAME, sizeof(cap->driver)); @@ -268,7 +271,7 @@ static int hva_querycap(struct file *file, void *priv, static int hva_enum_fmt_stream(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_ctx *ctx = file_to_ctx(file); struct hva_dev *hva = ctx_to_hdev(ctx); if (unlikely(f->index >= hva->nb_of_streamformats)) @@ -282,7 +285,7 @@ static int hva_enum_fmt_stream(struct file *file, void *priv, static int hva_enum_fmt_frame(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_ctx *ctx = file_to_ctx(file); struct hva_dev *hva = ctx_to_hdev(ctx); if (unlikely(f->index >= hva->nb_of_pixelformats)) @@ -295,7 +298,7 @@ static int hva_enum_fmt_frame(struct file *file, void *priv, static int hva_g_fmt_stream(struct file *file, void *fh, struct v4l2_format *f) { - struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_ctx *ctx = file_to_ctx(file); struct hva_streaminfo *streaminfo = &ctx->streaminfo; f->fmt.pix.width = streaminfo->width; @@ -314,7 +317,7 @@ static int hva_g_fmt_stream(struct file *file, void *fh, struct v4l2_format *f) static int hva_g_fmt_frame(struct file *file, void *fh, struct v4l2_format *f) { - struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_ctx *ctx = file_to_ctx(file); struct hva_frameinfo *frameinfo = &ctx->frameinfo; f->fmt.pix.width = frameinfo->width; @@ -335,7 +338,7 @@ static int hva_g_fmt_frame(struct file *file, void *fh, struct v4l2_format *f) static int hva_try_fmt_stream(struct file *file, void *priv, struct v4l2_format *f) { - struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_ctx *ctx = file_to_ctx(file); struct device *dev = ctx_to_dev(ctx); struct v4l2_pix_format *pix = &f->fmt.pix; u32 streamformat = pix->pixelformat; @@ -399,7 +402,7 @@ static int hva_try_fmt_stream(struct file *file, void *priv, static int hva_try_fmt_frame(struct file *file, void *priv, struct v4l2_format *f) { - struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_ctx *ctx = file_to_ctx(file); struct device *dev = ctx_to_dev(ctx); struct v4l2_pix_format *pix = &f->fmt.pix; u32 pixelformat = pix->pixelformat; @@ -449,7 +452,7 @@ static int hva_try_fmt_frame(struct file *file, void *priv, static int hva_s_fmt_stream(struct file *file, void *fh, struct v4l2_format *f) { - struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_ctx *ctx = file_to_ctx(file); struct device *dev = ctx_to_dev(ctx); struct vb2_queue *vq; int ret; @@ -479,7 +482,7 @@ static int hva_s_fmt_stream(struct file *file, void *fh, struct v4l2_format *f) static int hva_s_fmt_frame(struct file *file, void *fh, struct v4l2_format *f) { - struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_ctx *ctx = file_to_ctx(file); struct device *dev = ctx_to_dev(ctx); struct v4l2_pix_format *pix = &f->fmt.pix; struct vb2_queue *vq; @@ -517,7 +520,7 @@ static int hva_s_fmt_frame(struct file *file, void *fh, struct v4l2_format *f) static int hva_g_parm(struct file *file, void *fh, struct v4l2_streamparm *sp) { - struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_ctx *ctx = file_to_ctx(file); struct v4l2_fract *time_per_frame = &ctx->ctrls.time_per_frame; if (sp->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) @@ -533,7 +536,7 @@ static int hva_g_parm(struct file *file, void *fh, struct v4l2_streamparm *sp) static int hva_s_parm(struct file *file, void *fh, struct v4l2_streamparm *sp) { - struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_ctx *ctx = file_to_ctx(file); struct v4l2_fract *time_per_frame = &ctx->ctrls.time_per_frame; if (sp->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) @@ -553,7 +556,7 @@ static int hva_s_parm(struct file *file, void *fh, struct v4l2_streamparm *sp) static int hva_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { - struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_ctx *ctx = file_to_ctx(file); struct device *dev = ctx_to_dev(ctx); if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { @@ -1114,8 +1117,6 @@ static const struct vb2_ops hva_qops = { .buf_queue = hva_buf_queue, .start_streaming = hva_start_streaming, .stop_streaming = hva_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; /* @@ -1173,8 +1174,7 @@ static int hva_open(struct file *file) INIT_WORK(&ctx->run_work, hva_run_work); v4l2_fh_init(&ctx->fh, video_devdata(file)); - file->private_data = &ctx->fh; - v4l2_fh_add(&ctx->fh); + v4l2_fh_add(&ctx->fh, file); ret = hva_ctrls_setup(ctx); if (ret) { @@ -1218,7 +1218,7 @@ static int hva_open(struct file *file) err_ctrls: v4l2_ctrl_handler_free(&ctx->ctrl_handler); err_fh: - v4l2_fh_del(&ctx->fh); + v4l2_fh_del(&ctx->fh, file); v4l2_fh_exit(&ctx->fh); kfree(ctx); out: @@ -1227,7 +1227,7 @@ out: static int hva_release(struct file *file) { - struct hva_ctx *ctx = fh_to_ctx(file->private_data); + struct hva_ctx *ctx = file_to_ctx(file); struct hva_dev *hva = ctx_to_hdev(ctx); struct device *dev = ctx_to_dev(ctx); const struct hva_enc *enc = ctx->enc; @@ -1249,7 +1249,7 @@ static int hva_release(struct file *file) v4l2_ctrl_handler_free(&ctx->ctrl_handler); - v4l2_fh_del(&ctx->fh); + v4l2_fh_del(&ctx->fh, file); v4l2_fh_exit(&ctx->fh); #ifdef CONFIG_VIDEO_STI_HVA_DEBUGFS @@ -1456,7 +1456,7 @@ MODULE_DEVICE_TABLE(of, hva_match_types); static struct platform_driver hva_driver = { .probe = hva_probe, - .remove_new = hva_remove, + .remove = hva_remove, .driver = { .name = HVA_NAME, .of_match_table = hva_match_types, diff --git a/drivers/media/platform/st/sti/hva/hva.h b/drivers/media/platform/st/sti/hva/hva.h index ba6b893416ec..1fe561082a74 100644 --- a/drivers/media/platform/st/sti/hva/hva.h +++ b/drivers/media/platform/st/sti/hva/hva.h @@ -13,8 +13,6 @@ #include <media/videobuf2-v4l2.h> #include <media/v4l2-mem2mem.h> -#define fh_to_ctx(f) (container_of(f, struct hva_ctx, fh)) - #define hva_to_dev(h) (h->dev) #define ctx_to_dev(c) (c->hva_dev->dev) diff --git a/drivers/media/platform/st/stm32/Kconfig b/drivers/media/platform/st/stm32/Kconfig index 9df9a2a17728..f12e67bcc9bc 100644 --- a/drivers/media/platform/st/stm32/Kconfig +++ b/drivers/media/platform/st/stm32/Kconfig @@ -1,6 +1,20 @@ # SPDX-License-Identifier: GPL-2.0-only # V4L drivers +config VIDEO_STM32_CSI + tristate "STM32 Camera Serial Interface (CSI) support" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && OF + depends on ARCH_STM32 || COMPILE_TEST + select MEDIA_CONTROLLER + select V4L2_FWNODE + help + This module makes the STM32 Camera Serial Interface (CSI) + available as a v4l2 device. + + To compile this driver as a module, choose M here: the module + will be called stm32-csi. + config VIDEO_STM32_DCMI tristate "STM32 Digital Camera Memory Interface (DCMI) support" depends on V4L_PLATFORM_DRIVERS diff --git a/drivers/media/platform/st/stm32/Makefile b/drivers/media/platform/st/stm32/Makefile index 7ed8297b9b19..9ae57897f030 100644 --- a/drivers/media/platform/st/stm32/Makefile +++ b/drivers/media/platform/st/stm32/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_VIDEO_STM32_CSI) += stm32-csi.o obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32-dcmi.o obj-$(CONFIG_VIDEO_STM32_DCMIPP) += stm32-dcmipp/ stm32-dma2d-objs := dma2d/dma2d.o dma2d/dma2d-hw.o diff --git a/drivers/media/platform/st/stm32/dma2d/dma2d.c b/drivers/media/platform/st/stm32/dma2d/dma2d.c index 92f1edee58f8..72488aa922fc 100644 --- a/drivers/media/platform/st/stm32/dma2d/dma2d.c +++ b/drivers/media/platform/st/stm32/dma2d/dma2d.c @@ -45,7 +45,10 @@ * whole of a destination image with a pixel format conversion. */ -#define fh2ctx(__fh) container_of(__fh, struct dma2d_ctx, fh) +static inline struct dma2d_ctx *file2ctx(struct file *filp) +{ + return container_of(file_to_v4l2_fh(filp), struct dma2d_ctx, fh); +} static const struct dma2d_fmt formats[] = { { @@ -186,8 +189,6 @@ static const struct vb2_ops dma2d_qops = { .buf_queue = dma2d_buf_queue, .start_streaming = dma2d_start_streaming, .stop_streaming = dma2d_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int queue_init(void *priv, struct vb2_queue *src_vq, @@ -303,8 +304,7 @@ static int dma2d_open(struct file *file) } v4l2_fh_init(&ctx->fh, video_devdata(file)); - file->private_data = &ctx->fh; - v4l2_fh_add(&ctx->fh); + v4l2_fh_add(&ctx->fh, file); dma2d_setup_ctrls(ctx); @@ -320,13 +320,13 @@ static int dma2d_open(struct file *file) static int dma2d_release(struct file *file) { struct dma2d_dev *dev = video_drvdata(file); - struct dma2d_ctx *ctx = fh2ctx(file->private_data); + struct dma2d_ctx *ctx = file2ctx(file); mutex_lock(&dev->mutex); v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); mutex_unlock(&dev->mutex); v4l2_ctrl_handler_free(&ctx->ctrl_handler); - v4l2_fh_del(&ctx->fh); + v4l2_fh_del(&ctx->fh, file); v4l2_fh_exit(&ctx->fh); kfree(ctx); @@ -343,7 +343,7 @@ static int vidioc_querycap(struct file *file, void *priv, return 0; } -static int vidioc_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f) +static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) { if (f->index >= NUM_FORMATS) return -EINVAL; @@ -352,16 +352,11 @@ static int vidioc_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f) return 0; } -static int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f) +static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) { - struct dma2d_ctx *ctx = prv; - struct vb2_queue *vq; + struct dma2d_ctx *ctx = file2ctx(file); struct dma2d_frame *frm; - vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); - if (!vq) - return -EINVAL; - frm = get_frame(ctx, f->type); f->fmt.pix.width = frm->width; f->fmt.pix.height = frm->height; @@ -377,9 +372,9 @@ static int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f) return 0; } -static int vidioc_try_fmt(struct file *file, void *prv, struct v4l2_format *f) +static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f) { - struct dma2d_ctx *ctx = prv; + struct dma2d_ctx *ctx = file2ctx(file); struct dma2d_fmt *fmt; enum v4l2_field *field; u32 fourcc = f->fmt.pix.pixelformat; @@ -420,9 +415,9 @@ static int vidioc_try_fmt(struct file *file, void *prv, struct v4l2_format *f) return 0; } -static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f) +static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f) { - struct dma2d_ctx *ctx = prv; + struct dma2d_ctx *ctx = file2ctx(file); struct vb2_queue *vq; struct dma2d_frame *frm; struct dma2d_fmt *fmt; @@ -431,7 +426,7 @@ static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f) /* Adjust all values accordingly to the hardware capabilities * and chosen format. */ - ret = vidioc_try_fmt(file, prv, f); + ret = vidioc_try_fmt(file, priv, f); if (ret) return ret; @@ -490,9 +485,10 @@ static void device_run(void *prv) src->sequence = frm_out->sequence++; dst->sequence = frm_cap->sequence++; - v4l2_m2m_buf_copy_metadata(src, dst, true); + v4l2_m2m_buf_copy_metadata(src, dst); - clk_enable(dev->gate); + if (clk_enable(dev->gate)) + goto end; dma2d_config_fg(dev, frm_out, vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0)); @@ -717,7 +713,7 @@ MODULE_DEVICE_TABLE(of, stm32_dma2d_match); static struct platform_driver dma2d_pdrv = { .probe = dma2d_probe, - .remove_new = dma2d_remove, + .remove = dma2d_remove, .driver = { .name = DMA2D_NAME, .of_match_table = stm32_dma2d_match, diff --git a/drivers/media/platform/st/stm32/stm32-csi.c b/drivers/media/platform/st/stm32/stm32-csi.c new file mode 100644 index 000000000000..fd2b6dfbd44c --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-csi.c @@ -0,0 +1,1141 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for STM32 Camera Serial Interface + * + * Copyright (C) STMicroelectronics SA 2024 + * Author: Alain Volmat <alain.volmat@foss.st.com> + * for STMicroelectronics. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#include <media/mipi-csi2.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +#define STM32_CSI_CR 0x0000 +#define STM32_CSI_CR_CSIEN BIT(0) +#define STM32_CSI_CR_VCXSTART(x) BIT(2 + ((x) * 4)) +#define STM32_CSI_CR_VCXSTOP(x) BIT(3 + ((x) * 4)) +#define STM32_CSI_PCR 0x0004 +#define STM32_CSI_PCR_DL1EN BIT(3) +#define STM32_CSI_PCR_DL0EN BIT(2) +#define STM32_CSI_PCR_CLEN BIT(1) +#define STM32_CSI_PCR_PWRDOWN BIT(0) +#define STM32_CSI_VCXCFGR1(x) ((((x) + 1) * 0x0010) + 0x0) +#define STM32_CSI_VCXCFGR1_ALLDT BIT(0) +#define STM32_CSI_VCXCFGR1_DT0EN BIT(1) +#define STM32_CSI_VCXCFGR1_DT1EN BIT(2) +#define STM32_CSI_VCXCFGR1_CDTFT_SHIFT 8 +#define STM32_CSI_VCXCFGR1_DT0_SHIFT 16 +#define STM32_CSI_VCXCFGR1_DT0FT_SHIFT 24 +#define STM32_CSI_VCXCFGR2(x) ((((x) + 1) * 0x0010) + 0x4) +#define STM32_CSI_VCXCFGR2_DT1_SHIFT 0 +#define STM32_CSI_VCXCFGR2_DT1FT_SHIFT 8 +#define STM32_CSI_INPUT_BPP8 2 +#define STM32_CSI_INPUT_BPP10 3 +#define STM32_CSI_INPUT_BPP12 4 +#define STM32_CSI_INPUT_BPP14 5 +#define STM32_CSI_LMCFGR 0x0070 +#define STM32_CSI_LMCFGR_LANENB_SHIFT 8 +#define STM32_CSI_LMCFGR_DLMAP_SHIFT 16 +#define STM32_CSI_IER0 0x0080 +#define STM32_CSI_IER1 0x0084 +#define STM32_CSI_SR0 0x0090 +#define STM32_CSI_SR0_SYNCERRF BIT(30) +#define STM32_CSI_SR0_SPKTERRF BIT(28) +#define STM32_CSI_SR0_IDERRF BIT(27) +#define STM32_CSI_SR0_CECCERRF BIT(26) +#define STM32_CSI_SR0_ECCERRF BIT(25) +#define STM32_CSI_SR0_CRCERRF BIT(24) +#define STM32_CSI_SR0_CCFIFOFF BIT(21) +#define STM32_CSI_SR0_VCXSTATEF(x) BIT(17 + (x)) +#define STM32_CSI_SR1 0x0094 +#define STM32_CSI_SR1_ECTRLDL1F BIT(12) +#define STM32_CSI_SR1_ESYNCESCDL1F BIT(11) +#define STM32_CSI_SR1_EESCDL1F BIT(10) +#define STM32_CSI_SR1_ESOTSYNCDL1F BIT(9) +#define STM32_CSI_SR1_ESOTDL1F BIT(8) +#define STM32_CSI_SR1_ECTRLDL0F BIT(4) +#define STM32_CSI_SR1_ESYNCESCDL0F BIT(3) +#define STM32_CSI_SR1_EESCDL0F BIT(2) +#define STM32_CSI_SR1_ESOTSYNCDL0F BIT(1) +#define STM32_CSI_SR1_ESOTDL0F BIT(0) +#define STM32_CSI_FCR0 0x0100 +#define STM32_CSI_FCR1 0x0104 +#define STM32_CSI_SPDFR 0x0110 +#define STM32_CSI_DT_MASK 0x3f +#define STM32_CSI_VC_MASK 0x03 +#define STM32_CSI_ERR1 0x0114 +#define STM32_CSI_ERR1_IDVCERR_SHIFT 22 +#define STM32_CSI_ERR1_IDDTERR_SHIFT 16 +#define STM32_CSI_ERR1_CECCVCERR_SHIFT 14 +#define STM32_CSI_ERR1_CECCDTERR_SHIFT 8 +#define STM32_CSI_ERR1_CRCVCERR_SHIFT 6 +#define STM32_CSI_ERR1_CRCDTERR_SHIFT 0 +#define STM32_CSI_ERR2 0x0118 +#define STM32_CSI_ERR2_SYNCVCERR_SHIFT 18 +#define STM32_CSI_ERR2_SPKTVCERR_SHIFT 6 +#define STM32_CSI_ERR2_SPKTDTERR_SHIFT 0 +#define STM32_CSI_PRCR 0x1000 +#define STM32_CSI_PRCR_PEN BIT(1) +#define STM32_CSI_PMCR 0x1004 +#define STM32_CSI_PFCR 0x1008 +#define STM32_CSI_PFCR_CCFR_MASK GENMASK(5, 0) +#define STM32_CSI_PFCR_CCFR_SHIFT 0 +#define STM32_CSI_PFCR_HSFR_MASK GENMASK(14, 8) +#define STM32_CSI_PFCR_HSFR_SHIFT 8 +#define STM32_CSI_PFCR_DLD BIT(16) +#define STM32_CSI_PTCR0 0x1010 +#define STM32_CSI_PTCR0_TCKEN BIT(0) +#define STM32_CSI_PTCR1 0x1014 +#define STM32_CSI_PTCR1_TWM BIT(16) +#define STM32_CSI_PTCR1_TDI_MASK GENMASK(7, 0) +#define STM32_CSI_PTCR1_TDI_SHIFT 0 +#define STM32_CSI_PTSR 0x1018 + +#define STM32_CSI_LANES_MAX 2 + +#define STM32_CSI_SR0_ERRORS (STM32_CSI_SR0_SYNCERRF | STM32_CSI_SR0_SPKTERRF |\ + STM32_CSI_SR0_IDERRF | STM32_CSI_SR0_CECCERRF |\ + STM32_CSI_SR0_ECCERRF | STM32_CSI_SR0_CRCERRF |\ + STM32_CSI_SR0_CCFIFOFF) +#define STM32_CSI_SR1_DL0_ERRORS (STM32_CSI_SR1_ECTRLDL0F | STM32_CSI_SR1_ESYNCESCDL0F |\ + STM32_CSI_SR1_EESCDL0F | STM32_CSI_SR1_ESOTSYNCDL0F |\ + STM32_CSI_SR1_ESOTDL0F) +#define STM32_CSI_SR1_DL1_ERRORS (STM32_CSI_SR1_ECTRLDL1F | STM32_CSI_SR1_ESYNCESCDL1F |\ + STM32_CSI_SR1_EESCDL1F | STM32_CSI_SR1_ESOTSYNCDL1F |\ + STM32_CSI_SR1_ESOTDL1F) +#define STM32_CSI_SR1_ERRORS (STM32_CSI_SR1_DL0_ERRORS | STM32_CSI_SR1_DL1_ERRORS) + +enum stm32_csi_pads { + STM32_CSI_PAD_SINK, + STM32_CSI_PAD_SOURCE, + STM32_CSI_PAD_MAX, +}; + +struct stm32_csi_event { + u32 mask; + const char * const name; +}; + +static const struct stm32_csi_event stm32_csi_events_sr0[] = { + {STM32_CSI_SR0_SYNCERRF, "Synchronization error"}, + {STM32_CSI_SR0_SPKTERRF, "Short packet error"}, + {STM32_CSI_SR0_IDERRF, "Data type ID error"}, + {STM32_CSI_SR0_CECCERRF, "Corrected ECC error"}, + {STM32_CSI_SR0_ECCERRF, "ECC error"}, + {STM32_CSI_SR0_CRCERRF, "CRC error"}, + {STM32_CSI_SR0_CCFIFOFF, "Clk changer FIFO full error"}, +}; + +#define STM32_CSI_NUM_SR0_EVENTS ARRAY_SIZE(stm32_csi_events_sr0) + +static const struct stm32_csi_event stm32_csi_events_sr1[] = { + {STM32_CSI_SR1_ECTRLDL1F, "L1: D-PHY control error"}, + {STM32_CSI_SR1_ESYNCESCDL1F, + "L1: D-PHY low power data transmission synchro error"}, + {STM32_CSI_SR1_EESCDL1F, "L1: D-PHY escape entry error"}, + {STM32_CSI_SR1_ESOTSYNCDL1F, + "L1: Start of transmission synchro error"}, + {STM32_CSI_SR1_ESOTDL1F, "L1: Start of transmission error"}, + {STM32_CSI_SR1_ECTRLDL0F, "L0: D-PHY control error"}, + {STM32_CSI_SR1_ESYNCESCDL0F, + "L0: D-PHY low power data transmission synchro error"}, + {STM32_CSI_SR1_EESCDL0F, "L0: D-PHY escape entry error"}, + {STM32_CSI_SR1_ESOTSYNCDL0F, + "L0: Start of transmission synchro error"}, + {STM32_CSI_SR1_ESOTDL0F, "L0: Start of transmission error"}, +}; + +#define STM32_CSI_NUM_SR1_EVENTS ARRAY_SIZE(stm32_csi_events_sr1) + +enum stm32_csi_clk { + STM32_CSI_CLK_PCLK, + STM32_CSI_CLK_TXESC, + STM32_CSI_CLK_CSI2PHY, + STM32_CSI_CLK_NB, +}; + +static const char * const stm32_csi_clks_id[] = { + "pclk", + "txesc", + "csi2phy", +}; + +struct stm32_csi_dev { + struct device *dev; + + void __iomem *base; + + struct clk_bulk_data clks[STM32_CSI_CLK_NB]; + struct regulator_bulk_data supplies[2]; + + u8 lanes[STM32_CSI_LANES_MAX]; + u8 num_lanes; + + /* + * spinlock slock is used to protect to srX_counters tables being + * accessed from log_status and interrupt context + */ + spinlock_t slock; + + u32 sr0_counters[STM32_CSI_NUM_SR0_EVENTS]; + u32 sr1_counters[STM32_CSI_NUM_SR1_EVENTS]; + + struct v4l2_subdev sd; + struct v4l2_async_notifier notifier; + struct media_pad pads[STM32_CSI_PAD_MAX]; + + /* Remote source */ + struct v4l2_subdev *s_subdev; + u32 s_subdev_pad_nb; +}; + +struct stm32_csi_fmts { + u32 code; + u32 datatype; + u32 input_fmt; + u8 bpp; +}; + +#define FMT_MBUS_DT_DTFMT_BPP(mbus, dt, input, byteperpixel) \ + { \ + .code = MEDIA_BUS_FMT_##mbus, \ + .datatype = MIPI_CSI2_DT_##dt, \ + .input_fmt = STM32_CSI_INPUT_##input, \ + .bpp = byteperpixel, \ + } +static const struct stm32_csi_fmts stm32_csi_formats[] = { + /* YUV 422 8 bit */ + FMT_MBUS_DT_DTFMT_BPP(UYVY8_1X16, YUV422_8B, BPP8, 8), + FMT_MBUS_DT_DTFMT_BPP(YUYV8_1X16, YUV422_8B, BPP8, 8), + FMT_MBUS_DT_DTFMT_BPP(YVYU8_1X16, YUV422_8B, BPP8, 8), + FMT_MBUS_DT_DTFMT_BPP(VYUY8_1X16, YUV422_8B, BPP8, 8), + + /* Raw Bayer */ + /* 8 bit */ + FMT_MBUS_DT_DTFMT_BPP(SBGGR8_1X8, RAW8, BPP8, 8), + FMT_MBUS_DT_DTFMT_BPP(SGBRG8_1X8, RAW8, BPP8, 8), + FMT_MBUS_DT_DTFMT_BPP(SGRBG8_1X8, RAW8, BPP8, 8), + FMT_MBUS_DT_DTFMT_BPP(SRGGB8_1X8, RAW8, BPP8, 8), + /* 10 bit */ + FMT_MBUS_DT_DTFMT_BPP(SRGGB10_1X10, RAW10, BPP10, 10), + FMT_MBUS_DT_DTFMT_BPP(SGBRG10_1X10, RAW10, BPP10, 10), + FMT_MBUS_DT_DTFMT_BPP(SGRBG10_1X10, RAW10, BPP10, 10), + FMT_MBUS_DT_DTFMT_BPP(SRGGB10_1X10, RAW10, BPP10, 10), + /* 12 bit */ + FMT_MBUS_DT_DTFMT_BPP(SRGGB12_1X12, RAW12, BPP12, 12), + FMT_MBUS_DT_DTFMT_BPP(SGBRG12_1X12, RAW12, BPP12, 12), + FMT_MBUS_DT_DTFMT_BPP(SGRBG12_1X12, RAW12, BPP12, 12), + FMT_MBUS_DT_DTFMT_BPP(SRGGB12_1X12, RAW12, BPP12, 12), + /* 14 bit */ + FMT_MBUS_DT_DTFMT_BPP(SRGGB14_1X14, RAW14, BPP14, 14), + FMT_MBUS_DT_DTFMT_BPP(SGBRG14_1X14, RAW14, BPP14, 14), + FMT_MBUS_DT_DTFMT_BPP(SGRBG14_1X14, RAW14, BPP14, 14), + FMT_MBUS_DT_DTFMT_BPP(SRGGB14_1X14, RAW14, BPP14, 14), + + /* RGB 565 */ + FMT_MBUS_DT_DTFMT_BPP(RGB565_1X16, RGB565, BPP8, 8), + + /* JPEG (datatype isn't used) */ + FMT_MBUS_DT_DTFMT_BPP(JPEG_1X8, NULL, BPP8, 8), +}; + +struct stm32_csi_mbps_phy_reg { + unsigned int mbps; + unsigned int hsfreqrange; + unsigned int osc_freq_target; +}; + +/* + * Table describing configuration of the PHY depending on the + * intended Bit Rate. From table 5-8 Frequency Ranges and Defaults + * of the Synopsis DWC MIPI PHY databook + */ +static const struct stm32_csi_mbps_phy_reg snps_stm32mp25[] = { + { .mbps = 80, .hsfreqrange = 0x00, .osc_freq_target = 460 }, + { .mbps = 90, .hsfreqrange = 0x10, .osc_freq_target = 460 }, + { .mbps = 100, .hsfreqrange = 0x20, .osc_freq_target = 460 }, + { .mbps = 110, .hsfreqrange = 0x30, .osc_freq_target = 460 }, + { .mbps = 120, .hsfreqrange = 0x01, .osc_freq_target = 460 }, + { .mbps = 130, .hsfreqrange = 0x11, .osc_freq_target = 460 }, + { .mbps = 140, .hsfreqrange = 0x21, .osc_freq_target = 460 }, + { .mbps = 150, .hsfreqrange = 0x31, .osc_freq_target = 460 }, + { .mbps = 160, .hsfreqrange = 0x02, .osc_freq_target = 460 }, + { .mbps = 170, .hsfreqrange = 0x12, .osc_freq_target = 460 }, + { .mbps = 180, .hsfreqrange = 0x22, .osc_freq_target = 460 }, + { .mbps = 190, .hsfreqrange = 0x32, .osc_freq_target = 460 }, + { .mbps = 205, .hsfreqrange = 0x03, .osc_freq_target = 460 }, + { .mbps = 220, .hsfreqrange = 0x13, .osc_freq_target = 460 }, + { .mbps = 235, .hsfreqrange = 0x23, .osc_freq_target = 460 }, + { .mbps = 250, .hsfreqrange = 0x33, .osc_freq_target = 460 }, + { .mbps = 275, .hsfreqrange = 0x04, .osc_freq_target = 460 }, + { .mbps = 300, .hsfreqrange = 0x14, .osc_freq_target = 460 }, + { .mbps = 325, .hsfreqrange = 0x25, .osc_freq_target = 460 }, + { .mbps = 350, .hsfreqrange = 0x35, .osc_freq_target = 460 }, + { .mbps = 400, .hsfreqrange = 0x05, .osc_freq_target = 460 }, + { .mbps = 450, .hsfreqrange = 0x16, .osc_freq_target = 460 }, + { .mbps = 500, .hsfreqrange = 0x26, .osc_freq_target = 460 }, + { .mbps = 550, .hsfreqrange = 0x37, .osc_freq_target = 460 }, + { .mbps = 600, .hsfreqrange = 0x07, .osc_freq_target = 460 }, + { .mbps = 650, .hsfreqrange = 0x18, .osc_freq_target = 460 }, + { .mbps = 700, .hsfreqrange = 0x28, .osc_freq_target = 460 }, + { .mbps = 750, .hsfreqrange = 0x39, .osc_freq_target = 460 }, + { .mbps = 800, .hsfreqrange = 0x09, .osc_freq_target = 460 }, + { .mbps = 850, .hsfreqrange = 0x19, .osc_freq_target = 460 }, + { .mbps = 900, .hsfreqrange = 0x29, .osc_freq_target = 460 }, + { .mbps = 950, .hsfreqrange = 0x3a, .osc_freq_target = 460 }, + { .mbps = 1000, .hsfreqrange = 0x0a, .osc_freq_target = 460 }, + { .mbps = 1050, .hsfreqrange = 0x1a, .osc_freq_target = 460 }, + { .mbps = 1100, .hsfreqrange = 0x2a, .osc_freq_target = 460 }, + { .mbps = 1150, .hsfreqrange = 0x3b, .osc_freq_target = 460 }, + { .mbps = 1200, .hsfreqrange = 0x0b, .osc_freq_target = 460 }, + { .mbps = 1250, .hsfreqrange = 0x1b, .osc_freq_target = 460 }, + { .mbps = 1300, .hsfreqrange = 0x2b, .osc_freq_target = 460 }, + { .mbps = 1350, .hsfreqrange = 0x3c, .osc_freq_target = 460 }, + { .mbps = 1400, .hsfreqrange = 0x0c, .osc_freq_target = 460 }, + { .mbps = 1450, .hsfreqrange = 0x1c, .osc_freq_target = 460 }, + { .mbps = 1500, .hsfreqrange = 0x2c, .osc_freq_target = 460 }, + { .mbps = 1550, .hsfreqrange = 0x3d, .osc_freq_target = 285 }, + { .mbps = 1600, .hsfreqrange = 0x0d, .osc_freq_target = 295 }, + { .mbps = 1650, .hsfreqrange = 0x1d, .osc_freq_target = 304 }, + { .mbps = 1700, .hsfreqrange = 0x2e, .osc_freq_target = 313 }, + { .mbps = 1750, .hsfreqrange = 0x3e, .osc_freq_target = 322 }, + { .mbps = 1800, .hsfreqrange = 0x0e, .osc_freq_target = 331 }, + { .mbps = 1850, .hsfreqrange = 0x1e, .osc_freq_target = 341 }, + { .mbps = 1900, .hsfreqrange = 0x2f, .osc_freq_target = 350 }, + { .mbps = 1950, .hsfreqrange = 0x3f, .osc_freq_target = 359 }, + { .mbps = 2000, .hsfreqrange = 0x0f, .osc_freq_target = 368 }, + { .mbps = 2050, .hsfreqrange = 0x40, .osc_freq_target = 377 }, + { .mbps = 2100, .hsfreqrange = 0x41, .osc_freq_target = 387 }, + { .mbps = 2150, .hsfreqrange = 0x42, .osc_freq_target = 396 }, + { .mbps = 2200, .hsfreqrange = 0x43, .osc_freq_target = 405 }, + { .mbps = 2250, .hsfreqrange = 0x44, .osc_freq_target = 414 }, + { .mbps = 2300, .hsfreqrange = 0x45, .osc_freq_target = 423 }, + { .mbps = 2350, .hsfreqrange = 0x46, .osc_freq_target = 432 }, + { .mbps = 2400, .hsfreqrange = 0x47, .osc_freq_target = 442 }, + { .mbps = 2450, .hsfreqrange = 0x48, .osc_freq_target = 451 }, + { .mbps = 2500, .hsfreqrange = 0x49, .osc_freq_target = 460 }, +}; + +static const struct v4l2_mbus_framefmt fmt_default = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_RGB565_1X16, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_REC709, + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, + .quantization = V4L2_QUANTIZATION_DEFAULT, + .xfer_func = V4L2_XFER_FUNC_DEFAULT, +}; + +static const struct stm32_csi_fmts *stm32_csi_code_to_fmt(unsigned int code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(stm32_csi_formats); i++) + if (stm32_csi_formats[i].code == code) + return &stm32_csi_formats[i]; + + return NULL; +} + +static inline struct stm32_csi_dev *to_csidev(struct v4l2_subdev *sd) +{ + return container_of(sd, struct stm32_csi_dev, sd); +} + +static int stm32_csi_setup_lane_merger(struct stm32_csi_dev *csidev) +{ + u32 lmcfgr = 0; + unsigned int i; + + for (i = 0; i < csidev->num_lanes; i++) { + if (!csidev->lanes[i] || csidev->lanes[i] > STM32_CSI_LANES_MAX) { + dev_err(csidev->dev, "Invalid lane id (%d)\n", csidev->lanes[i]); + return -EINVAL; + } + lmcfgr |= (csidev->lanes[i] << ((i * 4) + STM32_CSI_LMCFGR_DLMAP_SHIFT)); + } + + lmcfgr |= (csidev->num_lanes << STM32_CSI_LMCFGR_LANENB_SHIFT); + + writel_relaxed(lmcfgr, csidev->base + STM32_CSI_LMCFGR); + + return 0; +} + +static void stm32_csi_phy_reg_write(struct stm32_csi_dev *csidev, + u32 addr, u32 val) +{ + /* Based on sequence described at section 5.2.3.2 of DesignWave document */ + /* For writing the 4-bit testcode MSBs */ + /* Set testen to high */ + writel_relaxed(STM32_CSI_PTCR1_TWM, csidev->base + STM32_CSI_PTCR1); + + /* Set testclk to high */ + writel_relaxed(STM32_CSI_PTCR0_TCKEN, csidev->base + STM32_CSI_PTCR0); + + /* Place 0x00 in testdin */ + writel_relaxed(STM32_CSI_PTCR1_TWM, csidev->base + STM32_CSI_PTCR1); + + /* + * Set testclk to low (with the falling edge on testclk, the testdin + * signal content is latched internally) + */ + writel_relaxed(0, csidev->base + STM32_CSI_PTCR0); + + /* Set testen to low */ + writel_relaxed(0, csidev->base + STM32_CSI_PTCR1); + + /* Place the 8-bit word corresponding to the testcode MSBs in testdin */ + writel_relaxed(((addr >> 8) & STM32_CSI_PTCR1_TDI_MASK) << STM32_CSI_PTCR1_TDI_SHIFT, + csidev->base + STM32_CSI_PTCR1); + + /* Set testclk to high */ + writel_relaxed(STM32_CSI_PTCR0_TCKEN, csidev->base + STM32_CSI_PTCR0); + + /* For writing the 8-bit testcode LSBs */ + /* Set testclk to low */ + writel_relaxed(0, csidev->base + STM32_CSI_PTCR0); + + /* Set testen to high */ + writel_relaxed(STM32_CSI_PTCR1_TWM, csidev->base + STM32_CSI_PTCR1); + + /* Set testclk to high */ + writel_relaxed(STM32_CSI_PTCR0_TCKEN, csidev->base + STM32_CSI_PTCR0); + + /* Place the 8-bit word test data in testdin */ + writel_relaxed((addr & STM32_CSI_PTCR1_TDI_MASK) << + STM32_CSI_PTCR1_TDI_SHIFT | STM32_CSI_PTCR1_TWM, + csidev->base + STM32_CSI_PTCR1); + + /* + * Set testclk to low (with the falling edge on testclk, the testdin + * signal content is latched internally) + */ + writel_relaxed(0, csidev->base + STM32_CSI_PTCR0); + + /* Set testen to low */ + writel_relaxed(0, csidev->base + STM32_CSI_PTCR1); + + /* For writing the data */ + /* Place the 8-bit word corresponding to the page offset in testdin */ + writel_relaxed((val & STM32_CSI_PTCR1_TDI_MASK) << STM32_CSI_PTCR1_TDI_SHIFT, + csidev->base + STM32_CSI_PTCR1); + + /* Set testclk to high (test data is programmed internally */ + writel_relaxed(STM32_CSI_PTCR0_TCKEN, csidev->base + STM32_CSI_PTCR0); + + /* Finish by setting testclk to low */ + writel_relaxed(0, csidev->base + STM32_CSI_PTCR0); +} + +static int stm32_csi_start(struct stm32_csi_dev *csidev, + struct v4l2_subdev_state *state) +{ + struct media_pad *src_pad; + const struct stm32_csi_mbps_phy_reg *phy_regs = NULL; + struct v4l2_mbus_framefmt *sink_fmt; + const struct stm32_csi_fmts *fmt; + unsigned long phy_clk_frate; + u32 lanes_ie, lanes_en; + unsigned int mbps; + unsigned int i; + s64 link_freq; + int ret; + u32 ccfr; + + dev_dbg(csidev->dev, "Starting the CSI2\n"); + + /* Get the bpp value on pad0 (input of CSI) */ + sink_fmt = v4l2_subdev_state_get_format(state, STM32_CSI_PAD_SINK); + fmt = stm32_csi_code_to_fmt(sink_fmt->code); + + /* Get the remote sensor link frequency */ + if (!csidev->s_subdev) + return -EIO; + + src_pad = &csidev->s_subdev->entity.pads[csidev->s_subdev_pad_nb]; + link_freq = v4l2_get_link_freq(src_pad, + fmt->bpp, 2 * csidev->num_lanes); + if (link_freq < 0) + return link_freq; + + /* MBPS is expressed in Mbps, hence link_freq / 100000 * 2 */ + mbps = div_s64(link_freq, 500000); + dev_dbg(csidev->dev, "Computed Mbps: %u\n", mbps); + + for (i = 0; i < ARRAY_SIZE(snps_stm32mp25); i++) { + if (snps_stm32mp25[i].mbps >= mbps) { + phy_regs = &snps_stm32mp25[i]; + break; + } + } + + if (!phy_regs) { + dev_err(csidev->dev, "Unsupported PHY speed (%u Mbps)", mbps); + return -ERANGE; + } + + dev_dbg(csidev->dev, "PHY settings: (%u Mbps, %u HS FRange, %u OSC Freq)\n", + phy_regs->mbps, phy_regs->hsfreqrange, + phy_regs->osc_freq_target); + + /* Prepare lanes related configuration bits */ + lanes_ie = STM32_CSI_SR1_DL0_ERRORS; + lanes_en = STM32_CSI_PCR_DL0EN; + if (csidev->num_lanes == 2) { + lanes_ie |= STM32_CSI_SR1_DL1_ERRORS; + lanes_en |= STM32_CSI_PCR_DL1EN; + } + + ret = pm_runtime_get_sync(csidev->dev); + if (ret < 0) + goto error_put; + + /* Retrieve CSI2PHY clock rate to compute CCFR value */ + phy_clk_frate = clk_get_rate(csidev->clks[STM32_CSI_CLK_CSI2PHY].clk); + if (!phy_clk_frate) { + dev_err(csidev->dev, "CSI2PHY clock rate invalid (0)\n"); + ret = -EINVAL; + goto error_put; + } + + ret = stm32_csi_setup_lane_merger(csidev); + if (ret) + goto error_put; + + /* Enable the CSI */ + writel_relaxed(STM32_CSI_CR_CSIEN, csidev->base + STM32_CSI_CR); + + /* Enable some global CSI related interrupts - bits are same as SR0 */ + writel_relaxed(STM32_CSI_SR0_ERRORS, csidev->base + STM32_CSI_IER0); + + /* Enable lanes related error interrupts */ + writel_relaxed(lanes_ie, csidev->base + STM32_CSI_IER1); + + /* Initialization of the D-PHY */ + /* Stop the D-PHY */ + writel_relaxed(0, csidev->base + STM32_CSI_PRCR); + + /* Keep the D-PHY in power down state */ + writel_relaxed(0, csidev->base + STM32_CSI_PCR); + + /* Enable testclr clock during 15ns */ + writel_relaxed(STM32_CSI_PTCR0_TCKEN, csidev->base + STM32_CSI_PTCR0); + udelay(1); + writel_relaxed(0, csidev->base + STM32_CSI_PTCR0); + + /* Set hsfreqrange */ + phy_clk_frate /= 1000000; + ccfr = (phy_clk_frate - 17) * 4; + writel_relaxed((ccfr << STM32_CSI_PFCR_CCFR_SHIFT) | + (phy_regs->hsfreqrange << STM32_CSI_PFCR_HSFR_SHIFT), + csidev->base + STM32_CSI_PFCR); + + /* set reg @08 deskew_polarity_rw 1'b1 */ + stm32_csi_phy_reg_write(csidev, 0x08, 0x38); + + /* set reg @0xE4 counter_for_des_en_config_if_rx 0x10 + DLL prog EN */ + /* This is because 13<= cfgclkfreqrange[5:0]<=38 */ + stm32_csi_phy_reg_write(csidev, 0xe4, 0x11); + + /* set reg @0xe2 & reg @0xe3 value DLL target oscilation freq */ + /* Based on the table page 77, osc_freq_target */ + stm32_csi_phy_reg_write(csidev, 0xe2, phy_regs->osc_freq_target & 0xFF); + stm32_csi_phy_reg_write(csidev, 0xe3, (phy_regs->osc_freq_target >> 8) & 0x0F); + + writel_relaxed(STM32_CSI_PFCR_DLD | readl_relaxed(csidev->base + STM32_CSI_PFCR), + csidev->base + STM32_CSI_PFCR); + + /* Enable Lanes */ + writel_relaxed(lanes_en | STM32_CSI_PCR_CLEN, csidev->base + STM32_CSI_PCR); + writel_relaxed(lanes_en | STM32_CSI_PCR_CLEN | STM32_CSI_PCR_PWRDOWN, + csidev->base + STM32_CSI_PCR); + + writel_relaxed(STM32_CSI_PRCR_PEN, csidev->base + STM32_CSI_PRCR); + + /* Remove the force */ + writel_relaxed(0, csidev->base + STM32_CSI_PMCR); + + return ret; + +error_put: + pm_runtime_put(csidev->dev); + return ret; +} + +static void stm32_csi_stop(struct stm32_csi_dev *csidev) +{ + dev_dbg(csidev->dev, "Stopping the CSI2\n"); + + /* Disable the D-PHY */ + writel_relaxed(0, csidev->base + STM32_CSI_PCR); + + /* Disable ITs */ + writel_relaxed(0, csidev->base + STM32_CSI_IER0); + writel_relaxed(0, csidev->base + STM32_CSI_IER1); + + /* Disable the CSI */ + writel_relaxed(0, csidev->base + STM32_CSI_CR); + + pm_runtime_put(csidev->dev); +} + +static int stm32_csi_start_vc(struct stm32_csi_dev *csidev, + struct v4l2_subdev_state *state, u32 vc) +{ + struct v4l2_mbus_framefmt *mbus_fmt; + const struct stm32_csi_fmts *fmt; + u32 status; + u32 cfgr1; + int ret; + + mbus_fmt = v4l2_subdev_state_get_format(state, STM32_CSI_PAD_SOURCE); + fmt = stm32_csi_code_to_fmt(mbus_fmt->code); + + /* If the mbus code is JPEG, don't enable filtering */ + if (mbus_fmt->code == MEDIA_BUS_FMT_JPEG_1X8) { + cfgr1 = STM32_CSI_VCXCFGR1_ALLDT; + cfgr1 |= fmt->input_fmt << STM32_CSI_VCXCFGR1_CDTFT_SHIFT; + dev_dbg(csidev->dev, "VC%d: enable AllDT mode\n", vc); + } else { + cfgr1 = fmt->datatype << STM32_CSI_VCXCFGR1_DT0_SHIFT; + cfgr1 |= fmt->input_fmt << STM32_CSI_VCXCFGR1_DT0FT_SHIFT; + cfgr1 |= STM32_CSI_VCXCFGR1_DT0EN; + dev_dbg(csidev->dev, "VC%d: enable DT0(0x%x)/DT0FT(0x%x)\n", + vc, fmt->datatype, fmt->input_fmt); + } + writel_relaxed(cfgr1, csidev->base + STM32_CSI_VCXCFGR1(vc)); + + /* Enable processing of the virtual-channel and wait for its status */ + writel_relaxed(STM32_CSI_CR_VCXSTART(vc) | STM32_CSI_CR_CSIEN, + csidev->base + STM32_CSI_CR); + + ret = readl_relaxed_poll_timeout(csidev->base + STM32_CSI_SR0, + status, + status & STM32_CSI_SR0_VCXSTATEF(vc), + 1000, 1000000); + if (ret) { + dev_err(csidev->dev, "failed to start VC(%d)\n", vc); + return ret; + } + + return 0; +} + +static int stm32_csi_stop_vc(struct stm32_csi_dev *csidev, u32 vc) +{ + u32 status; + int ret; + + /* Stop the Virtual Channel */ + writel_relaxed(STM32_CSI_CR_VCXSTOP(vc) | STM32_CSI_CR_CSIEN, + csidev->base + STM32_CSI_CR); + + ret = readl_relaxed_poll_timeout(csidev->base + STM32_CSI_SR0, + status, + !(status & STM32_CSI_SR0_VCXSTATEF(vc)), + 1000, 1000000); + if (ret) { + dev_err(csidev->dev, "failed to stop VC(%d)\n", vc); + return ret; + } + + /* Disable all DTs */ + writel_relaxed(0, csidev->base + STM32_CSI_VCXCFGR1(vc)); + writel_relaxed(0, csidev->base + STM32_CSI_VCXCFGR2(vc)); + + return 0; +} + +static int stm32_csi_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct stm32_csi_dev *csidev = to_csidev(sd); + int ret; + + ret = v4l2_subdev_disable_streams(csidev->s_subdev, + csidev->s_subdev_pad_nb, BIT_ULL(0)); + if (ret) + return ret; + + /* Stop the VC0 */ + ret = stm32_csi_stop_vc(csidev, 0); + if (ret) + dev_err(csidev->dev, "Failed to stop VC0\n"); + + stm32_csi_stop(csidev); + + return 0; +} + +static int stm32_csi_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct stm32_csi_dev *csidev = to_csidev(sd); + int ret; + + ret = stm32_csi_start(csidev, state); + if (ret) + return ret; + + /* Configure & start the VC0 */ + ret = stm32_csi_start_vc(csidev, state, 0); + if (ret) { + dev_err(csidev->dev, "Failed to start VC0\n"); + goto failed_start_vc; + } + + ret = v4l2_subdev_enable_streams(csidev->s_subdev, + csidev->s_subdev_pad_nb, BIT_ULL(0)); + if (ret) + goto failed_enable_streams; + + return 0; + +failed_enable_streams: + stm32_csi_stop_vc(csidev, 0); +failed_start_vc: + stm32_csi_stop(csidev); + return ret; +} + +static int stm32_csi_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + unsigned int i; + + for (i = 0; i < sd->entity.num_pads; i++) + *v4l2_subdev_state_get_format(state, i) = fmt_default; + + return 0; +} + +static int stm32_csi_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(stm32_csi_formats)) + return -EINVAL; + + code->code = stm32_csi_formats[code->index].code; + return 0; +} + +static int stm32_csi_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct stm32_csi_dev *csidev = to_csidev(sd); + struct v4l2_mbus_framefmt *framefmt; + const struct stm32_csi_fmts *fmt; + + fmt = stm32_csi_code_to_fmt(format->format.code); + if (!fmt) { + dev_dbg(csidev->dev, "Unsupported code %d, use default\n", + format->format.code); + format->format.code = fmt_default.code; + } + + framefmt = v4l2_subdev_state_get_format(state, STM32_CSI_PAD_SINK); + + if (format->pad == STM32_CSI_PAD_SOURCE) + format->format = *framefmt; + else + *framefmt = format->format; + + framefmt = v4l2_subdev_state_get_format(state, STM32_CSI_PAD_SOURCE); + *framefmt = format->format; + + return 0; +} + +static int stm32_csi_log_status(struct v4l2_subdev *sd) +{ + struct stm32_csi_dev *csidev = to_csidev(sd); + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&csidev->slock, flags); + + for (i = 0; i < STM32_CSI_NUM_SR0_EVENTS; i++) { + if (csidev->sr0_counters[i]) + dev_info(csidev->dev, "%s events: %d\n", + stm32_csi_events_sr0[i].name, + csidev->sr0_counters[i]); + } + + for (i = 0; i < STM32_CSI_NUM_SR1_EVENTS; i++) { + if (csidev->sr1_counters[i]) + dev_info(csidev->dev, "%s events: %d\n", + stm32_csi_events_sr1[i].name, + csidev->sr1_counters[i]); + } + + spin_unlock_irqrestore(&csidev->slock, flags); + + return 0; +} + +static const struct v4l2_subdev_core_ops stm32_csi_core_ops = { + .log_status = stm32_csi_log_status, +}; + +static const struct v4l2_subdev_video_ops stm32_csi_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops stm32_csi_pad_ops = { + .enum_mbus_code = stm32_csi_enum_mbus_code, + .set_fmt = stm32_csi_set_pad_format, + .get_fmt = v4l2_subdev_get_fmt, + .enable_streams = stm32_csi_enable_streams, + .disable_streams = stm32_csi_disable_streams, +}; + +static const struct v4l2_subdev_ops stm32_csi_subdev_ops = { + .core = &stm32_csi_core_ops, + .pad = &stm32_csi_pad_ops, + .video = &stm32_csi_video_ops, +}; + +static const struct v4l2_subdev_internal_ops stm32_csi_subdev_internal_ops = { + .init_state = stm32_csi_init_state, +}; + +static int stm32_csi_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *s_subdev, + struct v4l2_async_connection *asd) +{ + struct v4l2_subdev *sd = notifier->sd; + struct stm32_csi_dev *csidev = to_csidev(sd); + int remote_pad; + + remote_pad = media_entity_get_fwnode_pad(&s_subdev->entity, + s_subdev->fwnode, + MEDIA_PAD_FL_SOURCE); + if (remote_pad < 0) { + dev_err(csidev->dev, "Couldn't find output pad for subdev %s\n", + s_subdev->name); + return remote_pad; + } + + csidev->s_subdev = s_subdev; + csidev->s_subdev_pad_nb = remote_pad; + + return media_create_pad_link(&csidev->s_subdev->entity, + remote_pad, &csidev->sd.entity, + STM32_CSI_PAD_SINK, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); +} + +static const struct v4l2_async_notifier_operations stm32_csi_notifier_ops = { + .bound = stm32_csi_async_bound, +}; + +static irqreturn_t stm32_csi_irq_thread(int irq, void *arg) +{ + struct stm32_csi_dev *csidev = arg; + unsigned long flags; + u32 sr0, sr1; + int i; + + sr0 = readl_relaxed(csidev->base + STM32_CSI_SR0); + sr1 = readl_relaxed(csidev->base + STM32_CSI_SR1); + + /* Clear interrupt */ + writel_relaxed(sr0 & STM32_CSI_SR0_ERRORS, + csidev->base + STM32_CSI_FCR0); + writel_relaxed(sr1 & STM32_CSI_SR1_ERRORS, + csidev->base + STM32_CSI_FCR1); + + spin_lock_irqsave(&csidev->slock, flags); + + for (i = 0; i < STM32_CSI_NUM_SR0_EVENTS; i++) + if (sr0 & stm32_csi_events_sr0[i].mask) + csidev->sr0_counters[i]++; + + for (i = 0; i < STM32_CSI_NUM_SR1_EVENTS; i++) + if (sr1 & stm32_csi_events_sr1[i].mask) + csidev->sr1_counters[i]++; + + spin_unlock_irqrestore(&csidev->slock, flags); + + return IRQ_HANDLED; +} + +static int stm32_csi_get_resources(struct stm32_csi_dev *csidev, + struct platform_device *pdev) +{ + unsigned int i; + int irq, ret; + + csidev->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(csidev->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(csidev->base), + "Failed to ioremap resource\n"); + + for (i = 0; i < STM32_CSI_CLK_NB; i++) + csidev->clks[i].id = stm32_csi_clks_id[i]; + + ret = devm_clk_bulk_get(&pdev->dev, STM32_CSI_CLK_NB, + csidev->clks); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "Couldn't get clks\n"); + + csidev->supplies[0].supply = "vdd"; + csidev->supplies[1].supply = "vdda18"; + ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(csidev->supplies), + csidev->supplies); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to request regulator vdd\n"); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + stm32_csi_irq_thread, IRQF_ONESHOT, + dev_name(&pdev->dev), csidev); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Unable to request irq"); + + return 0; +} + +static int stm32_csi_parse_dt(struct stm32_csi_dev *csidev) +{ + struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + struct v4l2_async_connection *asd; + struct fwnode_handle *ep; + int ret; + + /* Get bus characteristics from devicetree */ + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csidev->dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) { + dev_err(csidev->dev, "Could not find the endpoint\n"); + return -ENODEV; + } + + ret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep); + if (ret) { + dev_err(csidev->dev, "Could not parse v4l2 endpoint\n"); + goto out; + } + + csidev->num_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; + if (csidev->num_lanes > STM32_CSI_LANES_MAX) { + dev_err(csidev->dev, "Unsupported number of data-lanes: %d\n", + csidev->num_lanes); + ret = -EINVAL; + goto out; + } + + memcpy(csidev->lanes, v4l2_ep.bus.mipi_csi2.data_lanes, + sizeof(csidev->lanes)); + + v4l2_async_subdev_nf_init(&csidev->notifier, &csidev->sd); + + asd = v4l2_async_nf_add_fwnode_remote(&csidev->notifier, ep, + struct v4l2_async_connection); + + + if (IS_ERR(asd)) { + dev_err(csidev->dev, "Failed to add fwnode remote subdev\n"); + ret = PTR_ERR(asd); + goto out; + } + + csidev->notifier.ops = &stm32_csi_notifier_ops; + + ret = v4l2_async_nf_register(&csidev->notifier); + if (ret) { + dev_err(csidev->dev, "Failed to register notifier\n"); + v4l2_async_nf_cleanup(&csidev->notifier); + goto out; + } + +out: + fwnode_handle_put(ep); + return ret; +} + +static int stm32_csi_probe(struct platform_device *pdev) +{ + struct stm32_csi_dev *csidev; + struct reset_control *rstc; + int ret; + + csidev = devm_kzalloc(&pdev->dev, sizeof(*csidev), GFP_KERNEL); + if (!csidev) + return -ENOMEM; + + platform_set_drvdata(pdev, csidev); + csidev->dev = &pdev->dev; + + spin_lock_init(&csidev->slock); + + ret = stm32_csi_get_resources(csidev, pdev); + if (ret) + return ret; + + ret = stm32_csi_parse_dt(csidev); + if (ret) + return ret; + + csidev->sd.owner = THIS_MODULE; + csidev->sd.dev = &pdev->dev; + csidev->sd.internal_ops = &stm32_csi_subdev_internal_ops; + v4l2_subdev_init(&csidev->sd, &stm32_csi_subdev_ops); + v4l2_set_subdevdata(&csidev->sd, &pdev->dev); + snprintf(csidev->sd.name, sizeof(csidev->sd.name), "%s", + dev_name(&pdev->dev)); + + /* Create our media pads */ + csidev->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + csidev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + csidev->pads[STM32_CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + csidev->pads[STM32_CSI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&csidev->sd.entity, STM32_CSI_PAD_MAX, + csidev->pads); + if (ret) + goto err_cleanup; + + ret = v4l2_subdev_init_finalize(&csidev->sd); + if (ret < 0) + goto err_cleanup; + + /* Reset device */ + rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(rstc)) { + ret = dev_err_probe(&pdev->dev, PTR_ERR(rstc), + "Couldn't get reset control\n"); + goto err_cleanup; + } + + ret = reset_control_assert(rstc); + if (ret) { + ret = dev_err_probe(&pdev->dev, ret, + "Failed to assert the reset line\n"); + goto err_cleanup; + } + + usleep_range(3000, 5000); + + ret = reset_control_deassert(rstc); + if (ret) { + ret = dev_err_probe(&pdev->dev, ret, + "Failed to deassert the reset line\n"); + goto err_cleanup; + } + + pm_runtime_enable(&pdev->dev); + + ret = v4l2_async_register_subdev(&csidev->sd); + if (ret < 0) + goto err_cleanup; + + dev_info(&pdev->dev, + "Probed CSI with %u lanes\n", csidev->num_lanes); + + return 0; + +err_cleanup: + v4l2_async_nf_cleanup(&csidev->notifier); + return ret; +} + +static void stm32_csi_remove(struct platform_device *pdev) +{ + struct stm32_csi_dev *csidev = platform_get_drvdata(pdev); + + v4l2_async_unregister_subdev(&csidev->sd); + + pm_runtime_disable(&pdev->dev); +} + +static int stm32_csi_runtime_suspend(struct device *dev) +{ + struct stm32_csi_dev *csidev = dev_get_drvdata(dev); + int ret; + + clk_bulk_disable_unprepare(STM32_CSI_CLK_NB, csidev->clks); + + ret = regulator_bulk_disable(ARRAY_SIZE(csidev->supplies), + csidev->supplies); + if (ret < 0) + dev_err(dev, "cannot disable regulators %d\n", ret); + + return 0; +} + +static int stm32_csi_runtime_resume(struct device *dev) +{ + struct stm32_csi_dev *csidev = dev_get_drvdata(dev); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(csidev->supplies), + csidev->supplies); + if (ret) + goto error_out; + + ret = clk_bulk_prepare_enable(STM32_CSI_CLK_NB, csidev->clks); + if (ret) + goto error_disable_supplies; + + return 0; + +error_disable_supplies: + ret = regulator_bulk_disable(ARRAY_SIZE(csidev->supplies), csidev->supplies); + if (ret < 0) + dev_err(dev, "cannot disable regulators %d\n", ret); +error_out: + dev_err(csidev->dev, "Failed to resume: %d\n", ret); + + return ret; +} + +static const struct of_device_id stm32_csi_of_table[] = { + { .compatible = "st,stm32mp25-csi", }, + { /* end node */ }, +}; +MODULE_DEVICE_TABLE(of, stm32_csi_of_table); + +static const struct dev_pm_ops stm32_csi_pm_ops = { + RUNTIME_PM_OPS(stm32_csi_runtime_suspend, + stm32_csi_runtime_resume, NULL) +}; + +static struct platform_driver stm32_csi_driver = { + .driver = { + .name = "stm32-csi", + .of_match_table = stm32_csi_of_table, + .pm = pm_ptr(&stm32_csi_pm_ops), + }, + .probe = stm32_csi_probe, + .remove = stm32_csi_remove, +}; + +module_platform_driver(stm32_csi_driver); + +MODULE_AUTHOR("Alain Volmat <alain.volmat@foss.st.com>"); +MODULE_DESCRIPTION("STM32 CSI controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c index ff3331af9406..13762861b769 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmi.c +++ b/drivers/media/platform/st/stm32/stm32-dcmi.c @@ -388,9 +388,9 @@ static void dcmi_set_crop(struct stm32_dcmi *dcmi) ((dcmi->crop.left << 1)); reg_write(dcmi->regs, DCMI_CWSTRT, start); - dev_dbg(dcmi->dev, "Cropping to %ux%u@%u:%u\n", - dcmi->crop.width, dcmi->crop.height, - dcmi->crop.left, dcmi->crop.top); + dev_dbg(dcmi->dev, "Cropping to (%d,%d)/%ux%u\n", + dcmi->crop.left, dcmi->crop.top, + dcmi->crop.width, dcmi->crop.height); /* Enable crop */ reg_set(dcmi->regs, DCMI_CR, CR_CROP); @@ -898,8 +898,6 @@ static const struct vb2_ops dcmi_video_qops = { .buf_queue = dcmi_buf_queue, .start_streaming = dcmi_start_streaming, .stop_streaming = dcmi_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static int dcmi_g_fmt_vid_cap(struct file *file, void *priv, @@ -1294,8 +1292,8 @@ static int dcmi_s_selection(struct file *file, void *priv, /* Crop if request is different than sensor resolution */ dcmi->do_crop = true; dcmi->crop = r; - dev_dbg(dcmi->dev, "s_selection: crop %ux%u@(%u,%u) from %ux%u\n", - r.width, r.height, r.left, r.top, + dev_dbg(dcmi->dev, "s_selection: crop (%d,%d)/%ux%u from %ux%u\n", + r.left, r.top, r.width, r.height, pix.width, pix.height); } else { /* Disable crop */ @@ -1684,18 +1682,14 @@ static int dcmi_formats_init(struct stm32_dcmi *dcmi) return -ENXIO; dcmi->num_of_sd_formats = num_fmts; - dcmi->sd_formats = devm_kcalloc(dcmi->dev, - num_fmts, sizeof(struct dcmi_format *), - GFP_KERNEL); + dcmi->sd_formats = devm_kmemdup_array(dcmi->dev, sd_fmts, num_fmts, + sizeof(*sd_fmts), GFP_KERNEL); if (!dcmi->sd_formats) { dev_err(dcmi->dev, "Could not allocate memory\n"); return -ENOMEM; } - memcpy(dcmi->sd_formats, sd_fmts, - num_fmts * sizeof(struct dcmi_format *)); dcmi->sd_format = dcmi->sd_formats[0]; - return 0; } @@ -1707,8 +1701,8 @@ static int dcmi_framesizes_init(struct stm32_dcmi *dcmi) .which = V4L2_SUBDEV_FORMAT_ACTIVE, .code = dcmi->sd_format->mbus_code, }; - unsigned int ret; unsigned int i; + int ret; /* Allocate discrete framesizes array */ while (!v4l2_subdev_call(subdev, pad, enum_frame_size, @@ -1814,8 +1808,8 @@ static int dcmi_graph_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_async_connection *asd) { struct stm32_dcmi *dcmi = notifier_to_dcmi(notifier); - unsigned int ret; int src_pad; + int ret; dev_dbg(dcmi->dev, "Subdev \"%s\" bound\n", subdev->name); @@ -2149,7 +2143,7 @@ static const struct dev_pm_ops dcmi_pm_ops = { static struct platform_driver stm32_dcmi_driver = { .probe = dcmi_probe, - .remove_new = dcmi_remove, + .remove = dcmi_remove, .driver = { .name = DRV_NAME, .of_match_table = of_match_ptr(stm32_dcmi_of_match), diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile index 8920d9388a21..159105fb40b8 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -stm32-dcmipp-y := dcmipp-core.o dcmipp-common.o dcmipp-parallel.o dcmipp-byteproc.o dcmipp-bytecap.o +stm32-dcmipp-y := dcmipp-core.o dcmipp-common.o dcmipp-input.o dcmipp-byteproc.o dcmipp-bytecap.o obj-$(CONFIG_VIDEO_STM32_DCMIPP) += stm32-dcmipp.o diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c index 9f768f011fa2..1c1b6b48918e 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c @@ -56,15 +56,32 @@ struct dcmipp_bytecap_pix_map { static const struct dcmipp_bytecap_pix_map dcmipp_bytecap_pix_map_list[] = { PIXMAP_MBUS_PFMT(RGB565_2X8_LE, RGB565), + PIXMAP_MBUS_PFMT(RGB565_1X16, RGB565), PIXMAP_MBUS_PFMT(YUYV8_2X8, YUYV), + PIXMAP_MBUS_PFMT(YUYV8_1X16, YUYV), PIXMAP_MBUS_PFMT(YVYU8_2X8, YVYU), + PIXMAP_MBUS_PFMT(YVYU8_1X16, YVYU), PIXMAP_MBUS_PFMT(UYVY8_2X8, UYVY), + PIXMAP_MBUS_PFMT(UYVY8_1X16, UYVY), PIXMAP_MBUS_PFMT(VYUY8_2X8, VYUY), + PIXMAP_MBUS_PFMT(VYUY8_1X16, VYUY), PIXMAP_MBUS_PFMT(Y8_1X8, GREY), PIXMAP_MBUS_PFMT(SBGGR8_1X8, SBGGR8), PIXMAP_MBUS_PFMT(SGBRG8_1X8, SGBRG8), PIXMAP_MBUS_PFMT(SGRBG8_1X8, SGRBG8), PIXMAP_MBUS_PFMT(SRGGB8_1X8, SRGGB8), + PIXMAP_MBUS_PFMT(SBGGR10_1X10, SBGGR10), + PIXMAP_MBUS_PFMT(SGBRG10_1X10, SGBRG10), + PIXMAP_MBUS_PFMT(SGRBG10_1X10, SGRBG10), + PIXMAP_MBUS_PFMT(SRGGB10_1X10, SRGGB10), + PIXMAP_MBUS_PFMT(SBGGR12_1X12, SBGGR12), + PIXMAP_MBUS_PFMT(SGBRG12_1X12, SGBRG12), + PIXMAP_MBUS_PFMT(SGRBG12_1X12, SGRBG12), + PIXMAP_MBUS_PFMT(SRGGB12_1X12, SRGGB12), + PIXMAP_MBUS_PFMT(SBGGR14_1X14, SBGGR14), + PIXMAP_MBUS_PFMT(SGBRG14_1X14, SGBRG14), + PIXMAP_MBUS_PFMT(SGRBG14_1X14, SGRBG14), + PIXMAP_MBUS_PFMT(SRGGB14_1X14, SRGGB14), PIXMAP_MBUS_PFMT(JPEG_1X8, JPEG), }; @@ -112,6 +129,7 @@ struct dcmipp_bytecap_device { u32 sequence; struct media_pipeline pipe; struct v4l2_subdev *s_subdev; + u32 s_subdev_pad_nb; enum dcmipp_state state; @@ -250,34 +268,34 @@ static int dcmipp_bytecap_enum_fmt_vid_cap(struct file *file, void *priv, { const struct dcmipp_bytecap_pix_map *vpix; unsigned int index = f->index; - unsigned int i; + unsigned int i, prev_pixelformat = 0; - if (f->mbus_code) { - /* - * If a media bus code is specified, only enumerate formats - * compatible with it. - */ - for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) { - vpix = &dcmipp_bytecap_pix_map_list[i]; - if (vpix->code != f->mbus_code) - continue; + /* + * List up all formats (or only ones matching f->mbus_code), taking + * care of removing duplicated entries (due to support of both + * parallel & csi 16 bits formats + */ + for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) { + vpix = &dcmipp_bytecap_pix_map_list[i]; + /* Skip formats not matching requested mbus code */ + if (f->mbus_code && vpix->code != f->mbus_code) + continue; - if (index == 0) - break; + /* Skip duplicated pixelformat */ + if (vpix->pixelformat == prev_pixelformat) + continue; - index--; - } + prev_pixelformat = vpix->pixelformat; - if (i == ARRAY_SIZE(dcmipp_bytecap_pix_map_list)) - return -EINVAL; - } else { - /* Otherwise, enumerate all formats. */ - if (f->index >= ARRAY_SIZE(dcmipp_bytecap_pix_map_list)) - return -EINVAL; + if (index == 0) + break; - vpix = &dcmipp_bytecap_pix_map_list[f->index]; + index--; } + if (i == ARRAY_SIZE(dcmipp_bytecap_pix_map_list)) + return -EINVAL; + f->pixelformat = vpix->pixelformat; return 0; @@ -337,33 +355,6 @@ static const struct v4l2_ioctl_ops dcmipp_bytecap_ioctl_ops = { .vidioc_streamoff = vb2_ioctl_streamoff, }; -static int dcmipp_pipeline_s_stream(struct dcmipp_bytecap_device *vcap, - int state) -{ - struct media_pad *pad; - int ret; - - /* - * Get source subdev - since link is IMMUTABLE, pointer is cached - * within the dcmipp_bytecap_device structure - */ - if (!vcap->s_subdev) { - pad = media_pad_remote_pad_first(&vcap->vdev.entity.pads[0]); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) - return -EINVAL; - vcap->s_subdev = media_entity_to_v4l2_subdev(pad->entity); - } - - ret = v4l2_subdev_call(vcap->s_subdev, video, s_stream, state); - if (ret < 0) { - dev_err(vcap->dev, "failed to %s streaming (%d)\n", - state ? "start" : "stop", ret); - return ret; - } - - return 0; -} - static void dcmipp_start_capture(struct dcmipp_bytecap_device *vcap, struct dcmipp_buf *buf) { @@ -395,11 +386,24 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq, struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vq); struct media_entity *entity = &vcap->vdev.entity; struct dcmipp_buf *buf; + struct media_pad *pad; int ret; vcap->sequence = 0; memset(&vcap->count, 0, sizeof(vcap->count)); + /* + * Get source subdev - since link is IMMUTABLE, pointer is cached + * within the dcmipp_bytecap_device structure + */ + if (!vcap->s_subdev) { + pad = media_pad_remote_pad_first(&vcap->vdev.entity.pads[0]); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + return -EINVAL; + vcap->s_subdev = media_entity_to_v4l2_subdev(pad->entity); + vcap->s_subdev_pad_nb = pad->index; + } + ret = pm_runtime_resume_and_get(vcap->dev); if (ret < 0) { dev_err(vcap->dev, "%s: Failed to start streaming, cannot get sync (%d)\n", @@ -414,7 +418,8 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq, goto err_pm_put; } - ret = dcmipp_pipeline_s_stream(vcap, 1); + ret = v4l2_subdev_enable_streams(vcap->s_subdev, + vcap->s_subdev_pad_nb, BIT_ULL(0)); if (ret) goto err_media_pipeline_stop; @@ -482,7 +487,10 @@ static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq) int ret; u32 status; - dcmipp_pipeline_s_stream(vcap, 0); + ret = v4l2_subdev_disable_streams(vcap->s_subdev, + vcap->s_subdev_pad_nb, BIT_ULL(0)); + if (ret) + dev_warn(vcap->dev, "Failed to disable stream\n"); /* Stop the media pipeline */ media_pipeline_stop(vcap->vdev.entity.pads); @@ -625,12 +633,6 @@ static const struct vb2_ops dcmipp_bytecap_qops = { .buf_prepare = dcmipp_bytecap_buf_prepare, .buf_queue = dcmipp_bytecap_buf_queue, .queue_setup = dcmipp_bytecap_queue_setup, - /* - * Since q->lock is set we can use the standard - * vb2_ops_wait_prepare/finish helper functions. - */ - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; static void dcmipp_bytecap_release(struct video_device *vdev) @@ -816,8 +818,7 @@ static int dcmipp_bytecap_link_validate(struct media_link *link) .which = V4L2_SUBDEV_FORMAT_ACTIVE, .pad = link->source->index, }; - const struct dcmipp_bytecap_pix_map *vpix; - int ret; + int ret, i; ret = v4l2_subdev_call(source_sd, pad, get_fmt, NULL, &source_fmt); if (ret < 0) @@ -831,10 +832,17 @@ static int dcmipp_bytecap_link_validate(struct media_link *link) return -EINVAL; } - vpix = dcmipp_bytecap_pix_map_by_pixelformat(vcap->format.pixelformat); - if (source_fmt.format.code != vpix->code) { - dev_err(vcap->dev, "Wrong mbus_code 0x%x, (0x%x expected)\n", - vpix->code, source_fmt.format.code); + for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) { + if (dcmipp_bytecap_pix_map_list[i].pixelformat == + vcap->format.pixelformat && + dcmipp_bytecap_pix_map_list[i].code == + source_fmt.format.code) + break; + } + + if (i == ARRAY_SIZE(dcmipp_bytecap_pix_map_list)) { + dev_err(vcap->dev, "mbus code 0x%x do not match capture device format (0x%x)\n", + vcap->format.pixelformat, source_fmt.format.code); return -EINVAL; } @@ -893,7 +901,7 @@ struct dcmipp_ent_device *dcmipp_bytecap_ent_init(struct device *dev, q->dev = dev; /* DCMIPP requires 16 bytes aligned buffers */ - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32) & ~0x0f); + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); if (ret) { dev_err(dev, "Failed to set DMA mask\n"); goto err_mutex_destroy; diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c index 5a361ad6b023..db76a02a1848 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c @@ -48,15 +48,32 @@ struct dcmipp_byteproc_pix_map { } static const struct dcmipp_byteproc_pix_map dcmipp_byteproc_pix_map_list[] = { PIXMAP_MBUS_BPP(RGB565_2X8_LE, 2), + PIXMAP_MBUS_BPP(RGB565_1X16, 2), PIXMAP_MBUS_BPP(YUYV8_2X8, 2), + PIXMAP_MBUS_BPP(YUYV8_1X16, 2), PIXMAP_MBUS_BPP(YVYU8_2X8, 2), + PIXMAP_MBUS_BPP(YVYU8_1X16, 2), PIXMAP_MBUS_BPP(UYVY8_2X8, 2), + PIXMAP_MBUS_BPP(UYVY8_1X16, 2), PIXMAP_MBUS_BPP(VYUY8_2X8, 2), + PIXMAP_MBUS_BPP(VYUY8_1X16, 2), PIXMAP_MBUS_BPP(Y8_1X8, 1), PIXMAP_MBUS_BPP(SBGGR8_1X8, 1), PIXMAP_MBUS_BPP(SGBRG8_1X8, 1), PIXMAP_MBUS_BPP(SGRBG8_1X8, 1), PIXMAP_MBUS_BPP(SRGGB8_1X8, 1), + PIXMAP_MBUS_BPP(SBGGR10_1X10, 2), + PIXMAP_MBUS_BPP(SGBRG10_1X10, 2), + PIXMAP_MBUS_BPP(SGRBG10_1X10, 2), + PIXMAP_MBUS_BPP(SRGGB10_1X10, 2), + PIXMAP_MBUS_BPP(SBGGR12_1X12, 2), + PIXMAP_MBUS_BPP(SGBRG12_1X12, 2), + PIXMAP_MBUS_BPP(SGRBG12_1X12, 2), + PIXMAP_MBUS_BPP(SRGGB12_1X12, 2), + PIXMAP_MBUS_BPP(SBGGR14_1X14, 2), + PIXMAP_MBUS_BPP(SGBRG14_1X14, 2), + PIXMAP_MBUS_BPP(SGRBG14_1X14, 2), + PIXMAP_MBUS_BPP(SRGGB14_1X14, 2), PIXMAP_MBUS_BPP(JPEG_1X8, 1), }; @@ -78,7 +95,6 @@ struct dcmipp_byteproc_device { struct v4l2_subdev sd; struct device *dev; void __iomem *regs; - bool streaming; }; static const struct v4l2_mbus_framefmt fmt_default = { @@ -239,11 +255,10 @@ static int dcmipp_byteproc_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { - struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf; struct v4l2_rect *crop, *compose; - if (byteproc->streaming) + if (v4l2_subdev_is_streaming(sd)) return -EBUSY; mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); @@ -358,8 +373,8 @@ static int dcmipp_byteproc_set_selection(struct v4l2_subdev *sd, mf->width = s->r.width; mf->height = s->r.height; - dev_dbg(byteproc->dev, "s_selection: crop %ux%u@(%u,%u)\n", - crop->width, crop->height, crop->left, crop->top); + dev_dbg(byteproc->dev, "s_selection: crop (%d,%d)/%ux%u\n", + crop->left, crop->top, crop->width, crop->height); break; case V4L2_SEL_TGT_COMPOSE: mf = v4l2_subdev_state_get_format(sd_state, 0); @@ -371,9 +386,9 @@ static int dcmipp_byteproc_set_selection(struct v4l2_subdev *sd, mf->width = s->r.width; mf->height = s->r.height; - dev_dbg(byteproc->dev, "s_selection: compose %ux%u@(%u,%u)\n", - compose->width, compose->height, - compose->left, compose->top); + dev_dbg(byteproc->dev, "s_selection: compose (%d,%d)/%ux%u\n", + compose->left, compose->top, + compose->width, compose->height); break; default: return -EINVAL; @@ -382,30 +397,19 @@ static int dcmipp_byteproc_set_selection(struct v4l2_subdev *sd, return 0; } -static const struct v4l2_subdev_pad_ops dcmipp_byteproc_pad_ops = { - .enum_mbus_code = dcmipp_byteproc_enum_mbus_code, - .enum_frame_size = dcmipp_byteproc_enum_frame_size, - .get_fmt = v4l2_subdev_get_fmt, - .set_fmt = dcmipp_byteproc_set_fmt, - .get_selection = dcmipp_byteproc_get_selection, - .set_selection = dcmipp_byteproc_set_selection, -}; - static int dcmipp_byteproc_configure_scale_crop - (struct dcmipp_byteproc_device *byteproc) + (struct dcmipp_byteproc_device *byteproc, + struct v4l2_subdev_state *state) { const struct dcmipp_byteproc_pix_map *vpix; - struct v4l2_subdev_state *state; struct v4l2_mbus_framefmt *sink_fmt; u32 hprediv, vprediv; struct v4l2_rect *compose, *crop; u32 val = 0; - state = v4l2_subdev_lock_and_get_active_state(&byteproc->sd); sink_fmt = v4l2_subdev_state_get_format(state, 0); compose = v4l2_subdev_state_get_compose(state, 0); crop = v4l2_subdev_state_get_crop(state, 1); - v4l2_subdev_unlock_state(state); /* find output format bpp */ vpix = dcmipp_byteproc_pix_map_by_code(sink_fmt->code); @@ -460,48 +464,73 @@ static int dcmipp_byteproc_configure_scale_crop return 0; } -static int dcmipp_byteproc_s_stream(struct v4l2_subdev *sd, int enable) +static int dcmipp_byteproc_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) { struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd); struct v4l2_subdev *s_subdev; - struct media_pad *pad; - int ret = 0; + struct media_pad *s_pad; + int ret; /* Get source subdev */ - pad = media_pad_remote_pad_first(&sd->entity.pads[0]); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]); + if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity)) return -EINVAL; - s_subdev = media_entity_to_v4l2_subdev(pad->entity); - - if (enable) { - ret = dcmipp_byteproc_configure_scale_crop(byteproc); - if (ret) - return ret; - - ret = v4l2_subdev_call(s_subdev, video, s_stream, enable); - if (ret < 0) { - dev_err(byteproc->dev, - "failed to start source subdev streaming (%d)\n", - ret); - return ret; - } - } else { - ret = v4l2_subdev_call(s_subdev, video, s_stream, enable); - if (ret < 0) { - dev_err(byteproc->dev, - "failed to stop source subdev streaming (%d)\n", - ret); - return ret; - } + s_subdev = media_entity_to_v4l2_subdev(s_pad->entity); + + ret = dcmipp_byteproc_configure_scale_crop(byteproc, state); + if (ret) + return ret; + + ret = v4l2_subdev_enable_streams(s_subdev, s_pad->index, BIT_ULL(0)); + if (ret < 0) { + dev_err(byteproc->dev, + "failed to start source subdev streaming (%d)\n", ret); + return ret; } - byteproc->streaming = enable; + return 0; +} + +static int dcmipp_byteproc_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd); + struct v4l2_subdev *s_subdev; + struct media_pad *s_pad; + int ret; + + /* Get source subdev */ + s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]); + if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity)) + return -EINVAL; + s_subdev = media_entity_to_v4l2_subdev(s_pad->entity); + + ret = v4l2_subdev_disable_streams(s_subdev, s_pad->index, BIT_ULL(0)); + if (ret < 0) { + dev_err(byteproc->dev, + "failed to start source subdev streaming (%d)\n", ret); + return ret; + } return 0; } +static const struct v4l2_subdev_pad_ops dcmipp_byteproc_pad_ops = { + .enum_mbus_code = dcmipp_byteproc_enum_mbus_code, + .enum_frame_size = dcmipp_byteproc_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = dcmipp_byteproc_set_fmt, + .get_selection = dcmipp_byteproc_get_selection, + .set_selection = dcmipp_byteproc_set_selection, + .enable_streams = dcmipp_byteproc_enable_streams, + .disable_streams = dcmipp_byteproc_disable_streams, +}; + static const struct v4l2_subdev_video_ops dcmipp_byteproc_video_ops = { - .s_stream = dcmipp_byteproc_s_stream, + .s_stream = v4l2_subdev_s_stream_helper, }; static const struct v4l2_subdev_ops dcmipp_byteproc_ops = { diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h index 7a7cf43baf24..fe5f97233f5e 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h @@ -199,11 +199,11 @@ static inline void __reg_clear(struct device *dev, void __iomem *base, u32 reg, } /* DCMIPP subdev init / release entry points */ -struct dcmipp_ent_device *dcmipp_par_ent_init(struct device *dev, +struct dcmipp_ent_device *dcmipp_inp_ent_init(struct device *dev, const char *entity_name, struct v4l2_device *v4l2_dev, void __iomem *regs); -void dcmipp_par_ent_release(struct dcmipp_ent_device *ved); +void dcmipp_inp_ent_release(struct dcmipp_ent_device *ved); struct dcmipp_ent_device * dcmipp_byteproc_ent_init(struct device *dev, const char *entity_name, struct v4l2_device *v4l2_dev, void __iomem *regs); diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c index 4acc3b90d03a..1b7bae3266c8 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c @@ -40,6 +40,7 @@ struct dcmipp_device { /* Hardware resources */ void __iomem *regs; + struct clk *mclk; struct clk *kclk; /* The pipeline configuration */ @@ -87,6 +88,9 @@ struct dcmipp_pipeline_config { size_t num_ents; const struct dcmipp_ent_link *links; size_t num_links; + u32 hw_revision; + bool has_csi2; + bool needs_mclk; }; /* -------------------------------------------------------------------------- @@ -95,9 +99,9 @@ struct dcmipp_pipeline_config { static const struct dcmipp_ent_config stm32mp13_ent_config[] = { { - .name = "dcmipp_parallel", - .init = dcmipp_par_ent_init, - .release = dcmipp_par_ent_release, + .name = "dcmipp_input", + .init = dcmipp_inp_ent_init, + .release = dcmipp_inp_ent_release, }, { .name = "dcmipp_dump_postproc", @@ -111,22 +115,60 @@ static const struct dcmipp_ent_config stm32mp13_ent_config[] = { }, }; -#define ID_PARALLEL 0 +#define ID_INPUT 0 #define ID_DUMP_BYTEPROC 1 #define ID_DUMP_CAPTURE 2 static const struct dcmipp_ent_link stm32mp13_ent_links[] = { - DCMIPP_ENT_LINK(ID_PARALLEL, 1, ID_DUMP_BYTEPROC, 0, + DCMIPP_ENT_LINK(ID_INPUT, 1, ID_DUMP_BYTEPROC, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), DCMIPP_ENT_LINK(ID_DUMP_BYTEPROC, 1, ID_DUMP_CAPTURE, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), }; +#define DCMIPP_STM32MP13_VERR 0x10 static const struct dcmipp_pipeline_config stm32mp13_pipe_cfg = { .ents = stm32mp13_ent_config, .num_ents = ARRAY_SIZE(stm32mp13_ent_config), .links = stm32mp13_ent_links, - .num_links = ARRAY_SIZE(stm32mp13_ent_links) + .num_links = ARRAY_SIZE(stm32mp13_ent_links), + .hw_revision = DCMIPP_STM32MP13_VERR +}; + +static const struct dcmipp_ent_config stm32mp25_ent_config[] = { + { + .name = "dcmipp_input", + .init = dcmipp_inp_ent_init, + .release = dcmipp_inp_ent_release, + }, + { + .name = "dcmipp_dump_postproc", + .init = dcmipp_byteproc_ent_init, + .release = dcmipp_byteproc_ent_release, + }, + { + .name = "dcmipp_dump_capture", + .init = dcmipp_bytecap_ent_init, + .release = dcmipp_bytecap_ent_release, + }, +}; + +static const struct dcmipp_ent_link stm32mp25_ent_links[] = { + DCMIPP_ENT_LINK(ID_INPUT, 1, ID_DUMP_BYTEPROC, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), + DCMIPP_ENT_LINK(ID_DUMP_BYTEPROC, 1, ID_DUMP_CAPTURE, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE), +}; + +#define DCMIPP_STM32MP25_VERR 0x30 +static const struct dcmipp_pipeline_config stm32mp25_pipe_cfg = { + .ents = stm32mp25_ent_config, + .num_ents = ARRAY_SIZE(stm32mp25_ent_config), + .links = stm32mp25_ent_links, + .num_links = ARRAY_SIZE(stm32mp25_ent_links), + .hw_revision = DCMIPP_STM32MP25_VERR, + .has_csi2 = true, + .needs_mclk = true }; #define LINK_FLAG_TO_STR(f) ((f) == 0 ? "" :\ @@ -202,13 +244,14 @@ static int dcmipp_create_subdevs(struct dcmipp_device *dcmipp) return 0; err_init_entity: - while (i > 0) - dcmipp->pipe_cfg->ents[i - 1].release(dcmipp->entity[i - 1]); + while (i-- > 0) + dcmipp->pipe_cfg->ents[i].release(dcmipp->entity[i]); return ret; } static const struct of_device_id dcmipp_of_match[] = { { .compatible = "st,stm32mp13-dcmipp", .data = &stm32mp13_pipe_cfg }, + { .compatible = "st,stm32mp25-dcmipp", .data = &stm32mp25_pipe_cfg }, { /* end node */ }, }; MODULE_DEVICE_TABLE(of, dcmipp_of_match); @@ -257,11 +300,14 @@ static int dcmipp_graph_notify_bound(struct v4l2_async_notifier *notifier, struct v4l2_async_connection *asd) { struct dcmipp_device *dcmipp = notifier_to_dcmipp(notifier); - unsigned int ret; - int src_pad; + int ret = -EINVAL; + int src_pad, i; struct dcmipp_ent_device *sink; - struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_PARALLEL }; + struct v4l2_fwnode_endpoint vep = { 0 }; struct fwnode_handle *ep; + enum v4l2_mbus_type supported_types[] = { + V4L2_MBUS_PARALLEL, V4L2_MBUS_BT656, V4L2_MBUS_CSI2_DPHY + }; dev_dbg(dcmipp->dev, "Subdev \"%s\" bound\n", subdev->name); @@ -281,21 +327,28 @@ static int dcmipp_graph_notify_bound(struct v4l2_async_notifier *notifier, return -ENODEV; } - /* Check for parallel bus-type first, then bt656 */ - ret = v4l2_fwnode_endpoint_parse(ep, &vep); - if (ret) { - vep.bus_type = V4L2_MBUS_BT656; + /* Check for supported MBUS type */ + for (i = 0; i < ARRAY_SIZE(supported_types); i++) { + /* Only MP25 supports CSI input */ + if (supported_types[i] == V4L2_MBUS_CSI2_DPHY && + !dcmipp->pipe_cfg->has_csi2) + continue; + + vep.bus_type = supported_types[i]; ret = v4l2_fwnode_endpoint_parse(ep, &vep); - if (ret) { - dev_err(dcmipp->dev, "Could not parse the endpoint\n"); - fwnode_handle_put(ep); - return ret; - } + if (!ret) + break; } fwnode_handle_put(ep); - if (vep.bus.parallel.bus_width == 0) { + if (ret) { + dev_err(dcmipp->dev, "Could not parse the endpoint\n"); + return ret; + } + + if (vep.bus_type != V4L2_MBUS_CSI2_DPHY && + vep.bus.parallel.bus_width == 0) { dev_err(dcmipp->dev, "Invalid parallel interface bus-width\n"); return -ENODEV; } @@ -308,11 +361,13 @@ static int dcmipp_graph_notify_bound(struct v4l2_async_notifier *notifier, return -ENODEV; } - /* Parallel input device detected, connect it to parallel subdev */ - sink = dcmipp->entity[ID_PARALLEL]; - sink->bus.flags = vep.bus.parallel.flags; - sink->bus.bus_width = vep.bus.parallel.bus_width; - sink->bus.data_shift = vep.bus.parallel.data_shift; + /* Connect input device to the dcmipp_input subdev */ + sink = dcmipp->entity[ID_INPUT]; + if (vep.bus_type != V4L2_MBUS_CSI2_DPHY) { + sink->bus.flags = vep.bus.parallel.flags; + sink->bus.bus_width = vep.bus.parallel.bus_width; + sink->bus.data_shift = vep.bus.parallel.data_shift; + } sink->bus_type = vep.bus_type; ret = media_create_pad_link(&subdev->entity, src_pad, sink->ent, 0, MEDIA_LNK_FL_IMMUTABLE | @@ -411,7 +466,7 @@ static int dcmipp_graph_init(struct dcmipp_device *dcmipp) static int dcmipp_probe(struct platform_device *pdev) { struct dcmipp_device *dcmipp; - struct clk *kclk; + struct clk *kclk, *mclk; const struct dcmipp_pipeline_config *pipe_cfg; struct reset_control *rstc; int irq; @@ -471,12 +526,20 @@ static int dcmipp_probe(struct platform_device *pdev) return ret; } - kclk = devm_clk_get(&pdev->dev, NULL); + kclk = devm_clk_get(&pdev->dev, "kclk"); if (IS_ERR(kclk)) return dev_err_probe(&pdev->dev, PTR_ERR(kclk), "Unable to get kclk\n"); dcmipp->kclk = kclk; + if (dcmipp->pipe_cfg->needs_mclk) { + mclk = devm_clk_get(&pdev->dev, "mclk"); + if (IS_ERR(mclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(mclk), + "Unable to get mclk\n"); + dcmipp->mclk = mclk; + } + dcmipp->entity = devm_kcalloc(&pdev->dev, dcmipp->pipe_cfg->num_ents, sizeof(*dcmipp->entity), GFP_KERNEL); if (!dcmipp->entity) @@ -496,6 +559,7 @@ static int dcmipp_probe(struct platform_device *pdev) /* Initialize media device */ strscpy(dcmipp->mdev.model, DCMIPP_MDEV_MODEL_NAME, sizeof(dcmipp->mdev.model)); + dcmipp->mdev.hw_revision = pipe_cfg->hw_revision; dcmipp->mdev.dev = &pdev->dev; media_device_init(&dcmipp->mdev); @@ -538,6 +602,7 @@ static int dcmipp_runtime_suspend(struct device *dev) struct dcmipp_device *dcmipp = dev_get_drvdata(dev); clk_disable_unprepare(dcmipp->kclk); + clk_disable_unprepare(dcmipp->mclk); return 0; } @@ -547,9 +612,17 @@ static int dcmipp_runtime_resume(struct device *dev) struct dcmipp_device *dcmipp = dev_get_drvdata(dev); int ret; + ret = clk_prepare_enable(dcmipp->mclk); + if (ret) { + dev_err(dev, "%s: Failed to prepare_enable mclk\n", __func__); + return ret; + } + ret = clk_prepare_enable(dcmipp->kclk); - if (ret) + if (ret) { + clk_disable_unprepare(dcmipp->mclk); dev_err(dev, "%s: Failed to prepare_enable kclk\n", __func__); + } return ret; } @@ -583,7 +656,7 @@ static const struct dev_pm_ops dcmipp_pm_ops = { static struct platform_driver dcmipp_pdrv = { .probe = dcmipp_probe, - .remove_new = dcmipp_remove, + .remove = dcmipp_remove, .driver = { .name = DCMIPP_PDEV_NAME, .of_match_table = dcmipp_of_match, diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c new file mode 100644 index 000000000000..7e5311b67d7e --- /dev/null +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c @@ -0,0 +1,540 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for STM32 Digital Camera Memory Interface Pixel Processor + * + * Copyright (C) STMicroelectronics SA 2023 + * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com> + * Alain Volmat <alain.volmat@foss.st.com> + * for STMicroelectronics. + */ + +#include <linux/v4l2-mediabus.h> +#include <media/mipi-csi2.h> +#include <media/v4l2-event.h> +#include <media/v4l2-subdev.h> + +#include "dcmipp-common.h" + +#define DCMIPP_PRCR 0x104 +#define DCMIPP_PRCR_FORMAT_SHIFT 16 +#define DCMIPP_PRCR_FORMAT_YUV422 0x1e +#define DCMIPP_PRCR_FORMAT_RGB565 0x22 +#define DCMIPP_PRCR_FORMAT_RAW8 0x2a +#define DCMIPP_PRCR_FORMAT_RAW10 0x2b +#define DCMIPP_PRCR_FORMAT_RAW12 0x2c +#define DCMIPP_PRCR_FORMAT_RAW14 0x2d +#define DCMIPP_PRCR_FORMAT_G8 0x4a +#define DCMIPP_PRCR_FORMAT_BYTE_STREAM 0x5a +#define DCMIPP_PRCR_ESS BIT(4) +#define DCMIPP_PRCR_PCKPOL BIT(5) +#define DCMIPP_PRCR_HSPOL BIT(6) +#define DCMIPP_PRCR_VSPOL BIT(7) +#define DCMIPP_PRCR_ENABLE BIT(14) +#define DCMIPP_PRCR_SWAPCYCLES BIT(25) + +#define DCMIPP_PRESCR 0x108 +#define DCMIPP_PRESUR 0x10c + +#define DCMIPP_CMCR 0x204 +#define DCMIPP_CMCR_INSEL BIT(0) + +#define DCMIPP_P0FSCR 0x404 +#define DCMIPP_P0FSCR_DTMODE_MASK GENMASK(17, 16) +#define DCMIPP_P0FSCR_DTMODE_SHIFT 16 +#define DCMIPP_P0FSCR_DTMODE_DTIDA 0x00 +#define DCMIPP_P0FSCR_DTMODE_ALLDT 0x03 +#define DCMIPP_P0FSCR_DTIDA_MASK GENMASK(5, 0) +#define DCMIPP_P0FSCR_DTIDA_SHIFT 0 + +#define IS_SINK(pad) (!(pad)) +#define IS_SRC(pad) ((pad)) + +struct dcmipp_inp_pix_map { + unsigned int code_sink; + unsigned int code_src; + /* Parallel related information */ + u8 prcr_format; + u8 prcr_swapcycles; + /* CSI related information */ + unsigned int dt; +}; + +#define PIXMAP_SINK_SRC_PRCR_SWAP(sink, src, prcr, swap, data_type) \ + { \ + .code_sink = MEDIA_BUS_FMT_##sink, \ + .code_src = MEDIA_BUS_FMT_##src, \ + .prcr_format = DCMIPP_PRCR_FORMAT_##prcr, \ + .prcr_swapcycles = swap, \ + .dt = data_type, \ + } +static const struct dcmipp_inp_pix_map dcmipp_inp_pix_map_list[] = { + /* RGB565 */ + PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_LE, RGB565_2X8_LE, RGB565, 1, MIPI_CSI2_DT_RGB565), + PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_BE, RGB565_2X8_LE, RGB565, 0, MIPI_CSI2_DT_RGB565), + PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_1X16, RGB565_1X16, RGB565, 0, MIPI_CSI2_DT_RGB565), + /* YUV422 */ + PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, YUYV8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_1X16, YUYV8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, UYVY8_2X8, YUV422, 0, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, UYVY8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_1X16, UYVY8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, YUYV8_2X8, YUV422, 0, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_2X8, YVYU8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_1X16, YVYU8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_2X8, VYUY8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B), + PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_1X16, VYUY8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B), + /* GREY */ + PIXMAP_SINK_SRC_PRCR_SWAP(Y8_1X8, Y8_1X8, G8, 0, MIPI_CSI2_DT_RAW8), + /* Raw Bayer */ + PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR8_1X8, SBGGR8_1X8, RAW8, 0, MIPI_CSI2_DT_RAW8), + PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG8_1X8, SGBRG8_1X8, RAW8, 0, MIPI_CSI2_DT_RAW8), + PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG8_1X8, SGRBG8_1X8, RAW8, 0, MIPI_CSI2_DT_RAW8), + PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB8_1X8, SRGGB8_1X8, RAW8, 0, MIPI_CSI2_DT_RAW8), + PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR10_1X10, SBGGR10_1X10, RAW10, 0, MIPI_CSI2_DT_RAW10), + PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG10_1X10, SGBRG10_1X10, RAW10, 0, MIPI_CSI2_DT_RAW10), + PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG10_1X10, SGRBG10_1X10, RAW10, 0, MIPI_CSI2_DT_RAW10), + PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB10_1X10, SRGGB10_1X10, RAW10, 0, MIPI_CSI2_DT_RAW10), + PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR12_1X12, SBGGR12_1X12, RAW12, 0, MIPI_CSI2_DT_RAW12), + PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG12_1X12, SGBRG12_1X12, RAW12, 0, MIPI_CSI2_DT_RAW12), + PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG12_1X12, SGRBG12_1X12, RAW12, 0, MIPI_CSI2_DT_RAW12), + PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB12_1X12, SRGGB12_1X12, RAW12, 0, MIPI_CSI2_DT_RAW12), + PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR14_1X14, SBGGR14_1X14, RAW14, 0, MIPI_CSI2_DT_RAW14), + PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG14_1X14, SGBRG14_1X14, RAW14, 0, MIPI_CSI2_DT_RAW14), + PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG14_1X14, SGRBG14_1X14, RAW14, 0, MIPI_CSI2_DT_RAW14), + PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB14_1X14, SRGGB14_1X14, RAW14, 0, MIPI_CSI2_DT_RAW14), + /* JPEG */ + PIXMAP_SINK_SRC_PRCR_SWAP(JPEG_1X8, JPEG_1X8, BYTE_STREAM, 0, 0), +}; + +/* + * Search through the pix_map table, skipping two consecutive entry with the + * same code + */ +static inline const struct dcmipp_inp_pix_map *dcmipp_inp_pix_map_by_index + (unsigned int index, + unsigned int pad) +{ + unsigned int i = 0; + u32 prev_code = 0, cur_code; + + while (i < ARRAY_SIZE(dcmipp_inp_pix_map_list)) { + if (IS_SRC(pad)) + cur_code = dcmipp_inp_pix_map_list[i].code_src; + else + cur_code = dcmipp_inp_pix_map_list[i].code_sink; + + if (cur_code == prev_code) { + i++; + continue; + } + prev_code = cur_code; + + if (index == 0) + break; + i++; + index--; + } + + if (i >= ARRAY_SIZE(dcmipp_inp_pix_map_list)) + return NULL; + + return &dcmipp_inp_pix_map_list[i]; +} + +static inline const struct dcmipp_inp_pix_map *dcmipp_inp_pix_map_by_code + (u32 code_sink, u32 code_src) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dcmipp_inp_pix_map_list); i++) { + if ((dcmipp_inp_pix_map_list[i].code_sink == code_sink && + dcmipp_inp_pix_map_list[i].code_src == code_src) || + (dcmipp_inp_pix_map_list[i].code_sink == code_src && + dcmipp_inp_pix_map_list[i].code_src == code_sink) || + (dcmipp_inp_pix_map_list[i].code_sink == code_sink && + code_src == 0) || + (code_sink == 0 && + dcmipp_inp_pix_map_list[i].code_src == code_src)) + return &dcmipp_inp_pix_map_list[i]; + } + return NULL; +} + +struct dcmipp_inp_device { + struct dcmipp_ent_device ved; + struct v4l2_subdev sd; + struct device *dev; + void __iomem *regs; +}; + +static const struct v4l2_mbus_framefmt fmt_default = { + .width = DCMIPP_FMT_WIDTH_DEFAULT, + .height = DCMIPP_FMT_HEIGHT_DEFAULT, + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .field = V4L2_FIELD_NONE, + .colorspace = DCMIPP_COLORSPACE_DEFAULT, + .ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT, + .quantization = DCMIPP_QUANTIZATION_DEFAULT, + .xfer_func = DCMIPP_XFER_FUNC_DEFAULT, +}; + +static int dcmipp_inp_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + unsigned int i; + + for (i = 0; i < sd->entity.num_pads; i++) { + struct v4l2_mbus_framefmt *mf; + + mf = v4l2_subdev_state_get_format(sd_state, i); + *mf = fmt_default; + } + + return 0; +} + +static int dcmipp_inp_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + const struct dcmipp_inp_pix_map *vpix = + dcmipp_inp_pix_map_by_index(code->index, code->pad); + + if (!vpix) + return -EINVAL; + + code->code = IS_SRC(code->pad) ? vpix->code_src : vpix->code_sink; + + return 0; +} + +static int dcmipp_inp_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + const struct dcmipp_inp_pix_map *vpix; + + if (fse->index) + return -EINVAL; + + /* Only accept code in the pix map table */ + vpix = dcmipp_inp_pix_map_by_code(IS_SINK(fse->pad) ? fse->code : 0, + IS_SRC(fse->pad) ? fse->code : 0); + if (!vpix) + return -EINVAL; + + fse->min_width = DCMIPP_FRAME_MIN_WIDTH; + fse->max_width = DCMIPP_FRAME_MAX_WIDTH; + fse->min_height = DCMIPP_FRAME_MIN_HEIGHT; + fse->max_height = DCMIPP_FRAME_MAX_HEIGHT; + + return 0; +} + +static void dcmipp_inp_adjust_fmt(struct dcmipp_inp_device *inp, + struct v4l2_mbus_framefmt *fmt, __u32 pad) +{ + const struct dcmipp_inp_pix_map *vpix; + + /* Only accept code in the pix map table */ + vpix = dcmipp_inp_pix_map_by_code(IS_SINK(pad) ? fmt->code : 0, + IS_SRC(pad) ? fmt->code : 0); + if (!vpix) + fmt->code = fmt_default.code; + + /* Exclude JPEG if BT656 bus is selected */ + if (vpix && vpix->code_sink == MEDIA_BUS_FMT_JPEG_1X8 && + inp->ved.bus_type == V4L2_MBUS_BT656) + fmt->code = fmt_default.code; + + fmt->width = clamp_t(u32, fmt->width, DCMIPP_FRAME_MIN_WIDTH, + DCMIPP_FRAME_MAX_WIDTH) & ~1; + fmt->height = clamp_t(u32, fmt->height, DCMIPP_FRAME_MIN_HEIGHT, + DCMIPP_FRAME_MAX_HEIGHT) & ~1; + + if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE) + fmt->field = fmt_default.field; + + dcmipp_colorimetry_clamp(fmt); +} + +static int dcmipp_inp_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct dcmipp_inp_device *inp = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf; + + if (v4l2_subdev_is_streaming(sd)) + return -EBUSY; + + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); + + /* Set the new format */ + dcmipp_inp_adjust_fmt(inp, &fmt->format, fmt->pad); + + dev_dbg(inp->dev, "%s: format update: old:%dx%d (0x%x, %d, %d, %d, %d) new:%dx%d (0x%x, %d, %d, %d, %d)\n", + inp->sd.name, + /* old */ + mf->width, mf->height, mf->code, + mf->colorspace, mf->quantization, + mf->xfer_func, mf->ycbcr_enc, + /* new */ + fmt->format.width, fmt->format.height, fmt->format.code, + fmt->format.colorspace, fmt->format.quantization, + fmt->format.xfer_func, fmt->format.ycbcr_enc); + + *mf = fmt->format; + + /* When setting the sink format, report that format on the src pad */ + if (IS_SINK(fmt->pad)) { + mf = v4l2_subdev_state_get_format(sd_state, 1); + *mf = fmt->format; + dcmipp_inp_adjust_fmt(inp, mf, 1); + } + + return 0; +} + +static int dcmipp_inp_configure_parallel(struct dcmipp_inp_device *inp, + struct v4l2_subdev_state *state) +{ + u32 val = 0; + const struct dcmipp_inp_pix_map *vpix; + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_mbus_framefmt *src_fmt; + + /* Set vertical synchronization polarity */ + if (inp->ved.bus.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) + val |= DCMIPP_PRCR_VSPOL; + + /* Set horizontal synchronization polarity */ + if (inp->ved.bus.flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) + val |= DCMIPP_PRCR_HSPOL; + + /* Set pixel clock polarity */ + if (inp->ved.bus.flags & V4L2_MBUS_PCLK_SAMPLE_RISING) + val |= DCMIPP_PRCR_PCKPOL; + + /* + * BT656 embedded synchronisation bus mode. + * + * Default SAV/EAV mode is supported here with default codes + * SAV=0xff000080 & EAV=0xff00009d. + * With DCMIPP this means LSC=SAV=0x80 & LEC=EAV=0x9d. + */ + if (inp->ved.bus_type == V4L2_MBUS_BT656) { + val |= DCMIPP_PRCR_ESS; + + /* Unmask all codes */ + reg_write(inp, DCMIPP_PRESUR, 0xffffffff);/* FEC:LEC:LSC:FSC */ + + /* Trig on LSC=0x80 & LEC=0x9d codes, ignore FSC and FEC */ + reg_write(inp, DCMIPP_PRESCR, 0xff9d80ff);/* FEC:LEC:LSC:FSC */ + } + + /* Set format */ + sink_fmt = v4l2_subdev_state_get_format(state, 0); + src_fmt = v4l2_subdev_state_get_format(state, 1); + + vpix = dcmipp_inp_pix_map_by_code(sink_fmt->code, src_fmt->code); + if (!vpix) { + dev_err(inp->dev, "Invalid sink/src format configuration\n"); + return -EINVAL; + } + + val |= vpix->prcr_format << DCMIPP_PRCR_FORMAT_SHIFT; + + /* swap cycles */ + if (vpix->prcr_swapcycles) + val |= DCMIPP_PRCR_SWAPCYCLES; + + reg_write(inp, DCMIPP_PRCR, val); + + /* Select the DCMIPP parallel interface */ + reg_write(inp, DCMIPP_CMCR, 0); + + /* Enable parallel interface */ + reg_set(inp, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE); + + return 0; +} + +static int dcmipp_inp_configure_csi(struct dcmipp_inp_device *inp, + struct v4l2_subdev_state *state) +{ + const struct dcmipp_inp_pix_map *vpix; + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_mbus_framefmt *src_fmt; + + /* Get format information */ + sink_fmt = v4l2_subdev_state_get_format(state, 0); + src_fmt = v4l2_subdev_state_get_format(state, 1); + + vpix = dcmipp_inp_pix_map_by_code(sink_fmt->code, src_fmt->code); + if (!vpix) { + dev_err(inp->dev, "Invalid sink/src format configuration\n"); + return -EINVAL; + } + + /* Apply configuration on each input pipe */ + reg_clear(inp, DCMIPP_P0FSCR, + DCMIPP_P0FSCR_DTMODE_MASK | DCMIPP_P0FSCR_DTIDA_MASK); + + /* In case of JPEG we don't know the DT so we allow all data */ + /* + * TODO - check instead dt == 0 for the time being to allow other + * unknown data-type + */ + if (!vpix->dt) + reg_set(inp, DCMIPP_P0FSCR, + DCMIPP_P0FSCR_DTMODE_ALLDT << DCMIPP_P0FSCR_DTMODE_SHIFT); + else + reg_set(inp, DCMIPP_P0FSCR, + vpix->dt << DCMIPP_P0FSCR_DTIDA_SHIFT | + DCMIPP_P0FSCR_DTMODE_DTIDA); + + /* Select the DCMIPP CSI interface */ + reg_write(inp, DCMIPP_CMCR, DCMIPP_CMCR_INSEL); + + return 0; +} + +static int dcmipp_inp_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct dcmipp_inp_device *inp = + container_of(sd, struct dcmipp_inp_device, sd); + struct v4l2_subdev *s_subdev; + struct media_pad *s_pad; + int ret = 0; + + /* Get source subdev */ + s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]); + if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity)) + return -EINVAL; + s_subdev = media_entity_to_v4l2_subdev(s_pad->entity); + + if (inp->ved.bus_type == V4L2_MBUS_PARALLEL || + inp->ved.bus_type == V4L2_MBUS_BT656) + ret = dcmipp_inp_configure_parallel(inp, state); + else if (inp->ved.bus_type == V4L2_MBUS_CSI2_DPHY) + ret = dcmipp_inp_configure_csi(inp, state); + if (ret) + return ret; + + ret = v4l2_subdev_enable_streams(s_subdev, s_pad->index, BIT_ULL(0)); + if (ret < 0) { + dev_err(inp->dev, + "failed to start source subdev streaming (%d)\n", ret); + return ret; + } + + return 0; +} + +static int dcmipp_inp_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct dcmipp_inp_device *inp = + container_of(sd, struct dcmipp_inp_device, sd); + struct v4l2_subdev *s_subdev; + struct media_pad *s_pad; + int ret; + + /* Get source subdev */ + s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]); + if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity)) + return -EINVAL; + s_subdev = media_entity_to_v4l2_subdev(s_pad->entity); + + ret = v4l2_subdev_disable_streams(s_subdev, s_pad->index, BIT_ULL(0)); + if (ret < 0) { + dev_err(inp->dev, + "failed to stop source subdev streaming (%d)\n", ret); + return ret; + } + + if (inp->ved.bus_type == V4L2_MBUS_PARALLEL || + inp->ved.bus_type == V4L2_MBUS_BT656) { + /* Disable parallel interface */ + reg_clear(inp, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE); + } + + return 0; +} + +static const struct v4l2_subdev_pad_ops dcmipp_inp_pad_ops = { + .enum_mbus_code = dcmipp_inp_enum_mbus_code, + .enum_frame_size = dcmipp_inp_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = dcmipp_inp_set_fmt, + .enable_streams = dcmipp_inp_enable_streams, + .disable_streams = dcmipp_inp_disable_streams, +}; + +static const struct v4l2_subdev_video_ops dcmipp_inp_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_ops dcmipp_inp_ops = { + .pad = &dcmipp_inp_pad_ops, + .video = &dcmipp_inp_video_ops, +}; + +static void dcmipp_inp_release(struct v4l2_subdev *sd) +{ + struct dcmipp_inp_device *inp = + container_of(sd, struct dcmipp_inp_device, sd); + + kfree(inp); +} + +static const struct v4l2_subdev_internal_ops dcmipp_inp_int_ops = { + .init_state = dcmipp_inp_init_state, + .release = dcmipp_inp_release, +}; + +void dcmipp_inp_ent_release(struct dcmipp_ent_device *ved) +{ + struct dcmipp_inp_device *inp = + container_of(ved, struct dcmipp_inp_device, ved); + + dcmipp_ent_sd_unregister(ved, &inp->sd); +} + +struct dcmipp_ent_device *dcmipp_inp_ent_init(struct device *dev, + const char *entity_name, + struct v4l2_device *v4l2_dev, + void __iomem *regs) +{ + struct dcmipp_inp_device *inp; + const unsigned long pads_flag[] = { + MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE, + }; + int ret; + + /* Allocate the inp struct */ + inp = kzalloc(sizeof(*inp), GFP_KERNEL); + if (!inp) + return ERR_PTR(-ENOMEM); + + inp->regs = regs; + + /* Initialize ved and sd */ + ret = dcmipp_ent_sd_register(&inp->ved, &inp->sd, v4l2_dev, + entity_name, MEDIA_ENT_F_VID_IF_BRIDGE, + ARRAY_SIZE(pads_flag), pads_flag, + &dcmipp_inp_int_ops, &dcmipp_inp_ops, + NULL, NULL); + if (ret) { + kfree(inp); + return ERR_PTR(ret); + } + + inp->dev = dev; + + return &inp->ved; +} diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c deleted file mode 100644 index 62c5c3331cfe..000000000000 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c +++ /dev/null @@ -1,440 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Driver for STM32 Digital Camera Memory Interface Pixel Processor - * - * Copyright (C) STMicroelectronics SA 2023 - * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com> - * Alain Volmat <alain.volmat@foss.st.com> - * for STMicroelectronics. - */ - -#include <linux/v4l2-mediabus.h> -#include <media/v4l2-event.h> -#include <media/v4l2-subdev.h> - -#include "dcmipp-common.h" - -#define DCMIPP_PRCR 0x104 -#define DCMIPP_PRCR_FORMAT_SHIFT 16 -#define DCMIPP_PRCR_FORMAT_YUV422 0x1e -#define DCMIPP_PRCR_FORMAT_RGB565 0x22 -#define DCMIPP_PRCR_FORMAT_RAW8 0x2a -#define DCMIPP_PRCR_FORMAT_G8 0x4a -#define DCMIPP_PRCR_FORMAT_BYTE_STREAM 0x5a -#define DCMIPP_PRCR_ESS BIT(4) -#define DCMIPP_PRCR_PCKPOL BIT(5) -#define DCMIPP_PRCR_HSPOL BIT(6) -#define DCMIPP_PRCR_VSPOL BIT(7) -#define DCMIPP_PRCR_ENABLE BIT(14) -#define DCMIPP_PRCR_SWAPCYCLES BIT(25) - -#define DCMIPP_PRESCR 0x108 -#define DCMIPP_PRESUR 0x10c - -#define IS_SINK(pad) (!(pad)) -#define IS_SRC(pad) ((pad)) - -struct dcmipp_par_pix_map { - unsigned int code_sink; - unsigned int code_src; - u8 prcr_format; - u8 prcr_swapcycles; -}; - -#define PIXMAP_SINK_SRC_PRCR_SWAP(sink, src, prcr, swap) \ - { \ - .code_sink = MEDIA_BUS_FMT_##sink, \ - .code_src = MEDIA_BUS_FMT_##src, \ - .prcr_format = DCMIPP_PRCR_FORMAT_##prcr, \ - .prcr_swapcycles = swap, \ - } -static const struct dcmipp_par_pix_map dcmipp_par_pix_map_list[] = { - /* RGB565 */ - PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_LE, RGB565_2X8_LE, RGB565, 1), - PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_BE, RGB565_2X8_LE, RGB565, 0), - /* YUV422 */ - PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, YUYV8_2X8, YUV422, 1), - PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, UYVY8_2X8, YUV422, 0), - PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, UYVY8_2X8, YUV422, 1), - PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, YUYV8_2X8, YUV422, 0), - PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_2X8, YVYU8_2X8, YUV422, 1), - PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_2X8, VYUY8_2X8, YUV422, 1), - /* GREY */ - PIXMAP_SINK_SRC_PRCR_SWAP(Y8_1X8, Y8_1X8, G8, 0), - /* Raw Bayer */ - PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR8_1X8, SBGGR8_1X8, RAW8, 0), - PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG8_1X8, SGBRG8_1X8, RAW8, 0), - PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG8_1X8, SGRBG8_1X8, RAW8, 0), - PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB8_1X8, SRGGB8_1X8, RAW8, 0), - /* JPEG */ - PIXMAP_SINK_SRC_PRCR_SWAP(JPEG_1X8, JPEG_1X8, BYTE_STREAM, 0), -}; - -/* - * Search through the pix_map table, skipping two consecutive entry with the - * same code - */ -static inline const struct dcmipp_par_pix_map *dcmipp_par_pix_map_by_index - (unsigned int index, - unsigned int pad) -{ - unsigned int i = 0; - u32 prev_code = 0, cur_code; - - while (i < ARRAY_SIZE(dcmipp_par_pix_map_list)) { - if (IS_SRC(pad)) - cur_code = dcmipp_par_pix_map_list[i].code_src; - else - cur_code = dcmipp_par_pix_map_list[i].code_sink; - - if (cur_code == prev_code) { - i++; - continue; - } - prev_code = cur_code; - - if (index == 0) - break; - i++; - index--; - } - - if (i >= ARRAY_SIZE(dcmipp_par_pix_map_list)) - return NULL; - - return &dcmipp_par_pix_map_list[i]; -} - -static inline const struct dcmipp_par_pix_map *dcmipp_par_pix_map_by_code - (u32 code_sink, u32 code_src) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(dcmipp_par_pix_map_list); i++) { - if ((dcmipp_par_pix_map_list[i].code_sink == code_sink && - dcmipp_par_pix_map_list[i].code_src == code_src) || - (dcmipp_par_pix_map_list[i].code_sink == code_src && - dcmipp_par_pix_map_list[i].code_src == code_sink) || - (dcmipp_par_pix_map_list[i].code_sink == code_sink && - code_src == 0) || - (code_sink == 0 && - dcmipp_par_pix_map_list[i].code_src == code_src)) - return &dcmipp_par_pix_map_list[i]; - } - return NULL; -} - -struct dcmipp_par_device { - struct dcmipp_ent_device ved; - struct v4l2_subdev sd; - struct device *dev; - void __iomem *regs; - bool streaming; -}; - -static const struct v4l2_mbus_framefmt fmt_default = { - .width = DCMIPP_FMT_WIDTH_DEFAULT, - .height = DCMIPP_FMT_HEIGHT_DEFAULT, - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .field = V4L2_FIELD_NONE, - .colorspace = DCMIPP_COLORSPACE_DEFAULT, - .ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT, - .quantization = DCMIPP_QUANTIZATION_DEFAULT, - .xfer_func = DCMIPP_XFER_FUNC_DEFAULT, -}; - -static int dcmipp_par_init_state(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) -{ - unsigned int i; - - for (i = 0; i < sd->entity.num_pads; i++) { - struct v4l2_mbus_framefmt *mf; - - mf = v4l2_subdev_state_get_format(sd_state, i); - *mf = fmt_default; - } - - return 0; -} - -static int dcmipp_par_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - const struct dcmipp_par_pix_map *vpix = - dcmipp_par_pix_map_by_index(code->index, code->pad); - - if (!vpix) - return -EINVAL; - - code->code = IS_SRC(code->pad) ? vpix->code_src : vpix->code_sink; - - return 0; -} - -static int dcmipp_par_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) -{ - const struct dcmipp_par_pix_map *vpix; - - if (fse->index) - return -EINVAL; - - /* Only accept code in the pix map table */ - vpix = dcmipp_par_pix_map_by_code(IS_SINK(fse->pad) ? fse->code : 0, - IS_SRC(fse->pad) ? fse->code : 0); - if (!vpix) - return -EINVAL; - - fse->min_width = DCMIPP_FRAME_MIN_WIDTH; - fse->max_width = DCMIPP_FRAME_MAX_WIDTH; - fse->min_height = DCMIPP_FRAME_MIN_HEIGHT; - fse->max_height = DCMIPP_FRAME_MAX_HEIGHT; - - return 0; -} - -static void dcmipp_par_adjust_fmt(struct dcmipp_par_device *par, - struct v4l2_mbus_framefmt *fmt, __u32 pad) -{ - const struct dcmipp_par_pix_map *vpix; - - /* Only accept code in the pix map table */ - vpix = dcmipp_par_pix_map_by_code(IS_SINK(pad) ? fmt->code : 0, - IS_SRC(pad) ? fmt->code : 0); - if (!vpix) - fmt->code = fmt_default.code; - - /* Exclude JPEG if BT656 bus is selected */ - if (vpix && vpix->code_sink == MEDIA_BUS_FMT_JPEG_1X8 && - par->ved.bus_type == V4L2_MBUS_BT656) - fmt->code = fmt_default.code; - - fmt->width = clamp_t(u32, fmt->width, DCMIPP_FRAME_MIN_WIDTH, - DCMIPP_FRAME_MAX_WIDTH) & ~1; - fmt->height = clamp_t(u32, fmt->height, DCMIPP_FRAME_MIN_HEIGHT, - DCMIPP_FRAME_MAX_HEIGHT) & ~1; - - if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE) - fmt->field = fmt_default.field; - - dcmipp_colorimetry_clamp(fmt); -} - -static int dcmipp_par_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct dcmipp_par_device *par = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *mf; - - if (par->streaming) - return -EBUSY; - - mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); - - /* Set the new format */ - dcmipp_par_adjust_fmt(par, &fmt->format, fmt->pad); - - dev_dbg(par->dev, "%s: format update: old:%dx%d (0x%x, %d, %d, %d, %d) new:%dx%d (0x%x, %d, %d, %d, %d)\n", - par->sd.name, - /* old */ - mf->width, mf->height, mf->code, - mf->colorspace, mf->quantization, - mf->xfer_func, mf->ycbcr_enc, - /* new */ - fmt->format.width, fmt->format.height, fmt->format.code, - fmt->format.colorspace, fmt->format.quantization, - fmt->format.xfer_func, fmt->format.ycbcr_enc); - - *mf = fmt->format; - - /* When setting the sink format, report that format on the src pad */ - if (IS_SINK(fmt->pad)) { - mf = v4l2_subdev_state_get_format(sd_state, 1); - *mf = fmt->format; - dcmipp_par_adjust_fmt(par, mf, 1); - } - - return 0; -} - -static const struct v4l2_subdev_pad_ops dcmipp_par_pad_ops = { - .enum_mbus_code = dcmipp_par_enum_mbus_code, - .enum_frame_size = dcmipp_par_enum_frame_size, - .get_fmt = v4l2_subdev_get_fmt, - .set_fmt = dcmipp_par_set_fmt, -}; - -static int dcmipp_par_configure(struct dcmipp_par_device *par) -{ - u32 val = 0; - const struct dcmipp_par_pix_map *vpix; - struct v4l2_subdev_state *state; - struct v4l2_mbus_framefmt *sink_fmt; - struct v4l2_mbus_framefmt *src_fmt; - - /* Set vertical synchronization polarity */ - if (par->ved.bus.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) - val |= DCMIPP_PRCR_VSPOL; - - /* Set horizontal synchronization polarity */ - if (par->ved.bus.flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) - val |= DCMIPP_PRCR_HSPOL; - - /* Set pixel clock polarity */ - if (par->ved.bus.flags & V4L2_MBUS_PCLK_SAMPLE_RISING) - val |= DCMIPP_PRCR_PCKPOL; - - /* - * BT656 embedded synchronisation bus mode. - * - * Default SAV/EAV mode is supported here with default codes - * SAV=0xff000080 & EAV=0xff00009d. - * With DCMIPP this means LSC=SAV=0x80 & LEC=EAV=0x9d. - */ - if (par->ved.bus_type == V4L2_MBUS_BT656) { - val |= DCMIPP_PRCR_ESS; - - /* Unmask all codes */ - reg_write(par, DCMIPP_PRESUR, 0xffffffff);/* FEC:LEC:LSC:FSC */ - - /* Trig on LSC=0x80 & LEC=0x9d codes, ignore FSC and FEC */ - reg_write(par, DCMIPP_PRESCR, 0xff9d80ff);/* FEC:LEC:LSC:FSC */ - } - - /* Set format */ - state = v4l2_subdev_lock_and_get_active_state(&par->sd); - sink_fmt = v4l2_subdev_state_get_format(state, 0); - src_fmt = v4l2_subdev_state_get_format(state, 1); - v4l2_subdev_unlock_state(state); - - vpix = dcmipp_par_pix_map_by_code(sink_fmt->code, src_fmt->code); - if (!vpix) { - dev_err(par->dev, "Invalid sink/src format configuration\n"); - return -EINVAL; - } - - val |= vpix->prcr_format << DCMIPP_PRCR_FORMAT_SHIFT; - - /* swap cycles */ - if (vpix->prcr_swapcycles) - val |= DCMIPP_PRCR_SWAPCYCLES; - - reg_write(par, DCMIPP_PRCR, val); - - return 0; -} - -static int dcmipp_par_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct dcmipp_par_device *par = - container_of(sd, struct dcmipp_par_device, sd); - struct v4l2_subdev *s_subdev; - struct media_pad *pad; - int ret = 0; - - /* Get source subdev */ - pad = media_pad_remote_pad_first(&sd->entity.pads[0]); - if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) - return -EINVAL; - s_subdev = media_entity_to_v4l2_subdev(pad->entity); - - if (enable) { - ret = dcmipp_par_configure(par); - if (ret) - return ret; - - /* Enable parallel interface */ - reg_set(par, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE); - - ret = v4l2_subdev_call(s_subdev, video, s_stream, enable); - if (ret < 0) { - dev_err(par->dev, - "failed to start source subdev streaming (%d)\n", - ret); - return ret; - } - } else { - ret = v4l2_subdev_call(s_subdev, video, s_stream, enable); - if (ret < 0) { - dev_err(par->dev, - "failed to stop source subdev streaming (%d)\n", - ret); - return ret; - } - - /* Disable parallel interface */ - reg_clear(par, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE); - } - - par->streaming = enable; - - return ret; -} - -static const struct v4l2_subdev_video_ops dcmipp_par_video_ops = { - .s_stream = dcmipp_par_s_stream, -}; - -static const struct v4l2_subdev_ops dcmipp_par_ops = { - .pad = &dcmipp_par_pad_ops, - .video = &dcmipp_par_video_ops, -}; - -static void dcmipp_par_release(struct v4l2_subdev *sd) -{ - struct dcmipp_par_device *par = - container_of(sd, struct dcmipp_par_device, sd); - - kfree(par); -} - -static const struct v4l2_subdev_internal_ops dcmipp_par_int_ops = { - .init_state = dcmipp_par_init_state, - .release = dcmipp_par_release, -}; - -void dcmipp_par_ent_release(struct dcmipp_ent_device *ved) -{ - struct dcmipp_par_device *par = - container_of(ved, struct dcmipp_par_device, ved); - - dcmipp_ent_sd_unregister(ved, &par->sd); -} - -struct dcmipp_ent_device *dcmipp_par_ent_init(struct device *dev, - const char *entity_name, - struct v4l2_device *v4l2_dev, - void __iomem *regs) -{ - struct dcmipp_par_device *par; - const unsigned long pads_flag[] = { - MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE, - }; - int ret; - - /* Allocate the par struct */ - par = kzalloc(sizeof(*par), GFP_KERNEL); - if (!par) - return ERR_PTR(-ENOMEM); - - par->regs = regs; - - /* Initialize ved and sd */ - ret = dcmipp_ent_sd_register(&par->ved, &par->sd, v4l2_dev, - entity_name, MEDIA_ENT_F_VID_IF_BRIDGE, - ARRAY_SIZE(pads_flag), pads_flag, - &dcmipp_par_int_ops, &dcmipp_par_ops, - NULL, NULL); - if (ret) { - kfree(par); - return ERR_PTR(ret); - } - - par->dev = dev; - - return &par->ved; -} |
