From dd40472dc6d7078746680e71ed2f4cc9c4e15b0a Mon Sep 17 00:00:00 2001 From: Jianfeng Liu Date: Fri, 22 Mar 2024 13:29:14 +0800 Subject: dt-bindings: media: rockchip-rga: add rockchip,rk3588-rga Add a new compatible for the rk3588 Rockchip SoC, which also features an RGA, which is called RGA2 in the TRM Part2. It is the same core as used on the rk3288 and rk3568, which documents the same RGA2. Signed-off-by: Jianfeng Liu Acked-by: Conor Dooley Signed-off-by: Hans Verkuil --- Documentation/devicetree/bindings/media/rockchip-rga.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/media/rockchip-rga.yaml b/Documentation/devicetree/bindings/media/rockchip-rga.yaml index ea2342222408..ac17cda65191 100644 --- a/Documentation/devicetree/bindings/media/rockchip-rga.yaml +++ b/Documentation/devicetree/bindings/media/rockchip-rga.yaml @@ -24,6 +24,7 @@ properties: - enum: - rockchip,rk3228-rga - rockchip,rk3568-rga + - rockchip,rk3588-rga - const: rockchip,rk3288-rga reg: -- cgit v1.2.3 From 629913d6d79508b166c66e07e4857e20233d85a9 Mon Sep 17 00:00:00 2001 From: Mikhail Kobuk Date: Thu, 28 Mar 2024 02:32:23 +0300 Subject: media: pci: ivtv: Add check for DMA map result In case DMA fails, 'dma->SG_length' is 0. This value is later used to access 'dma->SGarray[dma->SG_length - 1]', which will cause out of bounds access. Add check to return early on invalid value. Adjust warnings accordingly. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 1932dc2f4cf6 ("media: pci/ivtv: switch from 'pci_' to 'dma_' API") Signed-off-by: Mikhail Kobuk Signed-off-by: Hans Verkuil --- drivers/media/pci/ivtv/ivtv-udma.c | 8 ++++++++ drivers/media/pci/ivtv/ivtv-yuv.c | 6 ++++++ drivers/media/pci/ivtv/ivtvfb.c | 6 +++--- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/media/pci/ivtv/ivtv-udma.c b/drivers/media/pci/ivtv/ivtv-udma.c index 99b9f55ca829..f467a00492f4 100644 --- a/drivers/media/pci/ivtv/ivtv-udma.c +++ b/drivers/media/pci/ivtv/ivtv-udma.c @@ -131,6 +131,8 @@ int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr, /* Fill SG List with new values */ if (ivtv_udma_fill_sg_list(dma, &user_dma, 0) < 0) { + IVTV_DEBUG_WARN("%s: could not allocate bounce buffers for highmem userspace buffers\n", + __func__); unpin_user_pages(dma->map, dma->page_count); dma->page_count = 0; return -ENOMEM; @@ -139,6 +141,12 @@ int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr, /* Map SG List */ dma->SG_length = dma_map_sg(&itv->pdev->dev, dma->SGlist, dma->page_count, DMA_TO_DEVICE); + if (!dma->SG_length) { + IVTV_DEBUG_WARN("%s: DMA map error, SG_length is 0\n", __func__); + unpin_user_pages(dma->map, dma->page_count); + dma->page_count = 0; + return -EINVAL; + } /* Fill SG Array with new values */ ivtv_udma_fill_sg_array (dma, ivtv_dest_addr, 0, -1); diff --git a/drivers/media/pci/ivtv/ivtv-yuv.c b/drivers/media/pci/ivtv/ivtv-yuv.c index 582146f8d70d..2d9274537725 100644 --- a/drivers/media/pci/ivtv/ivtv-yuv.c +++ b/drivers/media/pci/ivtv/ivtv-yuv.c @@ -114,6 +114,12 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, } dma->SG_length = dma_map_sg(&itv->pdev->dev, dma->SGlist, dma->page_count, DMA_TO_DEVICE); + if (!dma->SG_length) { + IVTV_DEBUG_WARN("%s: DMA map error, SG_length is 0\n", __func__); + unpin_user_pages(dma->map, dma->page_count); + dma->page_count = 0; + return -EINVAL; + } /* Fill SG Array with new values */ ivtv_udma_fill_sg_array(dma, y_buffer_offset, uv_buffer_offset, y_size); diff --git a/drivers/media/pci/ivtv/ivtvfb.c b/drivers/media/pci/ivtv/ivtvfb.c index 410477e3e621..d1ab7fee0d05 100644 --- a/drivers/media/pci/ivtv/ivtvfb.c +++ b/drivers/media/pci/ivtv/ivtvfb.c @@ -281,10 +281,10 @@ static int ivtvfb_prep_dec_dma_to_device(struct ivtv *itv, /* Map User DMA */ if (ivtv_udma_setup(itv, ivtv_dest_addr, userbuf, size_in_bytes) <= 0) { mutex_unlock(&itv->udma.lock); - IVTVFB_WARN("ivtvfb_prep_dec_dma_to_device, Error with pin_user_pages: %d bytes, %d pages returned\n", - size_in_bytes, itv->udma.page_count); + IVTVFB_WARN("%s, Error in ivtv_udma_setup: %d bytes, %d pages returned\n", + __func__, size_in_bytes, itv->udma.page_count); - /* pin_user_pages must have failed completely */ + /* pin_user_pages or DMA must have failed completely */ return -EIO; } -- cgit v1.2.3 From f56d1edbb7c4d9d20e1fea25adbaa76bb8b28021 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 6 May 2024 21:10:26 +0000 Subject: media: ivtv: Factor out schedule functions Cocci is very confused by unlock-lock a mutex in the middle of a function. Factor the schedules out, avoid code duplication and make cocci a bit happier. Fix the following cocci warnings: drivers/media/pci/ivtv/ivtv-fileops.c:223:4-10: preceding lock on line 267 drivers/media/pci/ivtv/ivtv-fileops.c:230:3-9: preceding lock on line 267 drivers/media/pci/ivtv/ivtv-fileops.c:236:4-10: preceding lock on line 267 drivers/media/pci/ivtv/ivtv-fileops.c:245:3-9: preceding lock on line 267 drivers/media/pci/ivtv/ivtv-fileops.c:251:3-9: preceding lock on line 267 drivers/media/pci/ivtv/ivtv-fileops.c:257:3-9: preceding lock on line 267 drivers/media/pci/ivtv/ivtv-fileops.c:272:3-9: preceding lock on line 267 drivers/media/pci/ivtv/ivtv-fileops.c:598:4-10: preceding lock on line 627 drivers/media/pci/ivtv/ivtv-fileops.c:598:4-10: preceding lock on line 689 drivers/media/pci/ivtv/ivtv-fileops.c:606:3-9: preceding lock on line 627 drivers/media/pci/ivtv/ivtv-fileops.c:606:3-9: preceding lock on line 689 drivers/media/pci/ivtv/ivtv-fileops.c:648:3-9: preceding lock on line 627 drivers/media/pci/ivtv/ivtv-fileops.c:648:3-9: preceding lock on line 689 drivers/media/pci/ivtv/ivtv-fileops.c:692:4-10: preceding lock on line 689 Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/pci/ivtv/ivtv-fileops.c | 66 +++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/drivers/media/pci/ivtv/ivtv-fileops.c b/drivers/media/pci/ivtv/ivtv-fileops.c index 4202c3a47d33..cfa28d035586 100644 --- a/drivers/media/pci/ivtv/ivtv-fileops.c +++ b/drivers/media/pci/ivtv/ivtv-fileops.c @@ -21,6 +21,7 @@ #include "ivtv-ioctl.h" #include "ivtv-cards.h" #include "ivtv-firmware.h" +#include #include #include @@ -190,12 +191,27 @@ static void ivtv_update_pgm_info(struct ivtv *itv) itv->pgm_info_write_idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num; } +static void ivtv_schedule(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + DEFINE_WAIT(wait); + + lockdep_assert_held(&itv->serialize_lock); + + mutex_unlock(&itv->serialize_lock); + prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); + /* New buffers might have become free before we were added to the waitqueue */ + if (!s->q_free.buffers) + schedule(); + finish_wait(&s->waitq, &wait); + mutex_lock(&itv->serialize_lock); +} + static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block, int *err) { struct ivtv *itv = s->itv; struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; struct ivtv_buffer *buf; - DEFINE_WAIT(wait); *err = 0; while (1) { @@ -258,13 +274,7 @@ static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block, } /* wait for more data to arrive */ - mutex_unlock(&itv->serialize_lock); - prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); - /* New buffers might have become available before we were added to the waitqueue */ - if (!s->q_full.buffers) - schedule(); - finish_wait(&s->waitq, &wait); - mutex_lock(&itv->serialize_lock); + ivtv_schedule(s); if (signal_pending(current)) { /* return if a signal was received */ IVTV_DEBUG_INFO("User stopped %s\n", s->name); @@ -533,6 +543,25 @@ int ivtv_start_decoding(struct ivtv_open_id *id, int speed) return 0; } +static int ivtv_schedule_dma(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + int got_sig; + DEFINE_WAIT(wait); + + lockdep_assert_held(&itv->serialize_lock); + + mutex_unlock(&itv->serialize_lock); + prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); + while (!(got_sig = signal_pending(current)) && + test_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) + schedule(); + finish_wait(&itv->dma_waitq, &wait); + mutex_lock(&itv->serialize_lock); + + return got_sig; +} + static ssize_t ivtv_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *pos) { struct ivtv_open_id *id = fh2id(filp->private_data); @@ -544,7 +573,6 @@ static ssize_t ivtv_write(struct file *filp, const char __user *user_buf, size_t int bytes_written = 0; int mode; int rc; - DEFINE_WAIT(wait); IVTV_DEBUG_HI_FILE("write %zd bytes to %s\n", count, s->name); @@ -618,13 +646,7 @@ retry: break; if (filp->f_flags & O_NONBLOCK) return -EAGAIN; - mutex_unlock(&itv->serialize_lock); - prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); - /* New buffers might have become free before we were added to the waitqueue */ - if (!s->q_free.buffers) - schedule(); - finish_wait(&s->waitq, &wait); - mutex_lock(&itv->serialize_lock); + ivtv_schedule(s); if (signal_pending(current)) { IVTV_DEBUG_INFO("User stopped %s\n", s->name); return -EINTR; @@ -674,20 +696,10 @@ retry: if (test_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags)) { if (s->q_full.length >= itv->dma_data_req_size) { - int got_sig; - if (mode == OUT_YUV) ivtv_yuv_setup_stream_frame(itv); - mutex_unlock(&itv->serialize_lock); - prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); - while (!(got_sig = signal_pending(current)) && - test_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) { - schedule(); - } - finish_wait(&itv->dma_waitq, &wait); - mutex_lock(&itv->serialize_lock); - if (got_sig) { + if (ivtv_schedule_dma(s)) { IVTV_DEBUG_INFO("User interrupted %s\n", s->name); return -EINTR; } -- cgit v1.2.3 From 0b9a0cd686f6e17e3b33953b9cd0746978fcaa7c Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 6 May 2024 21:10:28 +0000 Subject: media: dvb-frontends/stv090x: Refactor tuner_i2c_lock Move the lock logic to it's own function. There is less code duplication and cocci is much happier. Fix the following cocci warning: drivers/media/dvb-frontends/stv090x.c:799:1-7: preceding lock on line 768 Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/dvb-frontends/stv090x.c | 37 ++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c index cc45139057ba..3b02d504941f 100644 --- a/drivers/media/dvb-frontends/stv090x.c +++ b/drivers/media/dvb-frontends/stv090x.c @@ -748,6 +748,22 @@ static int stv090x_write_reg(struct stv090x_state *state, unsigned int reg, u8 d return stv090x_write_regs(state, reg, &tmp, 1); } +static inline void stv090x_tuner_i2c_lock(struct stv090x_state *state) +{ + if (state->config->tuner_i2c_lock) + state->config->tuner_i2c_lock(&state->frontend, 1); + else + mutex_lock(&state->internal->tuner_lock); +} + +static inline void stv090x_tuner_i2c_unlock(struct stv090x_state *state) +{ + if (state->config->tuner_i2c_lock) + state->config->tuner_i2c_lock(&state->frontend, 0); + else + mutex_unlock(&state->internal->tuner_lock); +} + static int stv090x_i2c_gate_ctrl(struct stv090x_state *state, int enable) { u32 reg; @@ -761,12 +777,8 @@ static int stv090x_i2c_gate_ctrl(struct stv090x_state *state, int enable) * In case of any error, the lock is unlocked and exit within the * relevant operations themselves. */ - if (enable) { - if (state->config->tuner_i2c_lock) - state->config->tuner_i2c_lock(&state->frontend, 1); - else - mutex_lock(&state->internal->tuner_lock); - } + if (enable) + stv090x_tuner_i2c_lock(state); reg = STV090x_READ_DEMOD(state, I2CRPT); if (enable) { @@ -782,20 +794,13 @@ static int stv090x_i2c_gate_ctrl(struct stv090x_state *state, int enable) goto err; } - if (!enable) { - if (state->config->tuner_i2c_lock) - state->config->tuner_i2c_lock(&state->frontend, 0); - else - mutex_unlock(&state->internal->tuner_lock); - } + if (!enable) + stv090x_tuner_i2c_unlock(state); return 0; err: dprintk(FE_ERROR, 1, "I/O error"); - if (state->config->tuner_i2c_lock) - state->config->tuner_i2c_lock(&state->frontend, 0); - else - mutex_unlock(&state->internal->tuner_lock); + stv090x_tuner_i2c_unlock(state); return -1; } -- cgit v1.2.3 From 68879806a567a50ba5fd25b16d2f923e88715e7f Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 6 May 2024 21:10:29 +0000 Subject: media: go7007: Refactor Adlink PCI-MPG24 i2c mutex Move the lock/unlock to its own function. It makes the code cleaner and cocci happier. Fix the following cocci warning: drivers/media/usb/go7007/go7007-i2c.c:125:1-7: preceding lock on line 61 Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/usb/go7007/go7007-i2c.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/drivers/media/usb/go7007/go7007-i2c.c b/drivers/media/usb/go7007/go7007-i2c.c index 2880370e45c8..f6ce28a4a768 100644 --- a/drivers/media/usb/go7007/go7007-i2c.c +++ b/drivers/media/usb/go7007/go7007-i2c.c @@ -33,7 +33,21 @@ /* There is only one I2C port on the TW2804 that feeds all four GO7007 VIPs * on the Adlink PCI-MPG24, so access is shared between all of them. */ -static DEFINE_MUTEX(adlink_mpg24_i2c_lock); +static DEFINE_MUTEX(adlink_mpg24_i2c_mutex); + +static inline void adlink_mpg24_i2c_lock(struct go7007 *go) +{ + /* Bridge the I2C port on this GO7007 to the shared bus */ + mutex_lock(&adlink_mpg24_i2c_mutex); + go7007_write_addr(go, 0x3c82, 0x0020); +} + +static inline void adlink_mpg24_i2c_unlock(struct go7007 *go) +{ + /* Isolate the I2C port on this GO7007 from the shared bus */ + go7007_write_addr(go, 0x3c82, 0x0000); + mutex_unlock(&adlink_mpg24_i2c_mutex); +} static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, u16 command, int flags, u8 *data) @@ -56,11 +70,8 @@ static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, mutex_lock(&go->hw_lock); - if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) { - /* Bridge the I2C port on this GO7007 to the shared bus */ - mutex_lock(&adlink_mpg24_i2c_lock); - go7007_write_addr(go, 0x3c82, 0x0020); - } + if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) + adlink_mpg24_i2c_lock(go); /* Wait for I2C adapter to be ready */ for (i = 0; i < 10; ++i) { @@ -116,11 +127,8 @@ static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, ret = 0; i2c_done: - if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) { - /* Isolate the I2C port on this GO7007 from the shared bus */ - go7007_write_addr(go, 0x3c82, 0x0000); - mutex_unlock(&adlink_mpg24_i2c_lock); - } + if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) + adlink_mpg24_i2c_unlock(go); mutex_unlock(&go->hw_lock); return ret; } -- cgit v1.2.3 From 715b1d31ef7d4609e03a9cb6a875949e3c80fe63 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Fri, 24 May 2024 01:29:14 +0100 Subject: media: dvb-frontends: drx39xyj: remove unused struct 'drxjeq_stat' 'drxjeq_stat' is unused since the original commit 38b2df95c53b ("[media] drx-j: add a driver for Trident drx-j frontend"). The name was changed by commit 57afe2f0bb0c ("[media] drx-j: Don't use CamelCase") and it was originally DRXJEQStat_t. Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Hans Verkuil --- drivers/media/dvb-frontends/drx39xyj/drxj.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index 6fcaf07e1b82..779cce93e94a 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -976,13 +976,6 @@ static struct drx_aud_data drxj_default_aud_data_g = { /*----------------------------------------------------------------------------- STRUCTURES ----------------------------------------------------------------------------*/ -struct drxjeq_stat { - u16 eq_mse; - u8 eq_mode; - u8 eq_ctrl; - u8 eq_stat; -}; - /* HI command */ struct drxj_hi_cmd { u16 cmd; -- cgit v1.2.3 From 46be626a214b3a3ac06bf515e1fb46193e11fd4d Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Fri, 24 May 2024 01:29:15 +0100 Subject: media: dvb-frontends: stv0910: remove unused struct 'sinit_table' 'sinit_table' has been unused since the original commit cd21b3349437 ("media: dvb-frontends: add ST STV0910 DVB-S/S2 demodulator frontend driver"). Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Hans Verkuil --- drivers/media/dvb-frontends/stv0910.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/media/dvb-frontends/stv0910.c b/drivers/media/dvb-frontends/stv0910.c index e517ff757744..069dec75129c 100644 --- a/drivers/media/dvb-frontends/stv0910.c +++ b/drivers/media/dvb-frontends/stv0910.c @@ -119,11 +119,6 @@ struct stv { u8 vth[6]; }; -struct sinit_table { - u16 address; - u8 data; -}; - struct slookup { s16 value; u32 reg_value; -- cgit v1.2.3 From c9230ee9251ecece02c4813d3c8b34e1501dc1bb Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Fri, 24 May 2024 01:29:16 +0100 Subject: media: dvb-frontends: dib7000p: remove unused struct 'i2c_device' 'i2c_device' was added by the original commit 713d54a8bd81 ("[media] DiB7090: add support for the dib7090 based") but is unused. Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Hans Verkuil --- drivers/media/dvb-frontends/dib7000p.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c index 444fe1c4bf2d..c5582d4fa5be 100644 --- a/drivers/media/dvb-frontends/dib7000p.c +++ b/drivers/media/dvb-frontends/dib7000p.c @@ -32,11 +32,6 @@ MODULE_PARM_DESC(buggy_sfn_workaround, "Enable work-around for buggy SFNs (defau __func__, ##arg); \ } while (0) -struct i2c_device { - struct i2c_adapter *i2c_adap; - u8 i2c_addr; -}; - struct dib7000p_state { struct dvb_frontend demod; struct dib7000p_config cfg; -- cgit v1.2.3 From 30376e2052bc87f6dfc13cddb85f8857a03a153a Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sat, 25 May 2024 18:52:27 +0100 Subject: media: gs1662: remove unused struct 'gs_reg_fmt_custom' 'gs_reg_fmt_custom' is unused since the original commit 7aae6e2df127 ("[media] Add GS1662 driver, a video serializer"). Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Hans Verkuil --- drivers/media/spi/gs1662.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/media/spi/gs1662.c b/drivers/media/spi/gs1662.c index dc5c4c055d29..7adf55fd0707 100644 --- a/drivers/media/spi/gs1662.c +++ b/drivers/media/spi/gs1662.c @@ -55,14 +55,6 @@ struct gs_reg_fmt { struct v4l2_dv_timings format; }; -struct gs_reg_fmt_custom { - u16 reg_value; - __u32 width; - __u32 height; - __u64 pixelclock; - __u32 interlaced; -}; - static const struct spi_device_id gs_id[] = { { "gs1662", 0 }, { } -- cgit v1.2.3 From f5ecfb982998b518d50bd861f46b73758710f138 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sat, 25 May 2024 18:52:28 +0100 Subject: media: opera1: remove unused struct 'rc_map_opera_table' 'rc_map_opera_table' has been unused since commit 2f4f58d689dd ("[media] rc: Name RC keymap tables as rc_map_table"). Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Hans Verkuil --- drivers/media/usb/dvb-usb/opera1.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/media/usb/dvb-usb/opera1.c b/drivers/media/usb/dvb-usb/opera1.c index d269f8bb2dee..268f05fc8691 100644 --- a/drivers/media/usb/dvb-usb/opera1.c +++ b/drivers/media/usb/dvb-usb/opera1.c @@ -32,10 +32,6 @@ struct opera1_state { u32 last_key_pressed; }; -struct rc_map_opera_table { - u32 keycode; - u32 event; -}; static int dvb_usb_opera1_debug; module_param_named(debug, dvb_usb_opera1_debug, int, 0644); -- cgit v1.2.3 From 6a4e6e34ceac24f4a17fb24fdca0e04c16ecfba5 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sat, 25 May 2024 18:52:29 +0100 Subject: media: pvrusb2: remove unused struct 'debugifc_mask_item' 'debugifc_mask_item' is unused since commit 681c73994401 ("V4L/DVB (6691): pvrusb2: Rework pipeline state control"). Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Hans Verkuil --- drivers/media/usb/pvrusb2/pvrusb2-debugifc.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c index 84cfb5ce8b8d..81d711269ab5 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c @@ -9,11 +9,6 @@ #include "pvrusb2-hdw.h" #include "pvrusb2-debug.h" -struct debugifc_mask_item { - const char *name; - unsigned long msk; -}; - static unsigned int debugifc_count_whitespace(const char *buf, unsigned int count) -- cgit v1.2.3 From 669d51f8f45db35a5e44101b90fcc7173088ad28 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 27 May 2024 21:08:51 +0000 Subject: media: allegro: nal-hevc: Replace array[1] with array[N] Structures that have a single element array as the last field can be mistaken as a "flex array". We could replace all the single element arrays in the structure with single element fields, but this driver prefers to follow the ITU-T H.265 specification, which defines it as an array. If we introduce a new define N_HRD_PARAMS, we make clear our intentions. This fixes this cocci warning: drivers/media/platform/allegro-dvt/nal-hevc.h:102:14-22: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) Signed-off-by: Ricardo Ribalda Reviewed-by: Michael Tretter Signed-off-by: Hans Verkuil [hverkuil: fix typo in subject and commit log] --- drivers/media/platform/allegro-dvt/nal-hevc.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/allegro-dvt/nal-hevc.h b/drivers/media/platform/allegro-dvt/nal-hevc.h index eb46f12aae80..361e2f55c254 100644 --- a/drivers/media/platform/allegro-dvt/nal-hevc.h +++ b/drivers/media/platform/allegro-dvt/nal-hevc.h @@ -96,10 +96,11 @@ struct nal_hevc_vps { unsigned int extension_data_flag; }; +#define N_HRD_PARAMS 1 struct nal_hevc_sub_layer_hrd_parameters { - unsigned int bit_rate_value_minus1[1]; - unsigned int cpb_size_value_minus1[1]; - unsigned int cbr_flag[1]; + unsigned int bit_rate_value_minus1[N_HRD_PARAMS]; + unsigned int cpb_size_value_minus1[N_HRD_PARAMS]; + unsigned int cbr_flag[N_HRD_PARAMS]; }; struct nal_hevc_hrd_parameters { -- cgit v1.2.3 From a93506f670de9fe57e40e18e32c84e73121bca55 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 27 May 2024 21:08:52 +0000 Subject: media: xilinx: Refactor struct xvip_dma Replace a single element array with a single field. The following cocci warning is fixed: drivers/media/platform/xilinx/xilinx-dma.h:100:19-22: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/xilinx/xilinx-dma.c | 4 ++-- drivers/media/platform/xilinx/xilinx-dma.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c index a96de5d388a1..a1687b868a44 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.c +++ b/drivers/media/platform/xilinx/xilinx-dma.c @@ -348,8 +348,8 @@ static void xvip_dma_buffer_queue(struct vb2_buffer *vb) } dma->xt.frame_size = 1; - dma->sgl[0].size = dma->format.width * dma->fmtinfo->bpp; - dma->sgl[0].icg = dma->format.bytesperline - dma->sgl[0].size; + dma->sgl.size = dma->format.width * dma->fmtinfo->bpp; + dma->sgl.icg = dma->format.bytesperline - dma->sgl.size; dma->xt.numf = dma->format.height; desc = dmaengine_prep_interleaved_dma(dma->dma, &dma->xt, flags); diff --git a/drivers/media/platform/xilinx/xilinx-dma.h b/drivers/media/platform/xilinx/xilinx-dma.h index 9c6d4c18d1a9..18f77e1a7b39 100644 --- a/drivers/media/platform/xilinx/xilinx-dma.h +++ b/drivers/media/platform/xilinx/xilinx-dma.h @@ -97,7 +97,7 @@ struct xvip_dma { struct dma_chan *dma; unsigned int align; struct dma_interleaved_template xt; - struct data_chunk sgl[1]; + struct data_chunk sgl; }; #define to_xvip_dma(vdev) container_of(vdev, struct xvip_dma, video) -- cgit v1.2.3 From 63916c3dec9b07f0a69a3c4a9722d7ec3ba9da23 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 27 May 2024 21:08:55 +0000 Subject: media: pci: cx18: Use flex arrays for struct cx18_scb Replace the old-style single element array with a flexible array. This structure does not seem to be allocated in the code, so there is no need to change anything else. The following cocci warning is fixed: drivers/media/pci/cx18/cx18-scb.h:261:22-29: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/pci/cx18/cx18-scb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/cx18/cx18-scb.h b/drivers/media/pci/cx18/cx18-scb.h index f7105421dd25..841edc0712ab 100644 --- a/drivers/media/pci/cx18/cx18-scb.h +++ b/drivers/media/pci/cx18/cx18-scb.h @@ -258,7 +258,7 @@ struct cx18_scb { struct cx18_mailbox ppu2epu_mb; struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][CX18_MAX_MDL_ACKS]; - struct cx18_mdl_ent cpu_mdl[1]; + struct cx18_mdl_ent cpu_mdl[]; }; void cx18_init_scb(struct cx18 *cx); -- cgit v1.2.3 From 6c69a73adf506080765d5dace6f4fcce8c6cda15 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 27 May 2024 21:08:56 +0000 Subject: media: siano: Refactor struct sms_msg_data Replace a single element array with a single element field. The endianness conversion code seems to support multiple elements. To avoid changing behavior a pointer to the single element has been used. This is safer than moving to a flex array, because in that case the structure size changes. This fixes the following cocci warning: drivers/media/common/siano/smscoreapi.h:619:5-13: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/common/siano/smscoreapi.c | 10 +++++----- drivers/media/common/siano/smscoreapi.h | 2 +- drivers/media/common/siano/smsdvb-main.c | 4 ++-- drivers/media/common/siano/smsendian.c | 8 +++++--- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/media/common/siano/smscoreapi.c b/drivers/media/common/siano/smscoreapi.c index 7ebcb10126c9..b6f1eb5dbbdf 100644 --- a/drivers/media/common/siano/smscoreapi.c +++ b/drivers/media/common/siano/smscoreapi.c @@ -839,7 +839,7 @@ static int smscore_configure_board(struct smscore_device_t *coredev) mtu_msg.x_msg_header.msg_flags = 0; mtu_msg.x_msg_header.msg_type = MSG_SMS_SET_MAX_TX_MSG_LEN_REQ; mtu_msg.x_msg_header.msg_length = sizeof(mtu_msg); - mtu_msg.msg_data[0] = board->mtu; + mtu_msg.msg_data = board->mtu; coredev->sendrequest_handler(coredev->context, &mtu_msg, sizeof(mtu_msg)); @@ -852,7 +852,7 @@ static int smscore_configure_board(struct smscore_device_t *coredev) SMS_INIT_MSG(&crys_msg.x_msg_header, MSG_SMS_NEW_CRYSTAL_REQ, sizeof(crys_msg)); - crys_msg.msg_data[0] = board->crystal; + crys_msg.msg_data = board->crystal; coredev->sendrequest_handler(coredev->context, &crys_msg, sizeof(crys_msg)); @@ -1306,7 +1306,7 @@ static int smscore_init_device(struct smscore_device_t *coredev, int mode) msg = (struct sms_msg_data *)SMS_ALIGN_ADDRESS(buffer); SMS_INIT_MSG(&msg->x_msg_header, MSG_SMS_INIT_DEVICE_REQ, sizeof(struct sms_msg_data)); - msg->msg_data[0] = mode; + msg->msg_data = mode; rc = smscore_sendrequest_and_wait(coredev, msg, msg->x_msg_header. msg_length, @@ -1394,7 +1394,7 @@ int smscore_set_device_mode(struct smscore_device_t *coredev, int mode) SMS_INIT_MSG(&msg->x_msg_header, MSG_SMS_INIT_DEVICE_REQ, sizeof(struct sms_msg_data)); - msg->msg_data[0] = mode; + msg->msg_data = mode; rc = smscore_sendrequest_and_wait( coredev, msg, msg->x_msg_header.msg_length, @@ -1554,7 +1554,7 @@ void smscore_onresponse(struct smscore_device_t *coredev, struct sms_msg_data *validity = (struct sms_msg_data *) phdr; pr_debug("MSG_SMS_DATA_VALIDITY_RES, checksum = 0x%x\n", - validity->msg_data[0]); + validity->msg_data); complete(&coredev->data_validity_done); break; } diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h index f8789ee0d554..46dc74ac9318 100644 --- a/drivers/media/common/siano/smscoreapi.h +++ b/drivers/media/common/siano/smscoreapi.h @@ -616,7 +616,7 @@ struct sms_msg_hdr { struct sms_msg_data { struct sms_msg_hdr x_msg_header; - u32 msg_data[1]; + u32 msg_data; }; struct sms_msg_data2 { diff --git a/drivers/media/common/siano/smsdvb-main.c b/drivers/media/common/siano/smsdvb-main.c index d893a0e4672b..44d8fe8b220e 100644 --- a/drivers/media/common/siano/smsdvb-main.c +++ b/drivers/media/common/siano/smsdvb-main.c @@ -689,7 +689,7 @@ static int smsdvb_start_feed(struct dvb_demux_feed *feed) pid_msg.x_msg_header.msg_flags = 0; pid_msg.x_msg_header.msg_type = MSG_SMS_ADD_PID_FILTER_REQ; pid_msg.x_msg_header.msg_length = sizeof(pid_msg); - pid_msg.msg_data[0] = feed->pid; + pid_msg.msg_data = feed->pid; return smsclient_sendrequest(client->smsclient, &pid_msg, sizeof(pid_msg)); @@ -711,7 +711,7 @@ static int smsdvb_stop_feed(struct dvb_demux_feed *feed) pid_msg.x_msg_header.msg_flags = 0; pid_msg.x_msg_header.msg_type = MSG_SMS_REMOVE_PID_FILTER_REQ; pid_msg.x_msg_header.msg_length = sizeof(pid_msg); - pid_msg.msg_data[0] = feed->pid; + pid_msg.msg_data = feed->pid; return smsclient_sendrequest(client->smsclient, &pid_msg, sizeof(pid_msg)); diff --git a/drivers/media/common/siano/smsendian.c b/drivers/media/common/siano/smsendian.c index a3573814919b..b957970c7d97 100644 --- a/drivers/media/common/siano/smsendian.c +++ b/drivers/media/common/siano/smsendian.c @@ -20,11 +20,12 @@ void smsendian_handle_tx_message(void *buffer) struct sms_msg_data *msg = buffer; int i; int msg_words; + u32 *msg_data = &msg->msg_data; switch (msg->x_msg_header.msg_type) { case MSG_SMS_DATA_DOWNLOAD_REQ: { - msg->msg_data[0] = le32_to_cpu((__force __le32)(msg->msg_data[0])); + msg->msg_data = le32_to_cpu((__force __le32)(msg->msg_data)); break; } @@ -33,7 +34,7 @@ void smsendian_handle_tx_message(void *buffer) sizeof(struct sms_msg_hdr))/4; for (i = 0; i < msg_words; i++) - msg->msg_data[i] = le32_to_cpu((__force __le32)msg->msg_data[i]); + msg_data[i] = le32_to_cpu((__force __le32)msg_data[i]); break; } @@ -66,11 +67,12 @@ void smsendian_handle_rx_message(void *buffer) default: { + u32 *msg_data = &msg->msg_data; msg_words = (msg->x_msg_header.msg_length - sizeof(struct sms_msg_hdr))/4; for (i = 0; i < msg_words; i++) - msg->msg_data[i] = le32_to_cpu((__force __le32)msg->msg_data[i]); + msg_data[i] = le32_to_cpu((__force __le32)msg_data[i]); break; } -- cgit v1.2.3 From b657179a11d9834df16b3bc2015f2931d20de528 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 27 May 2024 21:08:57 +0000 Subject: media: siano: Remove unused structures These structs are not used in the code, remove them. This fixes the following cocci warning: drivers/media/common/siano/smscoreapi.h:1049:4-8: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) drivers/media/common/siano/smscoreapi.h:1055:4-8: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/common/siano/smscoreapi.h | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h index 46dc74ac9318..bc61bc8b9ea9 100644 --- a/drivers/media/common/siano/smscoreapi.h +++ b/drivers/media/common/siano/smscoreapi.h @@ -1042,20 +1042,6 @@ struct sms_srvm_signal_status { u32 request_id; }; -struct sms_i2c_req { - u32 device_address; /* I2c device address */ - u32 write_count; /* number of bytes to write */ - u32 read_count; /* number of bytes to read */ - u8 Data[1]; -}; - -struct sms_i2c_res { - u32 status; /* non-zero value in case of failure */ - u32 read_count; /* number of bytes read */ - u8 Data[1]; -}; - - struct smscore_config_gpio { #define SMS_GPIO_DIRECTION_INPUT 0 #define SMS_GPIO_DIRECTION_OUTPUT 1 -- cgit v1.2.3 From 364ae46413f67f97e4611b050d7d53079dbb9dba Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 27 May 2024 21:08:58 +0000 Subject: media: siano: Use flex arrays for sms_firmware Replace old style single array member, with flex array. The struct is allocated, but it seems like there was an over allocation error: fw_buf = kmalloc(ALIGN(fw->size + sizeof(struct sms_firmware), SMS_ALLOC_ALIGNMENT), GFP_KERNEL | coredev->gfp_buf_flags); This change fixes this cocci warning: drivers/media/common/siano/smscoreapi.h:669:6-13: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/common/siano/smscoreapi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h index bc61bc8b9ea9..82d9f8a64d99 100644 --- a/drivers/media/common/siano/smscoreapi.h +++ b/drivers/media/common/siano/smscoreapi.h @@ -666,7 +666,7 @@ struct sms_firmware { u32 check_sum; u32 length; u32 start_address; - u8 payload[1]; + u8 payload[]; }; /* statistics information returned as response for -- cgit v1.2.3 From 7de8cf94b58f779b92124da26fc68ecda473e41c Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 27 May 2024 22:14:45 +0000 Subject: media: pci/ivtv: Replace ioremap with devm_ variants With Managed Device Resources the error handling is simpler. The following smatch warning is silenced: drivers/media/pci/ivtv/ivtv-driver.c: drivers/media/pci/ivtv/ivtv-driver.c:1296 ivtv_probe() warn: 'itv->dec_mem' from ioremap() not released on lines: 1296. drivers/media/pci/ivtv/ivtv-driver.c: drivers/media/pci/ivtv/ivtv-driver.c:1296 ivtv_probe() warn: 'itv->enc_mem' from ioremap() not released on lines: 1296. Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/pci/ivtv/ivtv-driver.c | 51 +++++++++--------------------------- 1 file changed, 12 insertions(+), 39 deletions(-) diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c index ba503d820e48..7599fffb0c9d 100644 --- a/drivers/media/pci/ivtv/ivtv-driver.c +++ b/drivers/media/pci/ivtv/ivtv-driver.c @@ -371,33 +371,6 @@ int ivtv_msleep_timeout(unsigned int msecs, int intr) return 0; } -/* Release ioremapped memory */ -static void ivtv_iounmap(struct ivtv *itv) -{ - if (itv == NULL) - return; - - /* Release registers memory */ - if (itv->reg_mem != NULL) { - IVTV_DEBUG_INFO("releasing reg_mem\n"); - iounmap(itv->reg_mem); - itv->reg_mem = NULL; - } - /* Release io memory */ - if (itv->has_cx23415 && itv->dec_mem != NULL) { - IVTV_DEBUG_INFO("releasing dec_mem\n"); - iounmap(itv->dec_mem); - } - itv->dec_mem = NULL; - - /* Release io memory */ - if (itv->enc_mem != NULL) { - IVTV_DEBUG_INFO("releasing enc_mem\n"); - iounmap(itv->enc_mem); - itv->enc_mem = NULL; - } -} - /* Hauppauge card? get values from tveeprom */ void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv) { @@ -1041,8 +1014,9 @@ static int ivtv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) /* map io memory */ IVTV_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n", (u64)itv->base_addr + IVTV_ENCODER_OFFSET, IVTV_ENCODER_SIZE); - itv->enc_mem = ioremap(itv->base_addr + IVTV_ENCODER_OFFSET, - IVTV_ENCODER_SIZE); + itv->enc_mem = devm_ioremap(&pdev->dev, + itv->base_addr + IVTV_ENCODER_OFFSET, + IVTV_ENCODER_SIZE); if (!itv->enc_mem) { IVTV_ERR("ioremap failed. Can't get a window into CX23415/6 encoder memory\n"); IVTV_ERR("Each capture card with a CX23415/6 needs 8 MB of vmalloc address space for this window\n"); @@ -1055,8 +1029,9 @@ static int ivtv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) if (itv->has_cx23415) { IVTV_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n", (u64)itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); - itv->dec_mem = ioremap(itv->base_addr + IVTV_DECODER_OFFSET, - IVTV_DECODER_SIZE); + itv->dec_mem = devm_ioremap(&pdev->dev, + itv->base_addr + IVTV_DECODER_OFFSET, + IVTV_DECODER_SIZE); if (!itv->dec_mem) { IVTV_ERR("ioremap failed. Can't get a window into CX23415 decoder memory\n"); IVTV_ERR("Each capture card with a CX23415 needs 8 MB of vmalloc address space for this window\n"); @@ -1073,26 +1048,27 @@ static int ivtv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) /* map registers memory */ IVTV_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n", (u64)itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); - itv->reg_mem = - ioremap(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); + itv->reg_mem = devm_ioremap(&pdev->dev, + itv->base_addr + IVTV_REG_OFFSET, + IVTV_REG_SIZE); if (!itv->reg_mem) { IVTV_ERR("ioremap failed. Can't get a window into CX23415/6 register space\n"); IVTV_ERR("Each capture card with a CX23415/6 needs 64 kB of vmalloc address space for this window\n"); IVTV_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n"); IVTV_ERR("Use the vmalloc= kernel command line option to set VmallocTotal to a larger value\n"); retval = -ENOMEM; - goto free_io; + goto free_mem; } retval = ivtv_gpio_init(itv); if (retval) - goto free_io; + goto free_mem; /* active i2c */ IVTV_DEBUG_INFO("activating i2c...\n"); if (init_ivtv_i2c(itv)) { IVTV_ERR("Could not initialize i2c\n"); - goto free_io; + goto free_mem; } if (itv->card->hw_all & IVTV_HW_TVEEPROM) { @@ -1277,8 +1253,6 @@ free_irq: free_i2c: v4l2_ctrl_handler_free(&itv->cxhdl.hdl); exit_ivtv_i2c(itv); -free_io: - ivtv_iounmap(itv); free_mem: release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); @@ -1439,7 +1413,6 @@ static void ivtv_remove(struct pci_dev *pdev) exit_ivtv_i2c(itv); free_irq(itv->pdev->irq, (void *)itv); - ivtv_iounmap(itv); release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); -- cgit v1.2.3 From 1c11a4cf06cd7ea8443144bba6f9ce43f037bf89 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 27 May 2024 22:14:46 +0000 Subject: media: pci/ivtv: Replace request_mem_region with devm_ variant The managed device resource version of the function greatly simplifies the error handling. Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/pci/ivtv/ivtv-driver.c | 45 +++++++++++------------------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c index 7599fffb0c9d..96f40c9685d2 100644 --- a/drivers/media/pci/ivtv/ivtv-driver.c +++ b/drivers/media/pci/ivtv/ivtv-driver.c @@ -814,24 +814,24 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *pdev, IVTV_ERR("No suitable DMA available.\n"); return -EIO; } - if (!request_mem_region(itv->base_addr, IVTV_ENCODER_SIZE, "ivtv encoder")) { + if (!devm_request_mem_region(&pdev->dev, itv->base_addr, + IVTV_ENCODER_SIZE, "ivtv encoder")) { IVTV_ERR("Cannot request encoder memory region.\n"); return -EIO; } - if (!request_mem_region(itv->base_addr + IVTV_REG_OFFSET, - IVTV_REG_SIZE, "ivtv registers")) { + if (!devm_request_mem_region(&pdev->dev, + itv->base_addr + IVTV_REG_OFFSET, + IVTV_REG_SIZE, "ivtv registers")) { IVTV_ERR("Cannot request register memory region.\n"); - release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); return -EIO; } if (itv->has_cx23415 && - !request_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, - IVTV_DECODER_SIZE, "ivtv decoder")) { + !devm_request_mem_region(&pdev->dev, + itv->base_addr + IVTV_DECODER_OFFSET, + IVTV_DECODER_SIZE, "ivtv decoder")) { IVTV_ERR("Cannot request decoder memory region.\n"); - release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); - release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); return -EIO; } @@ -843,11 +843,6 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *pdev, pci_read_config_word(pdev, PCI_COMMAND, &cmd); if (!(cmd & PCI_COMMAND_MASTER)) { IVTV_ERR("Bus Mastering is not enabled\n"); - if (itv->has_cx23415) - release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, - IVTV_DECODER_SIZE); - release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); - release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); return -ENXIO; } } @@ -1006,10 +1001,8 @@ static int ivtv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) /* PCI Device Setup */ retval = ivtv_setup_pci(itv, pdev, pci_id); - if (retval == -EIO) + if (retval == -EIO || retval == -ENXIO) goto free_worker; - if (retval == -ENXIO) - goto free_mem; /* map io memory */ IVTV_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n", @@ -1023,7 +1016,7 @@ static int ivtv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) IVTV_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n"); IVTV_ERR("Use the vmalloc= kernel command line option to set VmallocTotal to a larger value\n"); retval = -ENOMEM; - goto free_mem; + goto free_worker; } if (itv->has_cx23415) { @@ -1038,7 +1031,7 @@ static int ivtv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) IVTV_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n"); IVTV_ERR("Use the vmalloc= kernel command line option to set VmallocTotal to a larger value\n"); retval = -ENOMEM; - goto free_mem; + goto free_worker; } } else { @@ -1057,18 +1050,18 @@ static int ivtv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) IVTV_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n"); IVTV_ERR("Use the vmalloc= kernel command line option to set VmallocTotal to a larger value\n"); retval = -ENOMEM; - goto free_mem; + goto free_worker; } retval = ivtv_gpio_init(itv); if (retval) - goto free_mem; + goto free_worker; /* active i2c */ IVTV_DEBUG_INFO("activating i2c...\n"); if (init_ivtv_i2c(itv)) { IVTV_ERR("Could not initialize i2c\n"); - goto free_mem; + goto free_worker; } if (itv->card->hw_all & IVTV_HW_TVEEPROM) { @@ -1253,11 +1246,6 @@ free_irq: free_i2c: v4l2_ctrl_handler_free(&itv->cxhdl.hdl); exit_ivtv_i2c(itv); -free_mem: - release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); - release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); - if (itv->has_cx23415) - release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); free_worker: kthread_stop(itv->irq_worker_task); err: @@ -1414,11 +1402,6 @@ static void ivtv_remove(struct pci_dev *pdev) free_irq(itv->pdev->irq, (void *)itv); - release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); - release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); - if (itv->has_cx23415) - release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); - pci_disable_device(itv->pdev); for (i = 0; i < IVTV_VBI_FRAMES; i++) kfree(itv->vbi.sliced_mpeg_data[i]); -- cgit v1.2.3 From b8b2b1a5d26d5ecf545d77384db1174a42cd89f6 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 27 May 2024 22:14:47 +0000 Subject: media: pci/ivtv: Use managed version of pci_enable_device If there is any error during probing, we should probably leave the device in its previous state. pcim_ will take care of this. Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/pci/ivtv/ivtv-driver.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c index 96f40c9685d2..ecc20cd89926 100644 --- a/drivers/media/pci/ivtv/ivtv-driver.c +++ b/drivers/media/pci/ivtv/ivtv-driver.c @@ -806,7 +806,7 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *pdev, IVTV_DEBUG_INFO("Enabling pci device\n"); - if (pci_enable_device(pdev)) { + if (pcim_enable_device(pdev)) { IVTV_ERR("Can't enable device!\n"); return -EIO; } @@ -1402,7 +1402,6 @@ static void ivtv_remove(struct pci_dev *pdev) free_irq(itv->pdev->irq, (void *)itv); - pci_disable_device(itv->pdev); for (i = 0; i < IVTV_VBI_FRAMES; i++) kfree(itv->vbi.sliced_mpeg_data[i]); -- cgit v1.2.3 From 58154dbda4345299bff30eb78cbce6bc6dafcf84 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Tue, 28 May 2024 16:05:14 +0200 Subject: media: rockchip: rga: fix sequence number handling The RGA driver didn't set the sequence numbers of the returned buffers. Keep track of the CAPTURE and OUTPUT sequence numbers, and set the sequence numbers in the source and destination buffers. Signed-off-by: Michael Tretter Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga-buf.c | 5 +++++ drivers/media/platform/rockchip/rga/rga.c | 4 ++++ drivers/media/platform/rockchip/rga/rga.h | 3 +++ 3 files changed, 12 insertions(+) diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c index 662c81b6d0b5..70808049d2e8 100644 --- a/drivers/media/platform/rockchip/rga/rga-buf.c +++ b/drivers/media/platform/rockchip/rga/rga-buf.c @@ -195,6 +195,11 @@ static int rga_buf_start_streaming(struct vb2_queue *q, unsigned int count) return ret; } + if (V4L2_TYPE_IS_OUTPUT(q->type)) + ctx->osequence = 0; + else + ctx->csequence = 0; + return 0; } diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c index 00fdfa9e10bc..0e768f3e9eda 100644 --- a/drivers/media/platform/rockchip/rga/rga.c +++ b/drivers/media/platform/rockchip/rga/rga.c @@ -43,6 +43,8 @@ static void device_run(void *prv) rga->curr = ctx; src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + src->sequence = ctx->osequence++; + dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); rga_hw_start(rga, vb_to_rga(src), vb_to_rga(dst)); @@ -75,6 +77,8 @@ static irqreturn_t rga_isr(int irq, void *prv) v4l2_m2m_buf_copy_metadata(src, dst, true); + dst->sequence = ctx->csequence++; + v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); v4l2_m2m_job_finish(rga->m2m_dev, ctx->fh.m2m_ctx); diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h index 3502dff6055c..8105bb2efe57 100644 --- a/drivers/media/platform/rockchip/rga/rga.h +++ b/drivers/media/platform/rockchip/rga/rga.h @@ -57,6 +57,9 @@ struct rga_ctx { struct rga_frame out; struct v4l2_ctrl_handler ctrl_handler; + int osequence; + int csequence; + /* Control values */ u32 op; u32 hflip; -- cgit v1.2.3 From 96646f3a5a5dd32232c337cf468830113be4e48c Mon Sep 17 00:00:00 2001 From: Fritz Koenig Date: Wed, 29 May 2024 15:22:24 -0700 Subject: Documentation: media: Fix v4l2_ctrl_vp8_frame struct Description and control were out of sync. Signed-off-by: Fritz Koenig Signed-off-by: Hans Verkuil --- Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst index 786127b1e206..22bde00d42df 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst @@ -971,8 +971,8 @@ FWHT Flags - ``horizontal_scale`` - Horizontal scaling factor. * - __u8 - - ``vertical_scaling factor`` - - Vertical scale. + - ``vertical_scale`` + - Vertical scaling factor. * - __u8 - ``version`` - Bitstream version. -- cgit v1.2.3 From 327f961f77ea56ec3fbc0b3d66452dfe9f0f4d2e Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Thu, 30 May 2024 14:26:17 +0100 Subject: media: amphion: remove unused struct 'vpu_malone_frame_buffer' 'vpu_malone_frame_buffer' has been unused since the original commit 145e936380ed ("media: amphion: implement malone decoder rpc interface"). Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Hans Verkuil --- drivers/media/platform/amphion/vpu_malone.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/media/platform/amphion/vpu_malone.c b/drivers/media/platform/amphion/vpu_malone.c index d3425de7bccd..4769c053c6c2 100644 --- a/drivers/media/platform/amphion/vpu_malone.c +++ b/drivers/media/platform/amphion/vpu_malone.c @@ -207,11 +207,6 @@ struct vpu_malone_dbglog_desc { u32 reserved; }; -struct vpu_malone_frame_buffer { - u32 addr; - u32 size; -}; - struct vpu_malone_udata { u32 base; u32 total_size; -- cgit v1.2.3 From 4f72947c694ce486af074b076f2e08dfaad9aeca Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Thu, 30 May 2024 14:26:18 +0100 Subject: media: m2m-deinterlace: remove unused struct 'vb2_dc_conf' 'vb2_dc_conf' has been unused since the original commit 8f0755c06b90 ("[media] media: Add mem2mem deinterlacing driver"). Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Hans Verkuil --- drivers/media/platform/m2m-deinterlace.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 96b35a5d6174..5adcef80c698 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -724,10 +724,6 @@ static const struct v4l2_ioctl_ops deinterlace_ioctl_ops = { /* * Queue operations */ -struct vb2_dc_conf { - struct device *dev; -}; - static int deinterlace_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) -- cgit v1.2.3 From 6c173766a547fb086c60da44fcb50e0542de78f9 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Thu, 30 May 2024 14:26:19 +0100 Subject: media: tegra-vde: remove unused struct 'tegra_vde_h264_frame' 'tegra_vde_h264_frame' has been unused since commit 313db7d235a0 ("media: staging: tegra-vde: Remove legacy UAPI support"). Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Hans Verkuil --- drivers/media/platform/nvidia/tegra-vde/h264.c | 5 ----- drivers/media/platform/nvidia/tegra-vde/vde.h | 1 - 2 files changed, 6 deletions(-) diff --git a/drivers/media/platform/nvidia/tegra-vde/h264.c b/drivers/media/platform/nvidia/tegra-vde/h264.c index cfea5572a1b8..d8812fc06c67 100644 --- a/drivers/media/platform/nvidia/tegra-vde/h264.c +++ b/drivers/media/platform/nvidia/tegra-vde/h264.c @@ -19,11 +19,6 @@ #define FLAG_B_FRAME 0x1 #define FLAG_REFERENCE 0x2 -struct tegra_vde_h264_frame { - unsigned int frame_num; - unsigned int flags; -}; - struct tegra_vde_h264_decoder_ctx { unsigned int dpb_frames_nb; unsigned int dpb_ref_frames_with_earlier_poc_nb; diff --git a/drivers/media/platform/nvidia/tegra-vde/vde.h b/drivers/media/platform/nvidia/tegra-vde/vde.h index 0fbb1f3d2c88..b2890484b7c3 100644 --- a/drivers/media/platform/nvidia/tegra-vde/vde.h +++ b/drivers/media/platform/nvidia/tegra-vde/vde.h @@ -47,7 +47,6 @@ struct iommu_group; struct iommu_domain; struct reset_control; struct dma_buf_attachment; -struct tegra_vde_h264_frame; struct tegra_vde_h264_decoder_ctx; struct tegra_video_frame { -- cgit v1.2.3 From 51f1f787c7c9c9a241794315d02ad82dec77d8e2 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Mon, 29 Apr 2024 14:04:36 +0100 Subject: media: mceusb: No need for vendor/product ID in name This is available in other places and doesn't belong in the name of the rc device. Signed-off-by: Sean Young Signed-off-by: Hans Verkuil --- drivers/media/rc/mceusb.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index c76ba24c1f55..615f48898300 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -494,7 +494,6 @@ struct mceusb_dev { u32 carrier; unsigned char tx_mask; - char name[128]; char phys[64]; enum mceusb_model_type model; @@ -1591,16 +1590,10 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir) goto out; } - snprintf(ir->name, sizeof(ir->name), "%s (%04x:%04x)", - mceusb_model[ir->model].name ? - mceusb_model[ir->model].name : - "Media Center Ed. eHome Infrared Remote Transceiver", - le16_to_cpu(ir->usbdev->descriptor.idVendor), - le16_to_cpu(ir->usbdev->descriptor.idProduct)); - usb_make_path(ir->usbdev, ir->phys, sizeof(ir->phys)); - rc->device_name = ir->name; + rc->device_name = mceusb_model[ir->model].name ? : + "Media Center Ed. eHome Infrared Remote Transceiver"; rc->input_phys = ir->phys; usb_to_input_id(ir->usbdev, &rc->input_id); rc->dev.parent = dev; -- cgit v1.2.3 From 2052138b7da52ad5ccaf74f736d00f39a1c9198c Mon Sep 17 00:00:00 2001 From: Zheng Yejian Date: Thu, 9 May 2024 20:44:14 +0800 Subject: media: dvb-usb: Fix unexpected infinite loop in dvb_usb_read_remote_control() Infinite log printing occurs during fuzz test: rc rc1: DViCO FusionHDTV DVB-T USB (LGZ201) as ... ... dvb-usb: schedule remote query interval to 100 msecs. dvb-usb: DViCO FusionHDTV DVB-T USB (LGZ201) successfully initialized ... dvb-usb: bulk message failed: -22 (1/0) dvb-usb: bulk message failed: -22 (1/0) dvb-usb: bulk message failed: -22 (1/0) ... dvb-usb: bulk message failed: -22 (1/0) Looking into the codes, there is a loop in dvb_usb_read_remote_control(), that is in rc_core_dvb_usb_remote_init() create a work that will call dvb_usb_read_remote_control(), and this work will reschedule itself at 'rc_interval' intervals to recursively call dvb_usb_read_remote_control(), see following code snippet: rc_core_dvb_usb_remote_init() { ... INIT_DELAYED_WORK(&d->rc_query_work, dvb_usb_read_remote_control); schedule_delayed_work(&d->rc_query_work, msecs_to_jiffies(rc_interval)); ... } dvb_usb_read_remote_control() { ... err = d->props.rc.core.rc_query(d); if (err) err(...) // Did not return even if query failed schedule_delayed_work(&d->rc_query_work, msecs_to_jiffies(rc_interval)); } When the infinite log printing occurs, the query callback 'd->props.rc.core.rc_query' is cxusb_rc_query(). And the log is due to the failure of finding a valid 'generic_bulk_ctrl_endpoint' in usb_bulk_msg(), see following code snippet: cxusb_rc_query() { cxusb_ctrl_msg() { dvb_usb_generic_rw() { ret = usb_bulk_msg(d->udev, usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint),...); if (ret) err("bulk message failed: %d (%d/%d)",ret,wlen,actlen); ... } ... } By analyzing the corresponding USB descriptor, it shows that the bNumEndpoints is 0 in its interface descriptor, but the 'generic_bulk_ctrl_endpoint' is 1, that means user don't configure a valid endpoint for 'generic_bulk_ctrl_endpoint', therefore this 'invalid' USB device should be rejected before it calls into dvb_usb_read_remote_control(). To fix it, we need to add endpoint check for 'generic_bulk_ctrl_endpoint'. And as Sean suggested, the same check and clear halts should be done for 'generic_bulk_ctrl_endpoint_response'. So introduce dvb_usb_check_bulk_endpoint() to do it for both of them. Fixes: 4d43e13f723e ("V4L/DVB (4643): Multi-input patch for DVB-USB device") Signed-off-by: Zheng Yejian Signed-off-by: Sean Young Signed-off-by: Hans Verkuil --- drivers/media/usb/dvb-usb/dvb-usb-init.c | 35 ++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/dvb-usb/dvb-usb-init.c b/drivers/media/usb/dvb-usb/dvb-usb-init.c index fbf58012becd..22d83ac18eb7 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-init.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-init.c @@ -23,11 +23,40 @@ static int dvb_usb_force_pid_filter_usage; module_param_named(force_pid_filter_usage, dvb_usb_force_pid_filter_usage, int, 0444); MODULE_PARM_DESC(force_pid_filter_usage, "force all dvb-usb-devices to use a PID filter, if any (default: 0)."); +static int dvb_usb_check_bulk_endpoint(struct dvb_usb_device *d, u8 endpoint) +{ + if (endpoint) { + int ret; + + ret = usb_pipe_type_check(d->udev, usb_sndbulkpipe(d->udev, endpoint)); + if (ret) + return ret; + ret = usb_pipe_type_check(d->udev, usb_rcvbulkpipe(d->udev, endpoint)); + if (ret) + return ret; + } + return 0; +} + +static void dvb_usb_clear_halt(struct dvb_usb_device *d, u8 endpoint) +{ + if (endpoint) { + usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, endpoint)); + usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, endpoint)); + } +} + static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs) { struct dvb_usb_adapter *adap; int ret, n, o; + ret = dvb_usb_check_bulk_endpoint(d, d->props.generic_bulk_ctrl_endpoint); + if (ret) + return ret; + ret = dvb_usb_check_bulk_endpoint(d, d->props.generic_bulk_ctrl_endpoint_response); + if (ret) + return ret; for (n = 0; n < d->props.num_adapters; n++) { adap = &d->adapter[n]; adap->dev = d; @@ -103,10 +132,8 @@ static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs) * when reloading the driver w/o replugging the device * sometimes a timeout occurs, this helps */ - if (d->props.generic_bulk_ctrl_endpoint != 0) { - usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); - usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); - } + dvb_usb_clear_halt(d, d->props.generic_bulk_ctrl_endpoint); + dvb_usb_clear_halt(d, d->props.generic_bulk_ctrl_endpoint_response); return 0; -- cgit v1.2.3 From 24147897507cd3a7d63745d1518a638bf4132238 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 6 May 2024 21:10:27 +0000 Subject: media: imon: Fix race getting ictx->lock Lets fix a race between mutex_is_lock() and mutex_lock(). <-mutex is not locked if (!mutex_is_locked(&ictx->lock)) { unlock = true; <- mutex is locked externaly mutex_lock(&ictx->lock); } Let's use mutex_trylock() that does mutex_is_lock() and mutex_lock() atomically. Fix the following cocci warning: drivers/media/rc/imon.c:1167:1-7: preceding lock on line 1153 Fixes: 23ef710e1a6c ("[media] imon: add conditional locking in change_protocol") Signed-off-by: Ricardo Ribalda Signed-off-by: Sean Young Signed-off-by: Hans Verkuil --- drivers/media/rc/imon.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 0b55314a8082..8f1361bcce3a 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -1148,10 +1148,7 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 *rc_proto) memcpy(ictx->usb_tx_buf, &ir_proto_packet, sizeof(ir_proto_packet)); - if (!mutex_is_locked(&ictx->lock)) { - unlock = true; - mutex_lock(&ictx->lock); - } + unlock = mutex_trylock(&ictx->lock); retval = send_packet(ictx); if (retval) -- cgit v1.2.3 From d0edc54455398248154062664f7d1b35e6ec6e01 Mon Sep 17 00:00:00 2001 From: Bingbu Cao Date: Fri, 10 May 2024 14:27:47 +0800 Subject: media: ipu-bridge: add mod_devicetable.h header inclusion ACPI_ID_LEN is defined in mod_devicetable.h, so the header should be guaranteed to included in ipu-bridge.h instead of the source files which include ipu-bridge.h. Signed-off-by: Bingbu Cao Reviewed-by: Andy Shevchenko Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- include/media/ipu-bridge.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/media/ipu-bridge.h b/include/media/ipu-bridge.h index 783bda6d5cc3..16fac765456e 100644 --- a/include/media/ipu-bridge.h +++ b/include/media/ipu-bridge.h @@ -3,6 +3,7 @@ #ifndef __IPU_BRIDGE_H #define __IPU_BRIDGE_H +#include #include #include #include -- cgit v1.2.3 From a1956bf53a2774014ee1768b484af2c38c633a25 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Thu, 9 May 2024 13:53:07 +0100 Subject: media: i2c: Fix imx412 exposure control Currently we have the following algorithm to calculate what value should be written to the exposure control of imx412. lpfr = imx412->vblank + imx412->cur_mode->height; shutter = lpfr - exposure; The 'shutter' value is given to IMX412_REG_EXPOSURE_CIT however, the above algorithm will result in the value given to IMX412_REG_EXPOSURE_CIT decreasing as the requested exposure value from user-space goes up. e.g. [ 2255.713989] imx412 20-001a: Received exp 1608, analog gain 0 [ 2255.714002] imx412 20-001a: Set exp 1608, analog gain 0, shutter 1938, lpfr 3546 [ 2256.302770] imx412 20-001a: Received exp 2586, analog gain 100 [ 2256.302800] imx412 20-001a: Set exp 2586, analog gain 100, shutter 960, lpfr 3546 [ 2256.753755] imx412 20-001a: Received exp 3524, analog gain 110 [ 2256.753772] imx412 20-001a: Set exp 3524, analog gain 110, shutter 22, lpfr 3546 This behaviour results in the image having less exposure as the requested exposure value from user-space increases. Other sensor drivers such as ov5675, imx218, hid556 and others take the requested exposure value and use the value directly. Take the example of the above cited sensor drivers and directly apply the requested exposure value from user-space. The 'lpfr' variable still functions as before but the 'shutter' variable can be dispensed with as a result. Once done a similar run of the test application requesting higher exposure looks like this, with 'exp' written directly to the sensor. [ 133.207884] imx412 20-001a: Received exp 1608, analog gain 0 [ 133.207899] imx412 20-001a: Set exp 1608, analog gain 0, lpfr 3546 [ 133.905309] imx412 20-001a: Received exp 2844, analog gain 100 [ 133.905344] imx412 20-001a: Set exp 2844, analog gain 100, lpfr 3546 [ 134.241705] imx412 20-001a: Received exp 3524, analog gain 110 [ 134.241775] imx412 20-001a: Set exp 3524, analog gain 110, lpfr 3546 The result is then setting the sensor exposure to lower values results in darker, less exposure images and vice versa with higher exposure values. Fixes: 9214e86c0cc1 ("media: i2c: Add imx412 camera sensor driver") Tested-by: Bryan O'Donoghue # qrb5165-rb5/imx577 Reviewed-by: Jacopo Mondi Reviewed-by: Gjorgji Rosikopulos Signed-off-by: Bryan O'Donoghue Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx412.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c index 0efce329525e..7d1f7af0a9df 100644 --- a/drivers/media/i2c/imx412.c +++ b/drivers/media/i2c/imx412.c @@ -542,14 +542,13 @@ static int imx412_update_controls(struct imx412 *imx412, */ static int imx412_update_exp_gain(struct imx412 *imx412, u32 exposure, u32 gain) { - u32 lpfr, shutter; + u32 lpfr; int ret; lpfr = imx412->vblank + imx412->cur_mode->height; - shutter = lpfr - exposure; - dev_dbg(imx412->dev, "Set exp %u, analog gain %u, shutter %u, lpfr %u", - exposure, gain, shutter, lpfr); + dev_dbg(imx412->dev, "Set exp %u, analog gain %u, lpfr %u", + exposure, gain, lpfr); ret = imx412_write_reg(imx412, IMX412_REG_HOLD, 1, 1); if (ret) @@ -559,7 +558,7 @@ static int imx412_update_exp_gain(struct imx412 *imx412, u32 exposure, u32 gain) if (ret) goto error_release_group_hold; - ret = imx412_write_reg(imx412, IMX412_REG_EXPOSURE_CIT, 2, shutter); + ret = imx412_write_reg(imx412, IMX412_REG_EXPOSURE_CIT, 2, exposure); if (ret) goto error_release_group_hold; -- cgit v1.2.3 From 984abe0b5794a2aca359bb61555351d2ec520d2a Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 9 May 2024 17:05:55 +0000 Subject: media: i2c: hi846: Fix V4L2_SUBDEV_FORMAT_TRY get_selection() The current code does not return anything to the user. Although the code looks a bit dangerous (using a pointer without checking if it is valid), it should be fine. The core validates that sel->pad has a valid value. Fix the following smatch error: drivers/media/i2c/hi846.c:1854 hi846_get_selection() warn: statement has no effect 31 Fixes: e8c0882685f9 ("media: i2c: add driver for the SK Hynix Hi-846 8M pixel camera") Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart [Sakari Ailus: code -> core.] Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/hi846.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/hi846.c b/drivers/media/i2c/hi846.c index 9c565ec033d4..52d9ca68a86c 100644 --- a/drivers/media/i2c/hi846.c +++ b/drivers/media/i2c/hi846.c @@ -1851,7 +1851,7 @@ static int hi846_get_selection(struct v4l2_subdev *sd, mutex_lock(&hi846->mutex); switch (sel->which) { case V4L2_SUBDEV_FORMAT_TRY: - v4l2_subdev_state_get_crop(sd_state, sel->pad); + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); break; case V4L2_SUBDEV_FORMAT_ACTIVE: sel->r = hi846->cur_mode->crop; -- cgit v1.2.3 From 9b4667ea67854f0b116fe22ad11ef5628c5b5b5f Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Wed, 8 May 2024 10:51:49 +0800 Subject: media: v4l: async: Fix NULL pointer dereference in adding ancillary links In v4l2_async_create_ancillary_links(), ancillary links are created for lens and flash sub-devices. These are sub-device to sub-device links and if the async notifier is related to a V4L2 device, the source sub-device of the ancillary link is NULL, leading to a NULL pointer dereference. Check the notifier's sd field is non-NULL in v4l2_async_create_ancillary_links(). Fixes: aa4faf6eb271 ("media: v4l2-async: Create links during v4l2_async_match_notify()") Signed-off-by: ChiYuan Huang [Sakari Ailus: Reword the subject and commit messages slightly.] Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-async.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 222f01665f7c..c477723c07bf 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -323,6 +323,9 @@ static int v4l2_async_create_ancillary_links(struct v4l2_async_notifier *n, sd->entity.function != MEDIA_ENT_F_FLASH) return 0; + if (!n->sd) + return 0; + link = media_create_ancillary_link(&n->sd->entity, &sd->entity); return IS_ERR(link) ? PTR_ERR(link) : 0; -- cgit v1.2.3 From e2adf52ee59101c6d2415511a72c836ecdada671 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 1 May 2024 09:24:18 -0600 Subject: media: i2c: imx258: Remove unused defines The IMX258_FLL_* defines are unused. Remove them. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi Signed-off-by: Luis Garcia Reviewed-by: Pavel Machek Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx258.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index a577afb530b7..2dbafd21dd70 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -29,12 +29,6 @@ #define IMX258_VTS_30FPS_VGA 0x034c #define IMX258_VTS_MAX 0xffff -/*Frame Length Line*/ -#define IMX258_FLL_MIN 0x08a6 -#define IMX258_FLL_MAX 0xffff -#define IMX258_FLL_STEP 1 -#define IMX258_FLL_DEFAULT 0x0c98 - /* HBLANK control - read only */ #define IMX258_PPL_DEFAULT 5352 -- cgit v1.2.3 From 5b9ee0401600d908ed7dd6582815a80957287683 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 1 May 2024 09:24:19 -0600 Subject: media: i2c: imx258: Make image geometry meet sensor requirements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The output image is defined as being 4208x3118 pixels in size. Y_ADD_STA register was set to 0, and Y_ADD_END to 3118, giving 3119 lines total. The datasheet lists a requirement for Y_ADD_STA to be a multiple of a power of 2 depending on binning/scaling mode (2 for full pixel, 4 for x2-bin/scale, 8 for (x2-bin)+(x2-subsample) or x4-bin, or 16 for (x4-bin)+(x2-subsample)). (Y_ADD_END – Y_ADD_STA + 1) also has to be a similar power of 2. The current configuration for the full res modes breaks that second requirement, and we can't increase Y_ADD_STA to 1 to retain exactly the same field of view as that then breaks the first requirement. For the binned modes, they are worse off as 3118 is not a multiple of 4. Increase the main mode to 4208x3120 so that it is the same FOV as the binned modes, with Y_ADD_STA at 0. Fix Y_ADD_STA and Y_ADD_END for the binned modes so that they meet the sensor requirements. This does change the Bayer order as the default configuration is for H&V flips to be enabled, so readout is from Y_STA_END to Y_ADD_STA, and this patch has changed Y_STA_END. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi Signed-off-by: Luis Garcia Reviewed-by: Pavel Machek Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx258.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index 2dbafd21dd70..4a7048d834c6 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -111,7 +111,7 @@ struct imx258_mode { struct imx258_reg_list reg_list; }; -/* 4208x3118 needs 1267Mbps/lane, 4 lanes */ +/* 4208x3120 needs 1267Mbps/lane, 4 lanes */ static const struct imx258_reg mipi_data_rate_1267mbps[] = { { 0x0301, 0x05 }, { 0x0303, 0x02 }, @@ -148,7 +148,7 @@ static const struct imx258_reg mipi_data_rate_640mbps[] = { { 0x0823, 0x00 }, }; -static const struct imx258_reg mode_4208x3118_regs[] = { +static const struct imx258_reg mode_4208x3120_regs[] = { { 0x0136, 0x13 }, { 0x0137, 0x33 }, { 0x3051, 0x00 }, @@ -210,7 +210,7 @@ static const struct imx258_reg mode_4208x3118_regs[] = { { 0x0348, 0x10 }, { 0x0349, 0x6F }, { 0x034A, 0x0C }, - { 0x034B, 0x2E }, + { 0x034B, 0x2F }, { 0x0381, 0x01 }, { 0x0383, 0x01 }, { 0x0385, 0x01 }, @@ -329,7 +329,7 @@ static const struct imx258_reg mode_2104_1560_regs[] = { { 0x0348, 0x10 }, { 0x0349, 0x6F }, { 0x034A, 0x0C }, - { 0x034B, 0x2E }, + { 0x034B, 0x2F }, { 0x0381, 0x01 }, { 0x0383, 0x01 }, { 0x0385, 0x01 }, @@ -448,7 +448,7 @@ static const struct imx258_reg mode_1048_780_regs[] = { { 0x0348, 0x10 }, { 0x0349, 0x6F }, { 0x034A, 0x0C }, - { 0x034B, 0x2E }, + { 0x034B, 0x2F }, { 0x0381, 0x01 }, { 0x0383, 0x01 }, { 0x0385, 0x01 }, @@ -562,12 +562,12 @@ static const struct imx258_link_freq_config link_freq_configs[] = { static const struct imx258_mode supported_modes[] = { { .width = 4208, - .height = 3118, + .height = 3120, .vts_def = IMX258_VTS_30FPS, .vts_min = IMX258_VTS_30FPS, .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_4208x3118_regs), - .regs = mode_4208x3118_regs, + .num_of_regs = ARRAY_SIZE(mode_4208x3120_regs), + .regs = mode_4208x3120_regs, }, .link_freq_index = IMX258_LINK_FREQ_1267MBPS, }, @@ -707,7 +707,7 @@ static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) /* Initialize try_fmt */ try_fmt->width = supported_modes[0].width; try_fmt->height = supported_modes[0].height; - try_fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; + try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10; try_fmt->field = V4L2_FIELD_NONE; return 0; @@ -819,7 +819,7 @@ static int imx258_enum_mbus_code(struct v4l2_subdev *sd, if (code->index > 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_SGRBG10_1X10; + code->code = MEDIA_BUS_FMT_SBGGR10_1X10; return 0; } @@ -831,7 +831,7 @@ static int imx258_enum_frame_size(struct v4l2_subdev *sd, if (fse->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; - if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10) return -EINVAL; fse->min_width = supported_modes[fse->index].width; @@ -847,7 +847,7 @@ static void imx258_update_pad_format(const struct imx258_mode *mode, { fmt->format.width = mode->width; fmt->format.height = mode->height; - fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; fmt->format.field = V4L2_FIELD_NONE; } @@ -894,7 +894,7 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd, mutex_lock(&imx258->mutex); /* Only one raw bayer(GBRG) order is supported */ - fmt->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; + fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes), width, height, -- cgit v1.2.3 From dc081787a8c30c28676f7aa5aaed650e6f480c59 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 1 May 2024 09:24:20 -0600 Subject: media: i2c: imx258: Disable digital cropping on binned modes The binned modes set DIG_CROP_X_OFFSET and DIG_CROP_IMAGE_WIDTH to less than the full image, even though the image being captured is meant to be a scaled version of the full array size. Reduce X_OFFSET to 0, and increase IMAGE_WIDTH to the full array. Signed-off-by: Dave Stevenson Signed-off-by: Luis Garcia Reviewed-by: Pavel Machek Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx258.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index 4a7048d834c6..0ae4371940ca 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -340,11 +340,11 @@ static const struct imx258_reg mode_2104_1560_regs[] = { { 0x0404, 0x00 }, { 0x0405, 0x20 }, { 0x0408, 0x00 }, - { 0x0409, 0x02 }, + { 0x0409, 0x00 }, { 0x040A, 0x00 }, { 0x040B, 0x00 }, { 0x040C, 0x10 }, - { 0x040D, 0x6A }, + { 0x040D, 0x70 }, { 0x040E, 0x06 }, { 0x040F, 0x18 }, { 0x3038, 0x00 }, @@ -459,11 +459,11 @@ static const struct imx258_reg mode_1048_780_regs[] = { { 0x0404, 0x00 }, { 0x0405, 0x40 }, { 0x0408, 0x00 }, - { 0x0409, 0x06 }, + { 0x0409, 0x00 }, { 0x040A, 0x00 }, { 0x040B, 0x00 }, { 0x040C, 0x10 }, - { 0x040D, 0x64 }, + { 0x040D, 0x70 }, { 0x040E, 0x03 }, { 0x040F, 0x0C }, { 0x3038, 0x00 }, -- cgit v1.2.3 From 04392d73ae360a4fc502243c1b408464963d5d98 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 1 May 2024 09:24:21 -0600 Subject: media: i2c: imx258: Remove redundant I2C writes. Registers 0x0202 and 0x0203 are written via the control handler for V4L2_CID_EXPOSURE, so are not needed from the mode lists. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi Signed-off-by: Luis Garcia Reviewed-by: Pavel Machek Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx258.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index 0ae4371940ca..df7ed4716762 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -237,8 +237,6 @@ static const struct imx258_reg mode_4208x3120_regs[] = { { 0x034E, 0x0C }, { 0x034F, 0x30 }, { 0x0350, 0x01 }, - { 0x0202, 0x0C }, - { 0x0203, 0x46 }, { 0x0204, 0x00 }, { 0x0205, 0x00 }, { 0x020E, 0x01 }, @@ -356,8 +354,6 @@ static const struct imx258_reg mode_2104_1560_regs[] = { { 0x034E, 0x06 }, { 0x034F, 0x18 }, { 0x0350, 0x01 }, - { 0x0202, 0x06 }, - { 0x0203, 0x2E }, { 0x0204, 0x00 }, { 0x0205, 0x00 }, { 0x020E, 0x01 }, @@ -475,8 +471,6 @@ static const struct imx258_reg mode_1048_780_regs[] = { { 0x034E, 0x03 }, { 0x034F, 0x0C }, { 0x0350, 0x01 }, - { 0x0202, 0x03 }, - { 0x0203, 0x42 }, { 0x0204, 0x00 }, { 0x0205, 0x00 }, { 0x020E, 0x01 }, -- cgit v1.2.3 From 850b8acde0b50d9bb894d26ed8cae0f85b89511b Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 1 May 2024 09:24:22 -0600 Subject: media: i2c: imx258: Add regulator control The device tree bindings define the relevant regulators for the sensor, so update the driver to request the regulators and control them at the appropriate times. Signed-off-by: Dave Stevenson Signed-off-by: Luis Garcia Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx258.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index df7ed4716762..495eaada2945 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -507,6 +508,16 @@ static const char * const imx258_test_pattern_menu[] = { "Pseudorandom Sequence (PN9)", }; +/* regulator supplies */ +static const char * const imx258_supply_name[] = { + /* Supplies can be enabled in any order */ + "vana", /* Analog (2.8V) supply */ + "vdig", /* Digital Core (1.2V) supply */ + "vif", /* IF (1.8V) supply */ +}; + +#define IMX258_NUM_SUPPLIES ARRAY_SIZE(imx258_supply_name) + /* Configurations for supported link frequencies */ #define IMX258_LINK_FREQ_634MHZ 633600000ULL #define IMX258_LINK_FREQ_320MHZ 320000000ULL @@ -611,6 +622,7 @@ struct imx258 { struct mutex mutex; struct clk *clk; + struct regulator_bulk_data supplies[IMX258_NUM_SUPPLIES]; }; static inline struct imx258 *to_imx258(struct v4l2_subdev *_sd) @@ -995,9 +1007,19 @@ static int imx258_power_on(struct device *dev) struct imx258 *imx258 = to_imx258(sd); int ret; + ret = regulator_bulk_enable(IMX258_NUM_SUPPLIES, + imx258->supplies); + if (ret) { + dev_err(dev, "%s: failed to enable regulators\n", + __func__); + return ret; + } + ret = clk_prepare_enable(imx258->clk); - if (ret) + if (ret) { dev_err(dev, "failed to enable clock\n"); + regulator_bulk_disable(IMX258_NUM_SUPPLIES, imx258->supplies); + } return ret; } @@ -1008,6 +1030,7 @@ static int imx258_power_off(struct device *dev) struct imx258 *imx258 = to_imx258(sd); clk_disable_unprepare(imx258->clk); + regulator_bulk_disable(IMX258_NUM_SUPPLIES, imx258->supplies); return 0; } @@ -1220,6 +1243,18 @@ static void imx258_free_controls(struct imx258 *imx258) mutex_destroy(&imx258->mutex); } +static int imx258_get_regulators(struct imx258 *imx258, + struct i2c_client *client) +{ + unsigned int i; + + for (i = 0; i < IMX258_NUM_SUPPLIES; i++) + imx258->supplies[i].supply = imx258_supply_name[i]; + + return devm_regulator_bulk_get(&client->dev, + IMX258_NUM_SUPPLIES, imx258->supplies); +} + static int imx258_probe(struct i2c_client *client) { struct imx258 *imx258; @@ -1230,6 +1265,11 @@ static int imx258_probe(struct i2c_client *client) if (!imx258) return -ENOMEM; + ret = imx258_get_regulators(imx258, client); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to get regulators\n"); + imx258->clk = devm_clk_get_optional(&client->dev, NULL); if (IS_ERR(imx258->clk)) return dev_err_probe(&client->dev, PTR_ERR(imx258->clk), -- cgit v1.2.3 From 67b5a3606d050413656bfbb12578c7b9cee0d411 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 1 May 2024 09:24:23 -0600 Subject: media: i2c: imx258: Make V4L2_CID_VBLANK configurable. The values and ranges of V4L2_CID_VBLANK are all computed, so there is no reason for it to be a read only control. Remove the register values from the mode lists, add the handler, and remove the read only flag. Signed-off-by: Dave Stevenson Signed-off-by: Luis Garcia Reviewed-by: Pavel Machek Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx258.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index 495eaada2945..321b504c6a48 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -30,6 +30,8 @@ #define IMX258_VTS_30FPS_VGA 0x034c #define IMX258_VTS_MAX 0xffff +#define IMX258_REG_VTS 0x0340 + /* HBLANK control - read only */ #define IMX258_PPL_DEFAULT 5352 @@ -202,8 +204,6 @@ static const struct imx258_reg mode_4208x3120_regs[] = { { 0x0114, 0x03 }, { 0x0342, 0x14 }, { 0x0343, 0xE8 }, - { 0x0340, 0x0C }, - { 0x0341, 0x50 }, { 0x0344, 0x00 }, { 0x0345, 0x00 }, { 0x0346, 0x00 }, @@ -319,8 +319,6 @@ static const struct imx258_reg mode_2104_1560_regs[] = { { 0x0114, 0x03 }, { 0x0342, 0x14 }, { 0x0343, 0xE8 }, - { 0x0340, 0x06 }, - { 0x0341, 0x38 }, { 0x0344, 0x00 }, { 0x0345, 0x00 }, { 0x0346, 0x00 }, @@ -436,8 +434,6 @@ static const struct imx258_reg mode_1048_780_regs[] = { { 0x0114, 0x03 }, { 0x0342, 0x14 }, { 0x0343, 0xE8 }, - { 0x0340, 0x03 }, - { 0x0341, 0x4C }, { 0x0344, 0x00 }, { 0x0345, 0x00 }, { 0x0346, 0x00 }, @@ -800,6 +796,11 @@ static int imx258_set_ctrl(struct v4l2_ctrl *ctrl) BIT(IMX258_HDR_RATIO_MAX)); } break; + case V4L2_CID_VBLANK: + ret = imx258_write_reg(imx258, IMX258_REG_VTS, + IMX258_REG_VALUE_16BIT, + imx258->cur_mode->height + ctrl->val); + break; default: dev_info(&client->dev, "ctrl(id:0x%x,val:0x%x) is not handled\n", @@ -1174,9 +1175,6 @@ static int imx258_init_controls(struct imx258 *imx258) IMX258_VTS_MAX - imx258->cur_mode->height, 1, vblank_def); - if (imx258->vblank) - imx258->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; - imx258->hblank = v4l2_ctrl_new_std( ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_HBLANK, IMX258_PPL_DEFAULT - imx258->cur_mode->width, -- cgit v1.2.3 From 186f4056ac66995f09eda63a221d1acd89bbb9e4 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 1 May 2024 09:24:24 -0600 Subject: media: i2c: imx258: Split out common registers from the mode based ones Out of all the registers that are defined for each mode, only around 10 differ between the modes. Split the table into common and mode specific ones. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi Signed-off-by: Luis Garcia Reviewed-by: Pavel Machek Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx258.c | 236 ++++----------------------------------------- 1 file changed, 21 insertions(+), 215 deletions(-) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index 321b504c6a48..351add1bc5d5 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -151,7 +151,7 @@ static const struct imx258_reg mipi_data_rate_640mbps[] = { { 0x0823, 0x00 }, }; -static const struct imx258_reg mode_4208x3120_regs[] = { +static const struct imx258_reg mode_common_regs[] = { { 0x0136, 0x13 }, { 0x0137, 0x33 }, { 0x3051, 0x00 }, @@ -216,27 +216,17 @@ static const struct imx258_reg mode_4208x3120_regs[] = { { 0x0383, 0x01 }, { 0x0385, 0x01 }, { 0x0387, 0x01 }, - { 0x0900, 0x00 }, - { 0x0901, 0x11 }, - { 0x0401, 0x00 }, { 0x0404, 0x00 }, - { 0x0405, 0x10 }, { 0x0408, 0x00 }, { 0x0409, 0x00 }, { 0x040A, 0x00 }, { 0x040B, 0x00 }, { 0x040C, 0x10 }, { 0x040D, 0x70 }, - { 0x040E, 0x0C }, - { 0x040F, 0x30 }, { 0x3038, 0x00 }, { 0x303A, 0x00 }, { 0x303B, 0x10 }, { 0x300D, 0x00 }, - { 0x034C, 0x10 }, - { 0x034D, 0x70 }, - { 0x034E, 0x0C }, - { 0x034F, 0x30 }, { 0x0350, 0x01 }, { 0x0204, 0x00 }, { 0x0205, 0x00 }, @@ -266,234 +256,43 @@ static const struct imx258_reg mode_4208x3120_regs[] = { { 0x0220, 0x00 }, }; +static const struct imx258_reg mode_4208x3120_regs[] = { + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0401, 0x00 }, + { 0x0405, 0x10 }, + { 0x040E, 0x0C }, + { 0x040F, 0x30 }, + { 0x034C, 0x10 }, + { 0x034D, 0x70 }, + { 0x034E, 0x0C }, + { 0x034F, 0x30 }, +}; + static const struct imx258_reg mode_2104_1560_regs[] = { - { 0x0136, 0x13 }, - { 0x0137, 0x33 }, - { 0x3051, 0x00 }, - { 0x3052, 0x00 }, - { 0x4E21, 0x14 }, - { 0x6B11, 0xCF }, - { 0x7FF0, 0x08 }, - { 0x7FF1, 0x0F }, - { 0x7FF2, 0x08 }, - { 0x7FF3, 0x1B }, - { 0x7FF4, 0x23 }, - { 0x7FF5, 0x60 }, - { 0x7FF6, 0x00 }, - { 0x7FF7, 0x01 }, - { 0x7FF8, 0x00 }, - { 0x7FF9, 0x78 }, - { 0x7FFA, 0x00 }, - { 0x7FFB, 0x00 }, - { 0x7FFC, 0x00 }, - { 0x7FFD, 0x00 }, - { 0x7FFE, 0x00 }, - { 0x7FFF, 0x03 }, - { 0x7F76, 0x03 }, - { 0x7F77, 0xFE }, - { 0x7FA8, 0x03 }, - { 0x7FA9, 0xFE }, - { 0x7B24, 0x81 }, - { 0x7B25, 0x00 }, - { 0x6564, 0x07 }, - { 0x6B0D, 0x41 }, - { 0x653D, 0x04 }, - { 0x6B05, 0x8C }, - { 0x6B06, 0xF9 }, - { 0x6B08, 0x65 }, - { 0x6B09, 0xFC }, - { 0x6B0A, 0xCF }, - { 0x6B0B, 0xD2 }, - { 0x6700, 0x0E }, - { 0x6707, 0x0E }, - { 0x9104, 0x00 }, - { 0x4648, 0x7F }, - { 0x7420, 0x00 }, - { 0x7421, 0x1C }, - { 0x7422, 0x00 }, - { 0x7423, 0xD7 }, - { 0x5F04, 0x00 }, - { 0x5F05, 0xED }, - { 0x0112, 0x0A }, - { 0x0113, 0x0A }, - { 0x0114, 0x03 }, - { 0x0342, 0x14 }, - { 0x0343, 0xE8 }, - { 0x0344, 0x00 }, - { 0x0345, 0x00 }, - { 0x0346, 0x00 }, - { 0x0347, 0x00 }, - { 0x0348, 0x10 }, - { 0x0349, 0x6F }, - { 0x034A, 0x0C }, - { 0x034B, 0x2F }, - { 0x0381, 0x01 }, - { 0x0383, 0x01 }, - { 0x0385, 0x01 }, - { 0x0387, 0x01 }, { 0x0900, 0x01 }, { 0x0901, 0x12 }, { 0x0401, 0x01 }, - { 0x0404, 0x00 }, { 0x0405, 0x20 }, - { 0x0408, 0x00 }, - { 0x0409, 0x00 }, - { 0x040A, 0x00 }, - { 0x040B, 0x00 }, - { 0x040C, 0x10 }, - { 0x040D, 0x70 }, { 0x040E, 0x06 }, { 0x040F, 0x18 }, - { 0x3038, 0x00 }, - { 0x303A, 0x00 }, - { 0x303B, 0x10 }, - { 0x300D, 0x00 }, { 0x034C, 0x08 }, { 0x034D, 0x38 }, { 0x034E, 0x06 }, { 0x034F, 0x18 }, - { 0x0350, 0x01 }, - { 0x0204, 0x00 }, - { 0x0205, 0x00 }, - { 0x020E, 0x01 }, - { 0x020F, 0x00 }, - { 0x0210, 0x01 }, - { 0x0211, 0x00 }, - { 0x0212, 0x01 }, - { 0x0213, 0x00 }, - { 0x0214, 0x01 }, - { 0x0215, 0x00 }, - { 0x7BCD, 0x01 }, - { 0x94DC, 0x20 }, - { 0x94DD, 0x20 }, - { 0x94DE, 0x20 }, - { 0x95DC, 0x20 }, - { 0x95DD, 0x20 }, - { 0x95DE, 0x20 }, - { 0x7FB0, 0x00 }, - { 0x9010, 0x3E }, - { 0x9419, 0x50 }, - { 0x941B, 0x50 }, - { 0x9519, 0x50 }, - { 0x951B, 0x50 }, - { 0x3030, 0x00 }, - { 0x3032, 0x00 }, - { 0x0220, 0x00 }, }; static const struct imx258_reg mode_1048_780_regs[] = { - { 0x0136, 0x13 }, - { 0x0137, 0x33 }, - { 0x3051, 0x00 }, - { 0x3052, 0x00 }, - { 0x4E21, 0x14 }, - { 0x6B11, 0xCF }, - { 0x7FF0, 0x08 }, - { 0x7FF1, 0x0F }, - { 0x7FF2, 0x08 }, - { 0x7FF3, 0x1B }, - { 0x7FF4, 0x23 }, - { 0x7FF5, 0x60 }, - { 0x7FF6, 0x00 }, - { 0x7FF7, 0x01 }, - { 0x7FF8, 0x00 }, - { 0x7FF9, 0x78 }, - { 0x7FFA, 0x00 }, - { 0x7FFB, 0x00 }, - { 0x7FFC, 0x00 }, - { 0x7FFD, 0x00 }, - { 0x7FFE, 0x00 }, - { 0x7FFF, 0x03 }, - { 0x7F76, 0x03 }, - { 0x7F77, 0xFE }, - { 0x7FA8, 0x03 }, - { 0x7FA9, 0xFE }, - { 0x7B24, 0x81 }, - { 0x7B25, 0x00 }, - { 0x6564, 0x07 }, - { 0x6B0D, 0x41 }, - { 0x653D, 0x04 }, - { 0x6B05, 0x8C }, - { 0x6B06, 0xF9 }, - { 0x6B08, 0x65 }, - { 0x6B09, 0xFC }, - { 0x6B0A, 0xCF }, - { 0x6B0B, 0xD2 }, - { 0x6700, 0x0E }, - { 0x6707, 0x0E }, - { 0x9104, 0x00 }, - { 0x4648, 0x7F }, - { 0x7420, 0x00 }, - { 0x7421, 0x1C }, - { 0x7422, 0x00 }, - { 0x7423, 0xD7 }, - { 0x5F04, 0x00 }, - { 0x5F05, 0xED }, - { 0x0112, 0x0A }, - { 0x0113, 0x0A }, - { 0x0114, 0x03 }, - { 0x0342, 0x14 }, - { 0x0343, 0xE8 }, - { 0x0344, 0x00 }, - { 0x0345, 0x00 }, - { 0x0346, 0x00 }, - { 0x0347, 0x00 }, - { 0x0348, 0x10 }, - { 0x0349, 0x6F }, - { 0x034A, 0x0C }, - { 0x034B, 0x2F }, - { 0x0381, 0x01 }, - { 0x0383, 0x01 }, - { 0x0385, 0x01 }, - { 0x0387, 0x01 }, { 0x0900, 0x01 }, { 0x0901, 0x14 }, { 0x0401, 0x01 }, - { 0x0404, 0x00 }, { 0x0405, 0x40 }, - { 0x0408, 0x00 }, - { 0x0409, 0x00 }, - { 0x040A, 0x00 }, - { 0x040B, 0x00 }, - { 0x040C, 0x10 }, - { 0x040D, 0x70 }, { 0x040E, 0x03 }, { 0x040F, 0x0C }, - { 0x3038, 0x00 }, - { 0x303A, 0x00 }, - { 0x303B, 0x10 }, - { 0x300D, 0x00 }, { 0x034C, 0x04 }, { 0x034D, 0x18 }, { 0x034E, 0x03 }, { 0x034F, 0x0C }, - { 0x0350, 0x01 }, - { 0x0204, 0x00 }, - { 0x0205, 0x00 }, - { 0x020E, 0x01 }, - { 0x020F, 0x00 }, - { 0x0210, 0x01 }, - { 0x0211, 0x00 }, - { 0x0212, 0x01 }, - { 0x0213, 0x00 }, - { 0x0214, 0x01 }, - { 0x0215, 0x00 }, - { 0x7BCD, 0x00 }, - { 0x94DC, 0x20 }, - { 0x94DD, 0x20 }, - { 0x94DE, 0x20 }, - { 0x95DC, 0x20 }, - { 0x95DD, 0x20 }, - { 0x95DE, 0x20 }, - { 0x7FB0, 0x00 }, - { 0x9010, 0x3E }, - { 0x9419, 0x50 }, - { 0x941B, 0x50 }, - { 0x9519, 0x50 }, - { 0x951B, 0x50 }, - { 0x3030, 0x00 }, - { 0x3032, 0x00 }, - { 0x0220, 0x00 }, }; static const char * const imx258_test_pattern_menu[] = { @@ -955,6 +754,13 @@ static int imx258_start_streaming(struct imx258 *imx258) return ret; } + ret = imx258_write_regs(imx258, mode_common_regs, + ARRAY_SIZE(mode_common_regs)); + if (ret) { + dev_err(&client->dev, "%s failed to set common regs\n", __func__); + return ret; + } + /* Apply default values of current mode */ reg_list = &imx258->cur_mode->reg_list; ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs); -- cgit v1.2.3 From d708d58b577c4cb2436e05f955fe2966aa33e851 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 1 May 2024 09:24:25 -0600 Subject: media: i2c: imx258: Add support for 24MHz clock There's no reason why only a clock of 19.2MHz is supported. Indeed this isn't even a frequency listed in the datasheet. Add support for 24MHz as well. The PLL settings result in slightly different link frequencies, so parameterise those. Signed-off-by: Dave Stevenson Signed-off-by: Luis Garcia Reviewed-by: Pavel Machek Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx258.c | 130 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 102 insertions(+), 28 deletions(-) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index 351add1bc5d5..e4b1b3cbbde5 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -76,9 +76,6 @@ #define REG_CONFIG_MIRROR_FLIP 0x03 #define REG_CONFIG_FLIP_TEST_PATTERN 0x02 -/* Input clock frequency in Hz */ -#define IMX258_INPUT_CLOCK_FREQ 19200000 - struct imx258_reg { u16 address; u8 val; @@ -115,7 +112,9 @@ struct imx258_mode { }; /* 4208x3120 needs 1267Mbps/lane, 4 lanes */ -static const struct imx258_reg mipi_data_rate_1267mbps[] = { +static const struct imx258_reg mipi_1267mbps_19_2mhz[] = { + { 0x0136, 0x13 }, + { 0x0137, 0x33 }, { 0x0301, 0x05 }, { 0x0303, 0x02 }, { 0x0305, 0x03 }, @@ -133,7 +132,29 @@ static const struct imx258_reg mipi_data_rate_1267mbps[] = { { 0x0823, 0xCC }, }; -static const struct imx258_reg mipi_data_rate_640mbps[] = { +static const struct imx258_reg mipi_1272mbps_24mhz[] = { + { 0x0136, 0x18 }, + { 0x0137, 0x00 }, + { 0x0301, 0x05 }, + { 0x0303, 0x02 }, + { 0x0305, 0x04 }, + { 0x0306, 0x00 }, + { 0x0307, 0xD4 }, + { 0x0309, 0x0A }, + { 0x030B, 0x01 }, + { 0x030D, 0x02 }, + { 0x030E, 0x00 }, + { 0x030F, 0xD8 }, + { 0x0310, 0x00 }, + { 0x0820, 0x13 }, + { 0x0821, 0x4C }, + { 0x0822, 0xCC }, + { 0x0823, 0xCC }, +}; + +static const struct imx258_reg mipi_640mbps_19_2mhz[] = { + { 0x0136, 0x13 }, + { 0x0137, 0x33 }, { 0x0301, 0x05 }, { 0x0303, 0x02 }, { 0x0305, 0x03 }, @@ -151,9 +172,27 @@ static const struct imx258_reg mipi_data_rate_640mbps[] = { { 0x0823, 0x00 }, }; +static const struct imx258_reg mipi_642mbps_24mhz[] = { + { 0x0136, 0x18 }, + { 0x0137, 0x00 }, + { 0x0301, 0x05 }, + { 0x0303, 0x02 }, + { 0x0305, 0x04 }, + { 0x0306, 0x00 }, + { 0x0307, 0x6B }, + { 0x0309, 0x0A }, + { 0x030B, 0x01 }, + { 0x030D, 0x02 }, + { 0x030E, 0x00 }, + { 0x030F, 0xD8 }, + { 0x0310, 0x00 }, + { 0x0820, 0x0A }, + { 0x0821, 0x00 }, + { 0x0822, 0x00 }, + { 0x0823, 0x00 }, +}; + static const struct imx258_reg mode_common_regs[] = { - { 0x0136, 0x13 }, - { 0x0137, 0x33 }, { 0x3051, 0x00 }, { 0x3052, 0x00 }, { 0x4E21, 0x14 }, @@ -313,10 +352,6 @@ static const char * const imx258_supply_name[] = { #define IMX258_NUM_SUPPLIES ARRAY_SIZE(imx258_supply_name) -/* Configurations for supported link frequencies */ -#define IMX258_LINK_FREQ_634MHZ 633600000ULL -#define IMX258_LINK_FREQ_320MHZ 320000000ULL - enum { IMX258_LINK_FREQ_1267MBPS, IMX258_LINK_FREQ_640MBPS, @@ -335,25 +370,48 @@ static u64 link_freq_to_pixel_rate(u64 f) } /* Menu items for LINK_FREQ V4L2 control */ -static const s64 link_freq_menu_items[] = { - IMX258_LINK_FREQ_634MHZ, - IMX258_LINK_FREQ_320MHZ, +/* Configurations for supported link frequencies */ +static const s64 link_freq_menu_items_19_2[] = { + 633600000ULL, + 320000000ULL, +}; + +static const s64 link_freq_menu_items_24[] = { + 636000000ULL, + 321000000ULL, }; /* Link frequency configs */ -static const struct imx258_link_freq_config link_freq_configs[] = { +static const struct imx258_link_freq_config link_freq_configs_19_2[] = { [IMX258_LINK_FREQ_1267MBPS] = { .pixels_per_line = IMX258_PPL_DEFAULT, .reg_list = { - .num_of_regs = ARRAY_SIZE(mipi_data_rate_1267mbps), - .regs = mipi_data_rate_1267mbps, + .num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz), + .regs = mipi_1267mbps_19_2mhz, } }, [IMX258_LINK_FREQ_640MBPS] = { .pixels_per_line = IMX258_PPL_DEFAULT, .reg_list = { - .num_of_regs = ARRAY_SIZE(mipi_data_rate_640mbps), - .regs = mipi_data_rate_640mbps, + .num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz), + .regs = mipi_640mbps_19_2mhz, + } + }, +}; + +static const struct imx258_link_freq_config link_freq_configs_24[] = { + [IMX258_LINK_FREQ_1267MBPS] = { + .pixels_per_line = IMX258_PPL_DEFAULT, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz), + .regs = mipi_1272mbps_24mhz, + } + }, + [IMX258_LINK_FREQ_640MBPS] = { + .pixels_per_line = IMX258_PPL_DEFAULT, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz), + .regs = mipi_642mbps_24mhz, } }, }; @@ -410,6 +468,9 @@ struct imx258 { /* Current mode */ const struct imx258_mode *cur_mode; + const struct imx258_link_freq_config *link_freq_configs; + const s64 *link_freq_menu_items; + /* * Mutex for serialized access: * Protect sensor module set pad format and start/stop streaming safely. @@ -713,7 +774,7 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd, imx258->cur_mode = mode; __v4l2_ctrl_s_ctrl(imx258->link_freq, mode->link_freq_index); - link_freq = link_freq_menu_items[mode->link_freq_index]; + link_freq = imx258->link_freq_menu_items[mode->link_freq_index]; pixel_rate = link_freq_to_pixel_rate(link_freq); __v4l2_ctrl_s_ctrl_int64(imx258->pixel_rate, pixel_rate); /* Update limits and set FPS to default */ @@ -727,7 +788,7 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd, vblank_def); __v4l2_ctrl_s_ctrl(imx258->vblank, vblank_def); h_blank = - link_freq_configs[mode->link_freq_index].pixels_per_line + imx258->link_freq_configs[mode->link_freq_index].pixels_per_line - imx258->cur_mode->width; __v4l2_ctrl_modify_range(imx258->hblank, h_blank, h_blank, 1, h_blank); @@ -747,7 +808,7 @@ static int imx258_start_streaming(struct imx258 *imx258) /* Setup PLL */ link_freq_index = imx258->cur_mode->link_freq_index; - reg_list = &link_freq_configs[link_freq_index].reg_list; + reg_list = &imx258->link_freq_configs[link_freq_index].reg_list; ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs); if (ret) { dev_err(&client->dev, "%s failed to set plls\n", __func__); @@ -946,9 +1007,9 @@ static int imx258_init_controls(struct imx258 *imx258) imx258->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_LINK_FREQ, - ARRAY_SIZE(link_freq_menu_items) - 1, + ARRAY_SIZE(link_freq_menu_items_19_2) - 1, 0, - link_freq_menu_items); + imx258->link_freq_menu_items); if (imx258->link_freq) imx258->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; @@ -964,8 +1025,10 @@ static int imx258_init_controls(struct imx258 *imx258) if (vflip) vflip->flags |= V4L2_CTRL_FLAG_READ_ONLY; - pixel_rate_max = link_freq_to_pixel_rate(link_freq_menu_items[0]); - pixel_rate_min = link_freq_to_pixel_rate(link_freq_menu_items[1]); + pixel_rate_max = + link_freq_to_pixel_rate(imx258->link_freq_menu_items[0]); + pixel_rate_min = + link_freq_to_pixel_rate(imx258->link_freq_menu_items[1]); /* By default, PIXEL_RATE is read only */ imx258->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_PIXEL_RATE, @@ -1086,8 +1149,19 @@ static int imx258_probe(struct i2c_client *client) } else { val = clk_get_rate(imx258->clk); } - if (val != IMX258_INPUT_CLOCK_FREQ) { - dev_err(&client->dev, "input clock frequency not supported\n"); + + switch (val) { + case 19200000: + imx258->link_freq_configs = link_freq_configs_19_2; + imx258->link_freq_menu_items = link_freq_menu_items_19_2; + break; + case 24000000: + imx258->link_freq_configs = link_freq_configs_24; + imx258->link_freq_menu_items = link_freq_menu_items_24; + break; + default: + dev_err(&client->dev, "input clock frequency of %u not supported\n", + val); return -EINVAL; } -- cgit v1.2.3 From c699953f61d42b121d755872048178a0b074efae Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 1 May 2024 09:24:26 -0600 Subject: media: i2c: imx258: Add support for running on 2 CSI data lanes Extends the driver to also support 2 data lanes. Frame rates are obviously more restricted on 2 lanes, but some hardware simply hasn't wired more up. Signed-off-by: Dave Stevenson Signed-off-by: Luis Garcia Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx258.c | 214 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 190 insertions(+), 24 deletions(-) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index e4b1b3cbbde5..8f792f0e0738 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -86,12 +86,18 @@ struct imx258_reg_list { const struct imx258_reg *regs; }; +enum { + IMX258_2_LANE_MODE, + IMX258_4_LANE_MODE, + IMX258_LANE_CONFIGS, +}; + /* Link frequency config */ struct imx258_link_freq_config { u32 pixels_per_line; /* PLL registers for this link frequency */ - struct imx258_reg_list reg_list; + struct imx258_reg_list reg_list[IMX258_LANE_CONFIGS]; }; /* Mode : resolution and related config&values */ @@ -111,8 +117,34 @@ struct imx258_mode { struct imx258_reg_list reg_list; }; -/* 4208x3120 needs 1267Mbps/lane, 4 lanes */ -static const struct imx258_reg mipi_1267mbps_19_2mhz[] = { +/* + * 4208x3120 @ 30 fps needs 1267Mbps/lane, 4 lanes. + * To avoid further computation of clock settings, adopt the same per + * lane data rate when using 2 lanes, thus allowing a maximum of 15fps. + */ +static const struct imx258_reg mipi_1267mbps_19_2mhz_2l[] = { + { 0x0136, 0x13 }, + { 0x0137, 0x33 }, + { 0x0301, 0x0A }, + { 0x0303, 0x02 }, + { 0x0305, 0x03 }, + { 0x0306, 0x00 }, + { 0x0307, 0xC6 }, + { 0x0309, 0x0A }, + { 0x030B, 0x01 }, + { 0x030D, 0x02 }, + { 0x030E, 0x00 }, + { 0x030F, 0xD8 }, + { 0x0310, 0x00 }, + + { 0x0114, 0x01 }, + { 0x0820, 0x09 }, + { 0x0821, 0xa6 }, + { 0x0822, 0x66 }, + { 0x0823, 0x66 }, +}; + +static const struct imx258_reg mipi_1267mbps_19_2mhz_4l[] = { { 0x0136, 0x13 }, { 0x0137, 0x33 }, { 0x0301, 0x05 }, @@ -126,16 +158,18 @@ static const struct imx258_reg mipi_1267mbps_19_2mhz[] = { { 0x030E, 0x00 }, { 0x030F, 0xD8 }, { 0x0310, 0x00 }, + + { 0x0114, 0x03 }, { 0x0820, 0x13 }, { 0x0821, 0x4C }, { 0x0822, 0xCC }, { 0x0823, 0xCC }, }; -static const struct imx258_reg mipi_1272mbps_24mhz[] = { +static const struct imx258_reg mipi_1272mbps_24mhz_2l[] = { { 0x0136, 0x18 }, { 0x0137, 0x00 }, - { 0x0301, 0x05 }, + { 0x0301, 0x0a }, { 0x0303, 0x02 }, { 0x0305, 0x04 }, { 0x0306, 0x00 }, @@ -146,13 +180,59 @@ static const struct imx258_reg mipi_1272mbps_24mhz[] = { { 0x030E, 0x00 }, { 0x030F, 0xD8 }, { 0x0310, 0x00 }, + + { 0x0114, 0x01 }, { 0x0820, 0x13 }, { 0x0821, 0x4C }, { 0x0822, 0xCC }, { 0x0823, 0xCC }, }; -static const struct imx258_reg mipi_640mbps_19_2mhz[] = { +static const struct imx258_reg mipi_1272mbps_24mhz_4l[] = { + { 0x0136, 0x18 }, + { 0x0137, 0x00 }, + { 0x0301, 0x05 }, + { 0x0303, 0x02 }, + { 0x0305, 0x04 }, + { 0x0306, 0x00 }, + { 0x0307, 0xD4 }, + { 0x0309, 0x0A }, + { 0x030B, 0x01 }, + { 0x030D, 0x02 }, + { 0x030E, 0x00 }, + { 0x030F, 0xD8 }, + { 0x0310, 0x00 }, + + { 0x0114, 0x03 }, + { 0x0820, 0x13 }, + { 0x0821, 0xE0 }, + { 0x0822, 0x00 }, + { 0x0823, 0x00 }, +}; + +static const struct imx258_reg mipi_640mbps_19_2mhz_2l[] = { + { 0x0136, 0x13 }, + { 0x0137, 0x33 }, + { 0x0301, 0x05 }, + { 0x0303, 0x02 }, + { 0x0305, 0x03 }, + { 0x0306, 0x00 }, + { 0x0307, 0x64 }, + { 0x0309, 0x0A }, + { 0x030B, 0x01 }, + { 0x030D, 0x02 }, + { 0x030E, 0x00 }, + { 0x030F, 0xD8 }, + { 0x0310, 0x00 }, + + { 0x0114, 0x01 }, + { 0x0820, 0x05 }, + { 0x0821, 0x00 }, + { 0x0822, 0x00 }, + { 0x0823, 0x00 }, +}; + +static const struct imx258_reg mipi_640mbps_19_2mhz_4l[] = { { 0x0136, 0x13 }, { 0x0137, 0x33 }, { 0x0301, 0x05 }, @@ -166,13 +246,37 @@ static const struct imx258_reg mipi_640mbps_19_2mhz[] = { { 0x030E, 0x00 }, { 0x030F, 0xD8 }, { 0x0310, 0x00 }, + + { 0x0114, 0x03 }, + { 0x0820, 0x0A }, + { 0x0821, 0x00 }, + { 0x0822, 0x00 }, + { 0x0823, 0x00 }, +}; + +static const struct imx258_reg mipi_642mbps_24mhz_2l[] = { + { 0x0136, 0x18 }, + { 0x0137, 0x00 }, + { 0x0301, 0x0A }, + { 0x0303, 0x02 }, + { 0x0305, 0x04 }, + { 0x0306, 0x00 }, + { 0x0307, 0x6B }, + { 0x0309, 0x0A }, + { 0x030B, 0x01 }, + { 0x030D, 0x02 }, + { 0x030E, 0x00 }, + { 0x030F, 0xD8 }, + { 0x0310, 0x00 }, + + { 0x0114, 0x01 }, { 0x0820, 0x0A }, { 0x0821, 0x00 }, { 0x0822, 0x00 }, { 0x0823, 0x00 }, }; -static const struct imx258_reg mipi_642mbps_24mhz[] = { +static const struct imx258_reg mipi_642mbps_24mhz_4l[] = { { 0x0136, 0x18 }, { 0x0137, 0x00 }, { 0x0301, 0x05 }, @@ -186,6 +290,8 @@ static const struct imx258_reg mipi_642mbps_24mhz[] = { { 0x030E, 0x00 }, { 0x030F, 0xD8 }, { 0x0310, 0x00 }, + + { 0x0114, 0x03 }, { 0x0820, 0x0A }, { 0x0821, 0x00 }, { 0x0822, 0x00 }, @@ -240,7 +346,6 @@ static const struct imx258_reg mode_common_regs[] = { { 0x5F05, 0xED }, { 0x0112, 0x0A }, { 0x0113, 0x0A }, - { 0x0114, 0x03 }, { 0x0342, 0x14 }, { 0x0343, 0xE8 }, { 0x0344, 0x00 }, @@ -359,11 +464,13 @@ enum { /* * pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample - * data rate => double data rate; number of lanes => 4; bits per pixel => 10 + * data rate => double data rate; + * number of lanes => (configurable 2 or 4); + * bits per pixel => 10 */ -static u64 link_freq_to_pixel_rate(u64 f) +static u64 link_freq_to_pixel_rate(u64 f, unsigned int nlanes) { - f *= 2 * 4; + f *= 2 * nlanes; do_div(f, 10); return f; @@ -386,15 +493,27 @@ static const struct imx258_link_freq_config link_freq_configs_19_2[] = { [IMX258_LINK_FREQ_1267MBPS] = { .pixels_per_line = IMX258_PPL_DEFAULT, .reg_list = { - .num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz), - .regs = mipi_1267mbps_19_2mhz, + [IMX258_2_LANE_MODE] = { + .num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz_2l), + .regs = mipi_1267mbps_19_2mhz_2l, + }, + [IMX258_4_LANE_MODE] = { + .num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz_4l), + .regs = mipi_1267mbps_19_2mhz_4l, + }, } }, [IMX258_LINK_FREQ_640MBPS] = { .pixels_per_line = IMX258_PPL_DEFAULT, .reg_list = { - .num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz), - .regs = mipi_640mbps_19_2mhz, + [IMX258_2_LANE_MODE] = { + .num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz_2l), + .regs = mipi_640mbps_19_2mhz_2l, + }, + [IMX258_4_LANE_MODE] = { + .num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz_4l), + .regs = mipi_640mbps_19_2mhz_4l, + }, } }, }; @@ -403,15 +522,27 @@ static const struct imx258_link_freq_config link_freq_configs_24[] = { [IMX258_LINK_FREQ_1267MBPS] = { .pixels_per_line = IMX258_PPL_DEFAULT, .reg_list = { - .num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz), - .regs = mipi_1272mbps_24mhz, + [IMX258_2_LANE_MODE] = { + .num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz_2l), + .regs = mipi_1272mbps_24mhz_2l, + }, + [IMX258_4_LANE_MODE] = { + .num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz_4l), + .regs = mipi_1272mbps_24mhz_4l, + }, } }, [IMX258_LINK_FREQ_640MBPS] = { .pixels_per_line = IMX258_PPL_DEFAULT, .reg_list = { - .num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz), - .regs = mipi_642mbps_24mhz, + [IMX258_2_LANE_MODE] = { + .num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz_2l), + .regs = mipi_642mbps_24mhz_2l, + }, + [IMX258_4_LANE_MODE] = { + .num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz_4l), + .regs = mipi_642mbps_24mhz_4l, + }, } }, }; @@ -470,6 +601,7 @@ struct imx258 { const struct imx258_link_freq_config *link_freq_configs; const s64 *link_freq_menu_items; + unsigned int nlanes; /* * Mutex for serialized access: @@ -775,7 +907,7 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd, __v4l2_ctrl_s_ctrl(imx258->link_freq, mode->link_freq_index); link_freq = imx258->link_freq_menu_items[mode->link_freq_index]; - pixel_rate = link_freq_to_pixel_rate(link_freq); + pixel_rate = link_freq_to_pixel_rate(link_freq, imx258->nlanes); __v4l2_ctrl_s_ctrl_int64(imx258->pixel_rate, pixel_rate); /* Update limits and set FPS to default */ vblank_def = imx258->cur_mode->vts_def - @@ -804,11 +936,13 @@ static int imx258_start_streaming(struct imx258 *imx258) { struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); const struct imx258_reg_list *reg_list; + const struct imx258_link_freq_config *link_freq_cfg; int ret, link_freq_index; /* Setup PLL */ link_freq_index = imx258->cur_mode->link_freq_index; - reg_list = &imx258->link_freq_configs[link_freq_index].reg_list; + link_freq_cfg = &imx258->link_freq_configs[link_freq_index]; + reg_list = &link_freq_cfg->reg_list[imx258->nlanes == 2 ? 0 : 1]; ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs); if (ret) { dev_err(&client->dev, "%s failed to set plls\n", __func__); @@ -1026,9 +1160,11 @@ static int imx258_init_controls(struct imx258 *imx258) vflip->flags |= V4L2_CTRL_FLAG_READ_ONLY; pixel_rate_max = - link_freq_to_pixel_rate(imx258->link_freq_menu_items[0]); + link_freq_to_pixel_rate(imx258->link_freq_menu_items[0], + imx258->nlanes); pixel_rate_min = - link_freq_to_pixel_rate(imx258->link_freq_menu_items[1]); + link_freq_to_pixel_rate(imx258->link_freq_menu_items[1], + imx258->nlanes); /* By default, PIXEL_RATE is read only */ imx258->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_PIXEL_RATE, @@ -1125,6 +1261,10 @@ static int imx258_get_regulators(struct imx258 *imx258, static int imx258_probe(struct i2c_client *client) { struct imx258 *imx258; + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint ep = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; int ret; u32 val = 0; @@ -1165,13 +1305,35 @@ static int imx258_probe(struct i2c_client *client) return -EINVAL; } + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); + if (!endpoint) { + dev_err(&client->dev, "Endpoint node not found\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep); + fwnode_handle_put(endpoint); + if (ret) { + dev_err(&client->dev, "Parsing endpoint node failed\n"); + return ret; + } + + /* Get number of data lanes */ + imx258->nlanes = ep.bus.mipi_csi2.num_data_lanes; + if (imx258->nlanes != 2 && imx258->nlanes != 4) { + dev_err(&client->dev, "Invalid data lanes: %u\n", + imx258->nlanes); + ret = -EINVAL; + goto error_endpoint_free; + } + /* Initialize subdev */ v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops); /* Will be powered off via pm_runtime_idle */ ret = imx258_power_on(&client->dev); if (ret) - return ret; + goto error_endpoint_free; /* Check module identity */ ret = imx258_identify_module(imx258); @@ -1204,6 +1366,7 @@ static int imx258_probe(struct i2c_client *client) pm_runtime_set_active(&client->dev); pm_runtime_enable(&client->dev); pm_runtime_idle(&client->dev); + v4l2_fwnode_endpoint_free(&ep); return 0; @@ -1216,6 +1379,9 @@ error_handler_free: error_identify: imx258_power_off(&client->dev); +error_endpoint_free: + v4l2_fwnode_endpoint_free(&ep); + return ret; } -- cgit v1.2.3 From 8eaf1994a40fdbd9e65c9b9f1e075fb47da6bc89 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 1 May 2024 09:24:27 -0600 Subject: media: i2c: imx258: Follow normal V4L2 behaviours for clipping exposure V4L2 sensor drivers are expected to clip the supported exposure range based on the VBLANK configured. IMX258 wasn't doing that as register 0x350 (FRM_LENGTH_CTL) switches it to a mode where frame length tracks coarse exposure time. Disable this mode and clip the range for V4L2_CID_EXPOSURE appropriately based on V4L2_CID_VBLANK. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi Signed-off-by: Luis Garcia Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx258.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index 8f792f0e0738..ebc404b548b3 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -37,10 +37,11 @@ /* Exposure control */ #define IMX258_REG_EXPOSURE 0x0202 +#define IMX258_EXPOSURE_OFFSET 10 #define IMX258_EXPOSURE_MIN 4 #define IMX258_EXPOSURE_STEP 1 #define IMX258_EXPOSURE_DEFAULT 0x640 -#define IMX258_EXPOSURE_MAX 65535 +#define IMX258_EXPOSURE_MAX (IMX258_VTS_MAX - IMX258_EXPOSURE_OFFSET) /* Analog gain control */ #define IMX258_REG_ANALOG_GAIN 0x0204 @@ -371,7 +372,7 @@ static const struct imx258_reg mode_common_regs[] = { { 0x303A, 0x00 }, { 0x303B, 0x10 }, { 0x300D, 0x00 }, - { 0x0350, 0x01 }, + { 0x0350, 0x00 }, { 0x0204, 0x00 }, { 0x0205, 0x00 }, { 0x020E, 0x01 }, @@ -734,6 +735,19 @@ static int imx258_update_digital_gain(struct imx258 *imx258, u32 len, u32 val) return 0; } +static void imx258_adjust_exposure_range(struct imx258 *imx258) +{ + int exposure_max, exposure_def; + + /* Honour the VBLANK limits when setting exposure. */ + exposure_max = imx258->cur_mode->height + imx258->vblank->val - + IMX258_EXPOSURE_OFFSET; + exposure_def = min(exposure_max, imx258->exposure->val); + __v4l2_ctrl_modify_range(imx258->exposure, imx258->exposure->minimum, + exposure_max, imx258->exposure->step, + exposure_def); +} + static int imx258_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx258 *imx258 = @@ -741,6 +755,13 @@ static int imx258_set_ctrl(struct v4l2_ctrl *ctrl) struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); int ret = 0; + /* + * The VBLANK control may change the limits of usable exposure, so check + * and adjust if necessary. + */ + if (ctrl->id == V4L2_CID_VBLANK) + imx258_adjust_exposure_range(imx258); + /* * Applying V4L2 control value only happens * when power is up for streaming -- cgit v1.2.3 From 29e7d3fbc8b946a9c80579e5d16dc89012c80556 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 1 May 2024 09:24:28 -0600 Subject: media: i2c: imx258: Add get_selection for pixel array information Libcamera requires the cropping information for each mode, so add this information to the driver. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi Signed-off-by: Luis Garcia Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx258.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index ebc404b548b3..59a78a4cfe44 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -77,6 +77,14 @@ #define REG_CONFIG_MIRROR_FLIP 0x03 #define REG_CONFIG_FLIP_TEST_PATTERN 0x02 +/* IMX258 native and active pixel array size. */ +#define IMX258_NATIVE_WIDTH 4224U +#define IMX258_NATIVE_HEIGHT 3192U +#define IMX258_PIXEL_ARRAY_LEFT 8U +#define IMX258_PIXEL_ARRAY_TOP 16U +#define IMX258_PIXEL_ARRAY_WIDTH 4208U +#define IMX258_PIXEL_ARRAY_HEIGHT 3120U + struct imx258_reg { u16 address; u8 val; @@ -116,6 +124,9 @@ struct imx258_mode { u32 link_freq_index; /* Default register values */ struct imx258_reg_list reg_list; + + /* Analog crop rectangle */ + struct v4l2_rect crop; }; /* @@ -560,6 +571,12 @@ static const struct imx258_mode supported_modes[] = { .regs = mode_4208x3120_regs, }, .link_freq_index = IMX258_LINK_FREQ_1267MBPS, + .crop = { + .left = IMX258_PIXEL_ARRAY_LEFT, + .top = IMX258_PIXEL_ARRAY_TOP, + .width = 4208, + .height = 3120, + }, }, { .width = 2104, @@ -571,6 +588,12 @@ static const struct imx258_mode supported_modes[] = { .regs = mode_2104_1560_regs, }, .link_freq_index = IMX258_LINK_FREQ_640MBPS, + .crop = { + .left = IMX258_PIXEL_ARRAY_LEFT, + .top = IMX258_PIXEL_ARRAY_TOP, + .width = 4208, + .height = 3120, + }, }, { .width = 1048, @@ -582,6 +605,12 @@ static const struct imx258_mode supported_modes[] = { .regs = mode_1048_780_regs, }, .link_freq_index = IMX258_LINK_FREQ_640MBPS, + .crop = { + .left = IMX258_PIXEL_ARRAY_LEFT, + .top = IMX258_PIXEL_ARRAY_TOP, + .width = 4208, + .height = 3120, + }, }, }; @@ -698,6 +727,7 @@ static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_state_get_format(fh->state, 0); + struct v4l2_rect *try_crop; /* Initialize try_fmt */ try_fmt->width = supported_modes[0].width; @@ -705,6 +735,13 @@ static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10; try_fmt->field = V4L2_FIELD_NONE; + /* Initialize try_crop */ + try_crop = v4l2_subdev_state_get_crop(fh->state, 0); + try_crop->left = IMX258_PIXEL_ARRAY_LEFT; + try_crop->top = IMX258_PIXEL_ARRAY_TOP; + try_crop->width = IMX258_PIXEL_ARRAY_WIDTH; + try_crop->height = IMX258_PIXEL_ARRAY_HEIGHT; + return 0; } @@ -952,6 +989,58 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd, return 0; } +static const struct v4l2_rect * +__imx258_get_pad_crop(struct imx258 *imx258, + struct v4l2_subdev_state *sd_state, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_state_get_crop(sd_state, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &imx258->cur_mode->crop; + } + + return NULL; +} + +static int imx258_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + struct imx258 *imx258 = to_imx258(sd); + + mutex_lock(&imx258->mutex); + sel->r = *__imx258_get_pad_crop(imx258, sd_state, sel->pad, + sel->which); + mutex_unlock(&imx258->mutex); + + return 0; + } + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = IMX258_NATIVE_WIDTH; + sel->r.height = IMX258_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.left = IMX258_PIXEL_ARRAY_LEFT; + sel->r.top = IMX258_PIXEL_ARRAY_TOP; + sel->r.width = IMX258_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX258_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; +} + /* Start streaming */ static int imx258_start_streaming(struct imx258 *imx258) { @@ -1128,6 +1217,7 @@ static const struct v4l2_subdev_pad_ops imx258_pad_ops = { .get_fmt = imx258_get_pad_format, .set_fmt = imx258_set_pad_format, .enum_frame_size = imx258_enum_frame_size, + .get_selection = imx258_get_selection, }; static const struct v4l2_subdev_ops imx258_subdev_ops = { -- cgit v1.2.3 From a7db195d67354899efb66f44df39cec66f69fc9c Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 1 May 2024 09:24:29 -0600 Subject: media: i2c: imx258: Allow configuration of clock lane behaviour The sensor supports the clock lane either remaining in HS mode during frame blanking, or dropping to LP11. Add configuration of the mode via V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi Signed-off-by: Luis Garcia Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx258.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index 59a78a4cfe44..2429eb7b55c6 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -72,6 +72,8 @@ /* Test Pattern Control */ #define IMX258_REG_TEST_PATTERN 0x0600 +#define IMX258_CLK_BLANK_STOP 0x4040 + /* Orientation */ #define REG_MIRROR_FLIP_CONTROL 0x0101 #define REG_CONFIG_MIRROR_FLIP 0x03 @@ -632,6 +634,7 @@ struct imx258 { const struct imx258_link_freq_config *link_freq_configs; const s64 *link_freq_menu_items; unsigned int nlanes; + unsigned int csi2_flags; /* * Mutex for serialized access: @@ -1066,6 +1069,14 @@ static int imx258_start_streaming(struct imx258 *imx258) return ret; } + ret = imx258_write_reg(imx258, IMX258_CLK_BLANK_STOP, + IMX258_REG_VALUE_08BIT, + !!(imx258->csi2_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)); + if (ret) { + dev_err(&client->dev, "%s failed to set clock lane mode\n", __func__); + return ret; + } + /* Apply default values of current mode */ reg_list = &imx258->cur_mode->reg_list; ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs); @@ -1438,6 +1449,8 @@ static int imx258_probe(struct i2c_client *client) goto error_endpoint_free; } + imx258->csi2_flags = ep.bus.mipi_csi2.flags; + /* Initialize subdev */ v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops); -- cgit v1.2.3 From c546429a60c8802723f36171e13cb53ca14ebae0 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 1 May 2024 09:24:30 -0600 Subject: media: i2c: imx258: Correct max FRM_LENGTH_LINES value The data sheet states that the maximum value for registers 0x0340/0x0341 FRM_LENGTH_LINES is 65525(decimal), not the 0xFFFF defined in this driver. Correct this limit. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi Signed-off-by: Luis Garcia Reviewed-by: Pavel Machek Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx258.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index 2429eb7b55c6..c1a2c2406aaa 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -28,7 +28,7 @@ #define IMX258_VTS_30FPS 0x0c50 #define IMX258_VTS_30FPS_2K 0x0638 #define IMX258_VTS_30FPS_VGA 0x034c -#define IMX258_VTS_MAX 0xffff +#define IMX258_VTS_MAX 65525 #define IMX258_REG_VTS 0x0340 -- cgit v1.2.3 From 185025977c3e7ba37d26af24c3648f3b52f62580 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 1 May 2024 09:24:31 -0600 Subject: media: i2c: imx258: Issue reset before starting streaming Whilst not documented, register 0x0103 bit 0 is the soft reset for the sensor, so send it before trying to configure the sensor. Signed-off-by: Dave Stevenson Signed-off-by: Luis Garcia Reviewed-by: Pavel Machek Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx258.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index c1a2c2406aaa..9c83ba1232fa 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -20,6 +20,8 @@ #define IMX258_MODE_STANDBY 0x00 #define IMX258_MODE_STREAMING 0x01 +#define IMX258_REG_RESET 0x0103 + /* Chip ID */ #define IMX258_REG_CHIP_ID 0x0016 #define IMX258_CHIP_ID 0x0258 @@ -1052,6 +1054,16 @@ static int imx258_start_streaming(struct imx258 *imx258) const struct imx258_link_freq_config *link_freq_cfg; int ret, link_freq_index; + ret = imx258_write_reg(imx258, IMX258_REG_RESET, IMX258_REG_VALUE_08BIT, + 0x01); + if (ret) { + dev_err(&client->dev, "%s failed to reset sensor\n", __func__); + return ret; + } + + /* 12ms is required from poweron to standby */ + fsleep(12000); + /* Setup PLL */ link_freq_index = imx258->cur_mode->link_freq_index; link_freq_cfg = &imx258->link_freq_configs[link_freq_index]; -- cgit v1.2.3 From b2482f8f722a25bba531ff444438f4149e22974e Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 1 May 2024 09:24:32 -0600 Subject: media: i2c: imx258: Set pixel_rate range to the same as the value With a read only control there is limited point in advertising a minimum and maximum for the control, so change to set the value, min, and max all to the selected pixel rate. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi Signed-off-by: Luis Garcia Reviewed-by: Pavel Machek Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx258.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index 9c83ba1232fa..4eb5f2eba491 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -971,7 +971,8 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd, link_freq = imx258->link_freq_menu_items[mode->link_freq_index]; pixel_rate = link_freq_to_pixel_rate(link_freq, imx258->nlanes); - __v4l2_ctrl_s_ctrl_int64(imx258->pixel_rate, pixel_rate); + __v4l2_ctrl_modify_range(imx258->pixel_rate, pixel_rate, + pixel_rate, 1, pixel_rate); /* Update limits and set FPS to default */ vblank_def = imx258->cur_mode->vts_def - imx258->cur_mode->height; @@ -1261,8 +1262,7 @@ static int imx258_init_controls(struct imx258 *imx258) struct v4l2_ctrl *vflip, *hflip; s64 vblank_def; s64 vblank_min; - s64 pixel_rate_min; - s64 pixel_rate_max; + s64 pixel_rate; int ret; ctrl_hdlr = &imx258->ctrl_handler; @@ -1293,18 +1293,13 @@ static int imx258_init_controls(struct imx258 *imx258) if (vflip) vflip->flags |= V4L2_CTRL_FLAG_READ_ONLY; - pixel_rate_max = - link_freq_to_pixel_rate(imx258->link_freq_menu_items[0], - imx258->nlanes); - pixel_rate_min = - link_freq_to_pixel_rate(imx258->link_freq_menu_items[1], - imx258->nlanes); + pixel_rate = link_freq_to_pixel_rate(imx258->link_freq_menu_items[0], + imx258->nlanes); /* By default, PIXEL_RATE is read only */ imx258->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_PIXEL_RATE, - pixel_rate_min, pixel_rate_max, - 1, pixel_rate_max); - + pixel_rate, pixel_rate, + 1, pixel_rate); vblank_def = imx258->cur_mode->vts_def - imx258->cur_mode->height; vblank_min = imx258->cur_mode->vts_min - imx258->cur_mode->height; -- cgit v1.2.3 From 01e0e4c34d08e340806826bf847b114b839d572f Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 1 May 2024 09:24:33 -0600 Subject: media: i2c: imx258: Support faster pixel rate on binned modes With the binned modes, there is little point in faithfully reproducing the horizontal line length of 5352 pixels on the CSI2 bus, and the FIFO between the pixel array and MIPI serialiser allows us to remove that dependency. Allow the pixel array to run with the normal settings, with the MIPI serialiser at half the rate. This requires some additional information for the link frequency to pixel rate function that needs to be added to the configuration tables. Signed-off-by: Dave Stevenson Signed-off-by: Luis Garcia Reviewed-by: Pavel Machek Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx258.c | 109 +++++++++++++++++++++++++++++---------------- 1 file changed, 71 insertions(+), 38 deletions(-) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index 4eb5f2eba491..32267d36b8f3 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -99,6 +99,11 @@ struct imx258_reg_list { const struct imx258_reg *regs; }; +struct imx258_link_cfg { + unsigned int lf_to_pix_rate_factor; + struct imx258_reg_list reg_list; +}; + enum { IMX258_2_LANE_MODE, IMX258_4_LANE_MODE, @@ -109,8 +114,8 @@ enum { struct imx258_link_freq_config { u32 pixels_per_line; - /* PLL registers for this link frequency */ - struct imx258_reg_list reg_list[IMX258_LANE_CONFIGS]; + /* Configuration for this link frequency / num lanes selection */ + struct imx258_link_cfg link_cfg[IMX258_LANE_CONFIGS]; }; /* Mode : resolution and related config&values */ @@ -273,7 +278,7 @@ static const struct imx258_reg mipi_640mbps_19_2mhz_4l[] = { static const struct imx258_reg mipi_642mbps_24mhz_2l[] = { { 0x0136, 0x18 }, { 0x0137, 0x00 }, - { 0x0301, 0x0A }, + { 0x0301, 0x05 }, { 0x0303, 0x02 }, { 0x0305, 0x04 }, { 0x0306, 0x00 }, @@ -479,14 +484,22 @@ enum { }; /* - * pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample - * data rate => double data rate; - * number of lanes => (configurable 2 or 4); - * bits per pixel => 10 + * Pixel rate does not necessarily relate to link frequency on this sensor as + * there is a FIFO between the pixel array pipeline and the MIPI serializer. + * The recommendation from Sony is that the pixel array is always run with a + * line length of 5352 pixels, which means that there is a large amount of + * blanking time for the 1048x780 mode. There is no need to replicate this + * blanking on the CSI2 bus, and the configuration of register 0x0301 allows the + * divider to be altered. + * + * The actual factor between link frequency and pixel rate is in the + * imx258_link_cfg, so use this to convert between the two. + * bits per pixel being 10, and D-PHY being DDR is assumed by this function, so + * the value is only the combination of number of lanes and pixel clock divider. */ -static u64 link_freq_to_pixel_rate(u64 f, unsigned int nlanes) +static u64 link_freq_to_pixel_rate(u64 f, const struct imx258_link_cfg *link_cfg) { - f *= 2 * nlanes; + f *= 2 * link_cfg->lf_to_pix_rate_factor; do_div(f, 10); return f; @@ -504,31 +517,33 @@ static const s64 link_freq_menu_items_24[] = { 321000000ULL, }; +#define REGS(_list) { .num_of_regs = ARRAY_SIZE(_list), .regs = _list, } + /* Link frequency configs */ static const struct imx258_link_freq_config link_freq_configs_19_2[] = { [IMX258_LINK_FREQ_1267MBPS] = { .pixels_per_line = IMX258_PPL_DEFAULT, - .reg_list = { + .link_cfg = { [IMX258_2_LANE_MODE] = { - .num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz_2l), - .regs = mipi_1267mbps_19_2mhz_2l, + .lf_to_pix_rate_factor = 2 * 2, + .reg_list = REGS(mipi_1267mbps_19_2mhz_2l), }, [IMX258_4_LANE_MODE] = { - .num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz_4l), - .regs = mipi_1267mbps_19_2mhz_4l, + .lf_to_pix_rate_factor = 4, + .reg_list = REGS(mipi_1267mbps_19_2mhz_4l), }, } }, [IMX258_LINK_FREQ_640MBPS] = { .pixels_per_line = IMX258_PPL_DEFAULT, - .reg_list = { + .link_cfg = { [IMX258_2_LANE_MODE] = { - .num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz_2l), - .regs = mipi_640mbps_19_2mhz_2l, + .lf_to_pix_rate_factor = 2, + .reg_list = REGS(mipi_640mbps_19_2mhz_2l), }, [IMX258_4_LANE_MODE] = { - .num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz_4l), - .regs = mipi_640mbps_19_2mhz_4l, + .lf_to_pix_rate_factor = 4, + .reg_list = REGS(mipi_640mbps_19_2mhz_4l), }, } }, @@ -537,27 +552,27 @@ static const struct imx258_link_freq_config link_freq_configs_19_2[] = { static const struct imx258_link_freq_config link_freq_configs_24[] = { [IMX258_LINK_FREQ_1267MBPS] = { .pixels_per_line = IMX258_PPL_DEFAULT, - .reg_list = { + .link_cfg = { [IMX258_2_LANE_MODE] = { - .num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz_2l), - .regs = mipi_1272mbps_24mhz_2l, + .lf_to_pix_rate_factor = 2, + .reg_list = REGS(mipi_1272mbps_24mhz_2l), }, [IMX258_4_LANE_MODE] = { - .num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz_4l), - .regs = mipi_1272mbps_24mhz_4l, + .lf_to_pix_rate_factor = 4, + .reg_list = REGS(mipi_1272mbps_24mhz_4l), }, } }, [IMX258_LINK_FREQ_640MBPS] = { .pixels_per_line = IMX258_PPL_DEFAULT, - .reg_list = { + .link_cfg = { [IMX258_2_LANE_MODE] = { - .num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz_2l), - .regs = mipi_642mbps_24mhz_2l, + .lf_to_pix_rate_factor = 2 * 2, + .reg_list = REGS(mipi_642mbps_24mhz_2l), }, [IMX258_4_LANE_MODE] = { - .num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz_4l), - .regs = mipi_642mbps_24mhz_4l, + .lf_to_pix_rate_factor = 4, + .reg_list = REGS(mipi_642mbps_24mhz_4l), }, } }, @@ -635,7 +650,7 @@ struct imx258 { const struct imx258_link_freq_config *link_freq_configs; const s64 *link_freq_menu_items; - unsigned int nlanes; + unsigned int lane_mode_idx; unsigned int csi2_flags; /* @@ -945,8 +960,10 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_format *fmt) { struct imx258 *imx258 = to_imx258(sd); - const struct imx258_mode *mode; + const struct imx258_link_freq_config *link_freq_cfgs; + const struct imx258_link_cfg *link_cfg; struct v4l2_mbus_framefmt *framefmt; + const struct imx258_mode *mode; s32 vblank_def; s32 vblank_min; s64 h_blank; @@ -970,7 +987,11 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd, __v4l2_ctrl_s_ctrl(imx258->link_freq, mode->link_freq_index); link_freq = imx258->link_freq_menu_items[mode->link_freq_index]; - pixel_rate = link_freq_to_pixel_rate(link_freq, imx258->nlanes); + link_freq_cfgs = + &imx258->link_freq_configs[mode->link_freq_index]; + + link_cfg = &link_freq_cfgs->link_cfg[imx258->lane_mode_idx]; + pixel_rate = link_freq_to_pixel_rate(link_freq, link_cfg); __v4l2_ctrl_modify_range(imx258->pixel_rate, pixel_rate, pixel_rate, 1, pixel_rate); /* Update limits and set FPS to default */ @@ -1068,7 +1089,8 @@ static int imx258_start_streaming(struct imx258 *imx258) /* Setup PLL */ link_freq_index = imx258->cur_mode->link_freq_index; link_freq_cfg = &imx258->link_freq_configs[link_freq_index]; - reg_list = &link_freq_cfg->reg_list[imx258->nlanes == 2 ? 0 : 1]; + + reg_list = &link_freq_cfg->link_cfg[imx258->lane_mode_idx].reg_list; ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs); if (ret) { dev_err(&client->dev, "%s failed to set plls\n", __func__); @@ -1257,9 +1279,11 @@ static const struct v4l2_subdev_internal_ops imx258_internal_ops = { static int imx258_init_controls(struct imx258 *imx258) { struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + const struct imx258_link_freq_config *link_freq_cfgs; struct v4l2_fwnode_device_properties props; - struct v4l2_ctrl_handler *ctrl_hdlr; struct v4l2_ctrl *vflip, *hflip; + struct v4l2_ctrl_handler *ctrl_hdlr; + const struct imx258_link_cfg *link_cfg; s64 vblank_def; s64 vblank_min; s64 pixel_rate; @@ -1293,8 +1317,11 @@ static int imx258_init_controls(struct imx258 *imx258) if (vflip) vflip->flags |= V4L2_CTRL_FLAG_READ_ONLY; + link_freq_cfgs = &imx258->link_freq_configs[0]; + link_cfg = link_freq_cfgs[imx258->lane_mode_idx].link_cfg; pixel_rate = link_freq_to_pixel_rate(imx258->link_freq_menu_items[0], - imx258->nlanes); + link_cfg); + /* By default, PIXEL_RATE is read only */ imx258->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, V4L2_CID_PIXEL_RATE, @@ -1448,10 +1475,16 @@ static int imx258_probe(struct i2c_client *client) } /* Get number of data lanes */ - imx258->nlanes = ep.bus.mipi_csi2.num_data_lanes; - if (imx258->nlanes != 2 && imx258->nlanes != 4) { + switch (ep.bus.mipi_csi2.num_data_lanes) { + case 2: + imx258->lane_mode_idx = IMX258_2_LANE_MODE; + break; + case 4: + imx258->lane_mode_idx = IMX258_4_LANE_MODE; + break; + default: dev_err(&client->dev, "Invalid data lanes: %u\n", - imx258->nlanes); + ep.bus.mipi_csi2.num_data_lanes); ret = -EINVAL; goto error_endpoint_free; } -- cgit v1.2.3 From 3ec02d3a2a9b46b91122a74ff49daa4694ee3471 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 1 May 2024 09:24:34 -0600 Subject: dt-bindings: media: imx258: Rename to include vendor prefix imx258.yaml doesn't include the vendor prefix of sony, so rename to add it. Update the id entry and MAINTAINERS to match. Signed-off-by: Dave Stevenson Acked-by: Conor Dooley Signed-off-by: Luis Garcia Reviewed-by: Pavel Machek Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .../devicetree/bindings/media/i2c/imx258.yaml | 134 --------------------- .../devicetree/bindings/media/i2c/sony,imx258.yaml | 134 +++++++++++++++++++++ MAINTAINERS | 2 +- 3 files changed, 135 insertions(+), 135 deletions(-) delete mode 100644 Documentation/devicetree/bindings/media/i2c/imx258.yaml create mode 100644 Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/imx258.yaml b/Documentation/devicetree/bindings/media/i2c/imx258.yaml deleted file mode 100644 index 80d24220baa0..000000000000 --- a/Documentation/devicetree/bindings/media/i2c/imx258.yaml +++ /dev/null @@ -1,134 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/media/i2c/imx258.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Sony IMX258 13 Mpixel CMOS Digital Image Sensor - -maintainers: - - Krzysztof Kozlowski - -description: |- - IMX258 is a diagonal 5.867mm (Type 1/3.06) 13 Mega-pixel CMOS active pixel - type stacked image sensor with a square pixel array of size 4208 x 3120. It - is programmable through I2C interface. Image data is sent through MIPI - CSI-2. - -properties: - compatible: - const: sony,imx258 - - assigned-clocks: true - assigned-clock-parents: true - assigned-clock-rates: true - - clocks: - description: - Clock frequency from 6 to 27 MHz. - maxItems: 1 - - reg: - maxItems: 1 - - reset-gpios: - description: |- - Reference to the GPIO connected to the XCLR pin, if any. - - vana-supply: - description: - Analog voltage (VANA) supply, 2.7 V - - vdig-supply: - description: - Digital I/O voltage (VDIG) supply, 1.2 V - - vif-supply: - description: - Interface voltage (VIF) supply, 1.8 V - - # See ../video-interfaces.txt for more details - port: - $ref: /schemas/graph.yaml#/$defs/port-base - additionalProperties: false - - properties: - endpoint: - $ref: /schemas/media/video-interfaces.yaml# - unevaluatedProperties: false - - properties: - data-lanes: - oneOf: - - items: - - const: 1 - - const: 2 - - const: 3 - - const: 4 - - items: - - const: 1 - - const: 2 - - link-frequencies: true - - required: - - data-lanes - - link-frequencies - -required: - - compatible - - reg - - port - -additionalProperties: false - -examples: - - | - i2c { - #address-cells = <1>; - #size-cells = <0>; - - sensor@6c { - compatible = "sony,imx258"; - reg = <0x6c>; - clocks = <&imx258_clk>; - - port { - endpoint { - remote-endpoint = <&csi1_ep>; - data-lanes = <1 2 3 4>; - link-frequencies = /bits/ 64 <320000000>; - }; - }; - }; - }; - - /* Oscillator on the camera board */ - imx258_clk: clk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <19200000>; - }; - - - | - i2c { - #address-cells = <1>; - #size-cells = <0>; - - sensor@6c { - compatible = "sony,imx258"; - reg = <0x6c>; - clocks = <&imx258_clk>; - - assigned-clocks = <&imx258_clk>; - assigned-clock-rates = <19200000>; - - port { - endpoint { - remote-endpoint = <&csi1_ep>; - data-lanes = <1 2 3 4>; - link-frequencies = /bits/ 64 <633600000>; - }; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml new file mode 100644 index 000000000000..bee61a443b23 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml @@ -0,0 +1,134 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/sony,imx258.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sony IMX258 13 Mpixel CMOS Digital Image Sensor + +maintainers: + - Krzysztof Kozlowski + +description: |- + IMX258 is a diagonal 5.867mm (Type 1/3.06) 13 Mega-pixel CMOS active pixel + type stacked image sensor with a square pixel array of size 4208 x 3120. It + is programmable through I2C interface. Image data is sent through MIPI + CSI-2. + +properties: + compatible: + const: sony,imx258 + + assigned-clocks: true + assigned-clock-parents: true + assigned-clock-rates: true + + clocks: + description: + Clock frequency from 6 to 27 MHz. + maxItems: 1 + + reg: + maxItems: 1 + + reset-gpios: + description: |- + Reference to the GPIO connected to the XCLR pin, if any. + + vana-supply: + description: + Analog voltage (VANA) supply, 2.7 V + + vdig-supply: + description: + Digital I/O voltage (VDIG) supply, 1.2 V + + vif-supply: + description: + Interface voltage (VIF) supply, 1.8 V + + # See ../video-interfaces.txt for more details + port: + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + oneOf: + - items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + - items: + - const: 1 + - const: 2 + + link-frequencies: true + + required: + - data-lanes + - link-frequencies + +required: + - compatible + - reg + - port + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + sensor@6c { + compatible = "sony,imx258"; + reg = <0x6c>; + clocks = <&imx258_clk>; + + port { + endpoint { + remote-endpoint = <&csi1_ep>; + data-lanes = <1 2 3 4>; + link-frequencies = /bits/ 64 <320000000>; + }; + }; + }; + }; + + /* Oscillator on the camera board */ + imx258_clk: clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <19200000>; + }; + + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + sensor@6c { + compatible = "sony,imx258"; + reg = <0x6c>; + clocks = <&imx258_clk>; + + assigned-clocks = <&imx258_clk>; + assigned-clock-rates = <19200000>; + + port { + endpoint { + remote-endpoint = <&csi1_ep>; + data-lanes = <1 2 3 4>; + link-frequencies = /bits/ 64 <633600000>; + }; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index d6c90161c7bf..f1d905f15180 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20804,7 +20804,7 @@ M: Sakari Ailus L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git -F: Documentation/devicetree/bindings/media/i2c/imx258.yaml +F: Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml F: drivers/media/i2c/imx258.c SONY IMX274 SENSOR DRIVER -- cgit v1.2.3 From 8bae5ecb11970fc3ff4d7017b557f548e4e4f784 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 1 May 2024 09:24:35 -0600 Subject: dt-bindings: media: imx258: Add alternate compatible strings There are a number of variants of the imx258 modules that can not be differentiated at runtime, so add compatible strings for the PDAF variant. Signed-off-by: Dave Stevenson Signed-off-by: Luis Garcia Acked-by: Conor Dooley Reviewed-by: Pavel Machek Reviewed-by: Rob Herring Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml index bee61a443b23..c978abc0cdb3 100644 --- a/Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml @@ -13,11 +13,16 @@ description: |- IMX258 is a diagonal 5.867mm (Type 1/3.06) 13 Mega-pixel CMOS active pixel type stacked image sensor with a square pixel array of size 4208 x 3120. It is programmable through I2C interface. Image data is sent through MIPI - CSI-2. + CSI-2. The sensor exists in two different models, a standard variant + (IMX258) and a variant with phase detection autofocus (IMX258-PDAF). + The camera module does not expose the model through registers, so the + exact model needs to be specified. properties: compatible: - const: sony,imx258 + enum: + - sony,imx258 + - sony,imx258-pdaf assigned-clocks: true assigned-clock-parents: true -- cgit v1.2.3 From 799c46e849b1b051fcf33547231aec6888fe5003 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 1 May 2024 09:24:36 -0600 Subject: media: i2c: imx258: Change register settings for variants of the sensor Sony have advised that there are variants of the IMX258 sensor which require slightly different register configuration to the mainline imx258 driver defaults. There is no available run-time detection for the variant, so add configuration via the DT compatible string. The Vision Components imx258 module supports PDAF, so add the register differences for that variant Signed-off-by: Dave Stevenson Signed-off-by: Luis Garcia Reviewed-by: Pavel Machek Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx258.c | 47 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index 32267d36b8f3..54f7625e60a3 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -321,8 +321,6 @@ static const struct imx258_reg mipi_642mbps_24mhz_4l[] = { static const struct imx258_reg mode_common_regs[] = { { 0x3051, 0x00 }, - { 0x3052, 0x00 }, - { 0x4E21, 0x14 }, { 0x6B11, 0xCF }, { 0x7FF0, 0x08 }, { 0x7FF1, 0x0F }, @@ -345,7 +343,6 @@ static const struct imx258_reg mode_common_regs[] = { { 0x7FA8, 0x03 }, { 0x7FA9, 0xFE }, { 0x7B24, 0x81 }, - { 0x7B25, 0x00 }, { 0x6564, 0x07 }, { 0x6B0D, 0x41 }, { 0x653D, 0x04 }, @@ -460,6 +457,33 @@ static const struct imx258_reg mode_1048_780_regs[] = { { 0x034F, 0x0C }, }; +struct imx258_variant_cfg { + const struct imx258_reg *regs; + unsigned int num_regs; +}; + +static const struct imx258_reg imx258_cfg_regs[] = { + { 0x3052, 0x00 }, + { 0x4E21, 0x14 }, + { 0x7B25, 0x00 }, +}; + +static const struct imx258_variant_cfg imx258_cfg = { + .regs = imx258_cfg_regs, + .num_regs = ARRAY_SIZE(imx258_cfg_regs), +}; + +static const struct imx258_reg imx258_pdaf_cfg_regs[] = { + { 0x3052, 0x01 }, + { 0x4E21, 0x10 }, + { 0x7B25, 0x01 }, +}; + +static const struct imx258_variant_cfg imx258_pdaf_cfg = { + .regs = imx258_pdaf_cfg_regs, + .num_regs = ARRAY_SIZE(imx258_pdaf_cfg_regs), +}; + static const char * const imx258_test_pattern_menu[] = { "Disabled", "Solid Colour", @@ -637,6 +661,8 @@ struct imx258 { struct v4l2_subdev sd; struct media_pad pad; + const struct imx258_variant_cfg *variant_cfg; + struct v4l2_ctrl_handler ctrl_handler; /* V4L2 Controls */ struct v4l2_ctrl *link_freq; @@ -1104,6 +1130,14 @@ static int imx258_start_streaming(struct imx258 *imx258) return ret; } + ret = imx258_write_regs(imx258, imx258->variant_cfg->regs, + imx258->variant_cfg->num_regs); + if (ret) { + dev_err(&client->dev, "%s failed to set variant config\n", + __func__); + return ret; + } + ret = imx258_write_reg(imx258, IMX258_CLK_BLANK_STOP, IMX258_REG_VALUE_08BIT, !!(imx258->csi2_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)); @@ -1491,6 +1525,10 @@ static int imx258_probe(struct i2c_client *client) imx258->csi2_flags = ep.bus.mipi_csi2.flags; + imx258->variant_cfg = device_get_match_data(&client->dev); + if (!imx258->variant_cfg) + imx258->variant_cfg = &imx258_cfg; + /* Initialize subdev */ v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops); @@ -1578,7 +1616,8 @@ MODULE_DEVICE_TABLE(acpi, imx258_acpi_ids); #endif static const struct of_device_id imx258_dt_ids[] = { - { .compatible = "sony,imx258" }, + { .compatible = "sony,imx258", .data = &imx258_cfg }, + { .compatible = "sony,imx258-pdaf", .data = &imx258_pdaf_cfg }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx258_dt_ids); -- cgit v1.2.3 From 4c05213aeed73ef055430e619bd7b40f6839de0e Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 1 May 2024 09:24:37 -0600 Subject: media: i2c: imx258: Make HFLIP and VFLIP controls writable The sensor supports H & V flips, but the controls were READ_ONLY. Note that the Bayer order changes with these flips, therefore they set the V4L2_CTRL_FLAG_MODIFY_LAYOUT property. Signed-off-by: Dave Stevenson Signed-off-by: Luis Garcia Reviewed-by: Pavel Machek Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx258.c | 100 +++++++++++++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 35 deletions(-) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index 54f7625e60a3..f043200e336e 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -78,8 +78,8 @@ /* Orientation */ #define REG_MIRROR_FLIP_CONTROL 0x0101 -#define REG_CONFIG_MIRROR_FLIP 0x03 -#define REG_CONFIG_FLIP_TEST_PATTERN 0x02 +#define REG_CONFIG_MIRROR_HFLIP 0x01 +#define REG_CONFIG_MIRROR_VFLIP 0x02 /* IMX258 native and active pixel array size. */ #define IMX258_NATIVE_WIDTH 4224U @@ -484,6 +484,23 @@ static const struct imx258_variant_cfg imx258_pdaf_cfg = { .num_regs = ARRAY_SIZE(imx258_pdaf_cfg_regs), }; +/* + * The supported formats. + * This table MUST contain 4 entries per format, to cover the various flip + * combinations in the order + * - no flip + * - h flip + * - v flip + * - h&v flips + */ +static const u32 codes[] = { + /* 10-bit modes. */ + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10 +}; + static const char * const imx258_test_pattern_menu[] = { "Disabled", "Solid Colour", @@ -670,6 +687,8 @@ struct imx258 { struct v4l2_ctrl *vblank; struct v4l2_ctrl *hblank; struct v4l2_ctrl *exposure; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; /* Current mode */ const struct imx258_mode *cur_mode; @@ -768,9 +787,23 @@ static int imx258_write_regs(struct imx258 *imx258, return 0; } +/* Get bayer order based on flip setting. */ +static u32 imx258_get_format_code(const struct imx258 *imx258) +{ + unsigned int i; + + lockdep_assert_held(&imx258->mutex); + + i = (imx258->vflip->val ? 2 : 0) | + (imx258->hflip->val ? 1 : 0); + + return codes[i]; +} + /* Open sub-device */ static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { + struct imx258 *imx258 = to_imx258(sd); struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_state_get_format(fh->state, 0); struct v4l2_rect *try_crop; @@ -778,7 +811,7 @@ static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) /* Initialize try_fmt */ try_fmt->width = supported_modes[0].width; try_fmt->height = supported_modes[0].height; - try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10; + try_fmt->code = imx258_get_format_code(imx258); try_fmt->field = V4L2_FIELD_NONE; /* Initialize try_crop */ @@ -871,10 +904,6 @@ static int imx258_set_ctrl(struct v4l2_ctrl *ctrl) ret = imx258_write_reg(imx258, IMX258_REG_TEST_PATTERN, IMX258_REG_VALUE_16BIT, ctrl->val); - ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL, - IMX258_REG_VALUE_08BIT, - !ctrl->val ? REG_CONFIG_MIRROR_FLIP : - REG_CONFIG_FLIP_TEST_PATTERN); break; case V4L2_CID_WIDE_DYNAMIC_RANGE: if (!ctrl->val) { @@ -897,6 +926,15 @@ static int imx258_set_ctrl(struct v4l2_ctrl *ctrl) IMX258_REG_VALUE_16BIT, imx258->cur_mode->height + ctrl->val); break; + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL, + IMX258_REG_VALUE_08BIT, + (imx258->hflip->val ? + REG_CONFIG_MIRROR_HFLIP : 0) | + (imx258->vflip->val ? + REG_CONFIG_MIRROR_VFLIP : 0)); + break; default: dev_info(&client->dev, "ctrl(id:0x%x,val:0x%x) is not handled\n", @@ -918,11 +956,13 @@ static int imx258_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { - /* Only one bayer order(GRBG) is supported */ + struct imx258 *imx258 = to_imx258(sd); + + /* Only one bayer format (10 bit) is supported */ if (code->index > 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_SBGGR10_1X10; + code->code = imx258_get_format_code(imx258); return 0; } @@ -931,10 +971,11 @@ static int imx258_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { + struct imx258 *imx258 = to_imx258(sd); if (fse->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; - if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10) + if (fse->code != imx258_get_format_code(imx258)) return -EINVAL; fse->min_width = supported_modes[fse->index].width; @@ -945,12 +986,13 @@ static int imx258_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static void imx258_update_pad_format(const struct imx258_mode *mode, +static void imx258_update_pad_format(struct imx258 *imx258, + const struct imx258_mode *mode, struct v4l2_subdev_format *fmt) { fmt->format.width = mode->width; fmt->format.height = mode->height; - fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.code = imx258_get_format_code(imx258); fmt->format.field = V4L2_FIELD_NONE; } @@ -962,7 +1004,7 @@ static int __imx258_get_pad_format(struct imx258 *imx258, fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad); else - imx258_update_pad_format(imx258->cur_mode, fmt); + imx258_update_pad_format(imx258, imx258->cur_mode, fmt); return 0; } @@ -998,13 +1040,12 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd, mutex_lock(&imx258->mutex); - /* Only one raw bayer(GBRG) order is supported */ - fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10; + fmt->format.code = imx258_get_format_code(imx258); mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes), width, height, fmt->format.width, fmt->format.height); - imx258_update_pad_format(mode, fmt); + imx258_update_pad_format(imx258, mode, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; @@ -1154,15 +1195,6 @@ static int imx258_start_streaming(struct imx258 *imx258) return ret; } - /* Set Orientation be 180 degree */ - ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL, - IMX258_REG_VALUE_08BIT, REG_CONFIG_MIRROR_FLIP); - if (ret) { - dev_err(&client->dev, "%s failed to set orientation\n", - __func__); - return ret; - } - /* Apply customized values from user */ ret = __v4l2_ctrl_handler_setup(imx258->sd.ctrl_handler); if (ret) @@ -1315,7 +1347,6 @@ static int imx258_init_controls(struct imx258 *imx258) struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); const struct imx258_link_freq_config *link_freq_cfgs; struct v4l2_fwnode_device_properties props; - struct v4l2_ctrl *vflip, *hflip; struct v4l2_ctrl_handler *ctrl_hdlr; const struct imx258_link_cfg *link_cfg; s64 vblank_def; @@ -1340,16 +1371,15 @@ static int imx258_init_controls(struct imx258 *imx258) if (imx258->link_freq) imx258->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - /* The driver only supports one bayer order and flips by default. */ - hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, - V4L2_CID_HFLIP, 1, 1, 1, 1); - if (hflip) - hflip->flags |= V4L2_CTRL_FLAG_READ_ONLY; + imx258->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 1); + if (imx258->hflip) + imx258->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; - vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, - V4L2_CID_VFLIP, 1, 1, 1, 1); - if (vflip) - vflip->flags |= V4L2_CTRL_FLAG_READ_ONLY; + imx258->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 1); + if (imx258->vflip) + imx258->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; link_freq_cfgs = &imx258->link_freq_configs[0]; link_cfg = link_freq_cfgs[imx258->lane_mode_idx].link_cfg; -- cgit v1.2.3 From fdf63603cee6c51609d9f2d987c5107106abf7ca Mon Sep 17 00:00:00 2001 From: Luis Garcia Date: Wed, 1 May 2024 09:24:41 -0600 Subject: media: i2c: imx258: Use v4l2_link_freq_to_bitmap helper Use the v4l2_link_freq_to_bitmap() helper to figure out which driver-supported link freq can be used on a given system. Signed-off-by: Luis Garcia Reviewed-by: Pavel Machek Reviewed-by: Tommaso Merciai Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx258.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index f043200e336e..a38c80342eea 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -693,6 +693,7 @@ struct imx258 { /* Current mode */ const struct imx258_mode *cur_mode; + unsigned long link_freq_bitmap; const struct imx258_link_freq_config *link_freq_configs; const s64 *link_freq_menu_items; unsigned int lane_mode_idx; @@ -1538,6 +1539,17 @@ static int imx258_probe(struct i2c_client *client) return ret; } + ret = v4l2_link_freq_to_bitmap(&client->dev, + ep.link_frequencies, + ep.nr_of_link_frequencies, + imx258->link_freq_menu_items, + ARRAY_SIZE(link_freq_menu_items_19_2), + &imx258->link_freq_bitmap); + if (ret) { + dev_err(&client->dev, "Link frequency not supported\n"); + goto error_endpoint_free; + } + /* Get number of data lanes */ switch (ep.bus.mipi_csi2.num_data_lanes) { case 2: -- cgit v1.2.3 From 40431ff5a0fcd3e42a8c9c921f221944fcaba1db Mon Sep 17 00:00:00 2001 From: Luis Garcia Date: Wed, 1 May 2024 09:24:42 -0600 Subject: media: i2c: imx258: Convert to new CCI register access helpers Use the new comon CCI register access helpers to replace the private register access helpers in the imx258 driver. Signed-off-by: Luis Garcia Reviewed-by: Tommaso Merciai Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/Kconfig | 1 + drivers/media/i2c/imx258.c | 817 +++++++++++++++++++-------------------------- 2 files changed, 349 insertions(+), 469 deletions(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index c6d3ee472d81..612b0625b176 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -139,6 +139,7 @@ config VIDEO_IMX219 config VIDEO_IMX258 tristate "Sony IMX258 sensor support" + select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the Sony IMX258 camera. diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index a38c80342eea..1a99eaaff21a 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -8,22 +8,20 @@ #include #include #include +#include #include #include #include #include -#define IMX258_REG_VALUE_08BIT 1 -#define IMX258_REG_VALUE_16BIT 2 - -#define IMX258_REG_MODE_SELECT 0x0100 +#define IMX258_REG_MODE_SELECT CCI_REG8(0x0100) #define IMX258_MODE_STANDBY 0x00 #define IMX258_MODE_STREAMING 0x01 -#define IMX258_REG_RESET 0x0103 +#define IMX258_REG_RESET CCI_REG8(0x0103) /* Chip ID */ -#define IMX258_REG_CHIP_ID 0x0016 +#define IMX258_REG_CHIP_ID CCI_REG16(0x0016) #define IMX258_CHIP_ID 0x0258 /* V_TIMING internal */ @@ -32,13 +30,11 @@ #define IMX258_VTS_30FPS_VGA 0x034c #define IMX258_VTS_MAX 65525 -#define IMX258_REG_VTS 0x0340 - /* HBLANK control - read only */ #define IMX258_PPL_DEFAULT 5352 /* Exposure control */ -#define IMX258_REG_EXPOSURE 0x0202 +#define IMX258_REG_EXPOSURE CCI_REG16(0x0202) #define IMX258_EXPOSURE_OFFSET 10 #define IMX258_EXPOSURE_MIN 4 #define IMX258_EXPOSURE_STEP 1 @@ -46,38 +42,38 @@ #define IMX258_EXPOSURE_MAX (IMX258_VTS_MAX - IMX258_EXPOSURE_OFFSET) /* Analog gain control */ -#define IMX258_REG_ANALOG_GAIN 0x0204 +#define IMX258_REG_ANALOG_GAIN CCI_REG16(0x0204) #define IMX258_ANA_GAIN_MIN 0 #define IMX258_ANA_GAIN_MAX 480 #define IMX258_ANA_GAIN_STEP 1 #define IMX258_ANA_GAIN_DEFAULT 0x0 /* Digital gain control */ -#define IMX258_REG_GR_DIGITAL_GAIN 0x020e -#define IMX258_REG_R_DIGITAL_GAIN 0x0210 -#define IMX258_REG_B_DIGITAL_GAIN 0x0212 -#define IMX258_REG_GB_DIGITAL_GAIN 0x0214 +#define IMX258_REG_GR_DIGITAL_GAIN CCI_REG16(0x020e) +#define IMX258_REG_R_DIGITAL_GAIN CCI_REG16(0x0210) +#define IMX258_REG_B_DIGITAL_GAIN CCI_REG16(0x0212) +#define IMX258_REG_GB_DIGITAL_GAIN CCI_REG16(0x0214) #define IMX258_DGTL_GAIN_MIN 0 #define IMX258_DGTL_GAIN_MAX 4096 /* Max = 0xFFF */ #define IMX258_DGTL_GAIN_DEFAULT 1024 #define IMX258_DGTL_GAIN_STEP 1 /* HDR control */ -#define IMX258_REG_HDR 0x0220 +#define IMX258_REG_HDR CCI_REG8(0x0220) #define IMX258_HDR_ON BIT(0) -#define IMX258_REG_HDR_RATIO 0x0222 +#define IMX258_REG_HDR_RATIO CCI_REG8(0x0222) #define IMX258_HDR_RATIO_MIN 0 #define IMX258_HDR_RATIO_MAX 5 #define IMX258_HDR_RATIO_STEP 1 #define IMX258_HDR_RATIO_DEFAULT 0x0 /* Test Pattern Control */ -#define IMX258_REG_TEST_PATTERN 0x0600 +#define IMX258_REG_TEST_PATTERN CCI_REG16(0x0600) -#define IMX258_CLK_BLANK_STOP 0x4040 +#define IMX258_CLK_BLANK_STOP CCI_REG8(0x4040) /* Orientation */ -#define REG_MIRROR_FLIP_CONTROL 0x0101 +#define REG_MIRROR_FLIP_CONTROL CCI_REG8(0x0101) #define REG_CONFIG_MIRROR_HFLIP 0x01 #define REG_CONFIG_MIRROR_VFLIP 0x02 @@ -89,14 +85,53 @@ #define IMX258_PIXEL_ARRAY_WIDTH 4208U #define IMX258_PIXEL_ARRAY_HEIGHT 3120U -struct imx258_reg { - u16 address; - u8 val; -}; +/* regs */ +#define IMX258_REG_PLL_MULT_DRIV CCI_REG8(0x0310) +#define IMX258_REG_IVTPXCK_DIV CCI_REG8(0x0301) +#define IMX258_REG_IVTSYCK_DIV CCI_REG8(0x0303) +#define IMX258_REG_PREPLLCK_VT_DIV CCI_REG8(0x0305) +#define IMX258_REG_IOPPXCK_DIV CCI_REG8(0x0309) +#define IMX258_REG_IOPSYCK_DIV CCI_REG8(0x030b) +#define IMX258_REG_PREPLLCK_OP_DIV CCI_REG8(0x030d) +#define IMX258_REG_PHASE_PIX_OUTEN CCI_REG8(0x3030) +#define IMX258_REG_PDPIX_DATA_RATE CCI_REG8(0x3032) +#define IMX258_REG_SCALE_MODE CCI_REG8(0x0401) +#define IMX258_REG_SCALE_MODE_EXT CCI_REG8(0x3038) +#define IMX258_REG_AF_WINDOW_MODE CCI_REG8(0x7bcd) +#define IMX258_REG_FRM_LENGTH_CTL CCI_REG8(0x0350) +#define IMX258_REG_CSI_LANE_MODE CCI_REG8(0x0114) +#define IMX258_REG_X_EVN_INC CCI_REG8(0x0381) +#define IMX258_REG_X_ODD_INC CCI_REG8(0x0383) +#define IMX258_REG_Y_EVN_INC CCI_REG8(0x0385) +#define IMX258_REG_Y_ODD_INC CCI_REG8(0x0387) +#define IMX258_REG_BINNING_MODE CCI_REG8(0x0900) +#define IMX258_REG_BINNING_TYPE_V CCI_REG8(0x0901) +#define IMX258_REG_FORCE_FD_SUM CCI_REG8(0x300d) +#define IMX258_REG_DIG_CROP_X_OFFSET CCI_REG16(0x0408) +#define IMX258_REG_DIG_CROP_Y_OFFSET CCI_REG16(0x040a) +#define IMX258_REG_DIG_CROP_IMAGE_WIDTH CCI_REG16(0x040c) +#define IMX258_REG_DIG_CROP_IMAGE_HEIGHT CCI_REG16(0x040e) +#define IMX258_REG_SCALE_M CCI_REG16(0x0404) +#define IMX258_REG_X_OUT_SIZE CCI_REG16(0x034c) +#define IMX258_REG_Y_OUT_SIZE CCI_REG16(0x034e) +#define IMX258_REG_X_ADD_STA CCI_REG16(0x0344) +#define IMX258_REG_Y_ADD_STA CCI_REG16(0x0346) +#define IMX258_REG_X_ADD_END CCI_REG16(0x0348) +#define IMX258_REG_Y_ADD_END CCI_REG16(0x034a) +#define IMX258_REG_EXCK_FREQ CCI_REG16(0x0136) +#define IMX258_REG_CSI_DT_FMT CCI_REG16(0x0112) +#define IMX258_REG_LINE_LENGTH_PCK CCI_REG16(0x0342) +#define IMX258_REG_SCALE_M_EXT CCI_REG16(0x303a) +#define IMX258_REG_FRM_LENGTH_LINES CCI_REG16(0x0340) +#define IMX258_REG_FINE_INTEG_TIME CCI_REG8(0x0200) +#define IMX258_REG_PLL_IVT_MPY CCI_REG16(0x0306) +#define IMX258_REG_PLL_IOP_MPY CCI_REG16(0x030e) +#define IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H CCI_REG16(0x0820) +#define IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L CCI_REG16(0x0822) struct imx258_reg_list { u32 num_of_regs; - const struct imx258_reg *regs; + const struct cci_reg_sequence *regs; }; struct imx258_link_cfg { @@ -143,329 +178,264 @@ struct imx258_mode { * To avoid further computation of clock settings, adopt the same per * lane data rate when using 2 lanes, thus allowing a maximum of 15fps. */ -static const struct imx258_reg mipi_1267mbps_19_2mhz_2l[] = { - { 0x0136, 0x13 }, - { 0x0137, 0x33 }, - { 0x0301, 0x0A }, - { 0x0303, 0x02 }, - { 0x0305, 0x03 }, - { 0x0306, 0x00 }, - { 0x0307, 0xC6 }, - { 0x0309, 0x0A }, - { 0x030B, 0x01 }, - { 0x030D, 0x02 }, - { 0x030E, 0x00 }, - { 0x030F, 0xD8 }, - { 0x0310, 0x00 }, - - { 0x0114, 0x01 }, - { 0x0820, 0x09 }, - { 0x0821, 0xa6 }, - { 0x0822, 0x66 }, - { 0x0823, 0x66 }, +static const struct cci_reg_sequence mipi_1267mbps_19_2mhz_2l[] = { + { IMX258_REG_EXCK_FREQ, 0x1333 }, + { IMX258_REG_IVTPXCK_DIV, 10 }, + { IMX258_REG_IVTSYCK_DIV, 2 }, + { IMX258_REG_PREPLLCK_VT_DIV, 3 }, + { IMX258_REG_PLL_IVT_MPY, 198 }, + { IMX258_REG_IOPPXCK_DIV, 10 }, + { IMX258_REG_IOPSYCK_DIV, 1 }, + { IMX258_REG_PREPLLCK_OP_DIV, 2 }, + { IMX258_REG_PLL_IOP_MPY, 216 }, + { IMX258_REG_PLL_MULT_DRIV, 0 }, + + { IMX258_REG_CSI_LANE_MODE, 1 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H, 1267 * 2 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L, 0 }, }; -static const struct imx258_reg mipi_1267mbps_19_2mhz_4l[] = { - { 0x0136, 0x13 }, - { 0x0137, 0x33 }, - { 0x0301, 0x05 }, - { 0x0303, 0x02 }, - { 0x0305, 0x03 }, - { 0x0306, 0x00 }, - { 0x0307, 0xC6 }, - { 0x0309, 0x0A }, - { 0x030B, 0x01 }, - { 0x030D, 0x02 }, - { 0x030E, 0x00 }, - { 0x030F, 0xD8 }, - { 0x0310, 0x00 }, - - { 0x0114, 0x03 }, - { 0x0820, 0x13 }, - { 0x0821, 0x4C }, - { 0x0822, 0xCC }, - { 0x0823, 0xCC }, +static const struct cci_reg_sequence mipi_1267mbps_19_2mhz_4l[] = { + { IMX258_REG_EXCK_FREQ, 0x1333 }, + { IMX258_REG_IVTPXCK_DIV, 5 }, + { IMX258_REG_IVTSYCK_DIV, 2 }, + { IMX258_REG_PREPLLCK_VT_DIV, 3 }, + { IMX258_REG_PLL_IVT_MPY, 198 }, + { IMX258_REG_IOPPXCK_DIV, 10 }, + { IMX258_REG_IOPSYCK_DIV, 1 }, + { IMX258_REG_PREPLLCK_OP_DIV, 2 }, + { IMX258_REG_PLL_IOP_MPY, 216 }, + { IMX258_REG_PLL_MULT_DRIV, 0 }, + + { IMX258_REG_CSI_LANE_MODE, 3 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H, 1267 * 4 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L, 0 }, }; -static const struct imx258_reg mipi_1272mbps_24mhz_2l[] = { - { 0x0136, 0x18 }, - { 0x0137, 0x00 }, - { 0x0301, 0x0a }, - { 0x0303, 0x02 }, - { 0x0305, 0x04 }, - { 0x0306, 0x00 }, - { 0x0307, 0xD4 }, - { 0x0309, 0x0A }, - { 0x030B, 0x01 }, - { 0x030D, 0x02 }, - { 0x030E, 0x00 }, - { 0x030F, 0xD8 }, - { 0x0310, 0x00 }, - - { 0x0114, 0x01 }, - { 0x0820, 0x13 }, - { 0x0821, 0x4C }, - { 0x0822, 0xCC }, - { 0x0823, 0xCC }, +static const struct cci_reg_sequence mipi_1272mbps_24mhz_2l[] = { + { IMX258_REG_EXCK_FREQ, 0x1800 }, + { IMX258_REG_IVTPXCK_DIV, 10 }, + { IMX258_REG_IVTSYCK_DIV, 2 }, + { IMX258_REG_PREPLLCK_VT_DIV, 4 }, + { IMX258_REG_PLL_IVT_MPY, 212 }, + { IMX258_REG_IOPPXCK_DIV, 10 }, + { IMX258_REG_IOPSYCK_DIV, 1 }, + { IMX258_REG_PREPLLCK_OP_DIV, 2 }, + { IMX258_REG_PLL_IOP_MPY, 216 }, + { IMX258_REG_PLL_MULT_DRIV, 0 }, + + { IMX258_REG_CSI_LANE_MODE, 1 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H, 1272 * 2 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L, 0 }, }; -static const struct imx258_reg mipi_1272mbps_24mhz_4l[] = { - { 0x0136, 0x18 }, - { 0x0137, 0x00 }, - { 0x0301, 0x05 }, - { 0x0303, 0x02 }, - { 0x0305, 0x04 }, - { 0x0306, 0x00 }, - { 0x0307, 0xD4 }, - { 0x0309, 0x0A }, - { 0x030B, 0x01 }, - { 0x030D, 0x02 }, - { 0x030E, 0x00 }, - { 0x030F, 0xD8 }, - { 0x0310, 0x00 }, - - { 0x0114, 0x03 }, - { 0x0820, 0x13 }, - { 0x0821, 0xE0 }, - { 0x0822, 0x00 }, - { 0x0823, 0x00 }, +static const struct cci_reg_sequence mipi_1272mbps_24mhz_4l[] = { + { IMX258_REG_EXCK_FREQ, 0x1800 }, + { IMX258_REG_IVTPXCK_DIV, 5 }, + { IMX258_REG_IVTSYCK_DIV, 2 }, + { IMX258_REG_PREPLLCK_VT_DIV, 4 }, + { IMX258_REG_PLL_IVT_MPY, 212 }, + { IMX258_REG_IOPPXCK_DIV, 10 }, + { IMX258_REG_IOPSYCK_DIV, 1 }, + { IMX258_REG_PREPLLCK_OP_DIV, 2 }, + { IMX258_REG_PLL_IOP_MPY, 216 }, + { IMX258_REG_PLL_MULT_DRIV, 0 }, + + { IMX258_REG_CSI_LANE_MODE, 3 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H, 1272 * 4 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L, 0 }, }; -static const struct imx258_reg mipi_640mbps_19_2mhz_2l[] = { - { 0x0136, 0x13 }, - { 0x0137, 0x33 }, - { 0x0301, 0x05 }, - { 0x0303, 0x02 }, - { 0x0305, 0x03 }, - { 0x0306, 0x00 }, - { 0x0307, 0x64 }, - { 0x0309, 0x0A }, - { 0x030B, 0x01 }, - { 0x030D, 0x02 }, - { 0x030E, 0x00 }, - { 0x030F, 0xD8 }, - { 0x0310, 0x00 }, - - { 0x0114, 0x01 }, - { 0x0820, 0x05 }, - { 0x0821, 0x00 }, - { 0x0822, 0x00 }, - { 0x0823, 0x00 }, +static const struct cci_reg_sequence mipi_640mbps_19_2mhz_2l[] = { + { IMX258_REG_EXCK_FREQ, 0x1333 }, + { IMX258_REG_IVTPXCK_DIV, 5 }, + { IMX258_REG_IVTSYCK_DIV, 2 }, + { IMX258_REG_PREPLLCK_VT_DIV, 3 }, + { IMX258_REG_PLL_IVT_MPY, 100 }, + { IMX258_REG_IOPPXCK_DIV, 10 }, + { IMX258_REG_IOPSYCK_DIV, 1 }, + { IMX258_REG_PREPLLCK_OP_DIV, 2 }, + { IMX258_REG_PLL_IOP_MPY, 216 }, + { IMX258_REG_PLL_MULT_DRIV, 0 }, + + { IMX258_REG_CSI_LANE_MODE, 1 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H, 640 * 2 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L, 0 }, }; -static const struct imx258_reg mipi_640mbps_19_2mhz_4l[] = { - { 0x0136, 0x13 }, - { 0x0137, 0x33 }, - { 0x0301, 0x05 }, - { 0x0303, 0x02 }, - { 0x0305, 0x03 }, - { 0x0306, 0x00 }, - { 0x0307, 0x64 }, - { 0x0309, 0x0A }, - { 0x030B, 0x01 }, - { 0x030D, 0x02 }, - { 0x030E, 0x00 }, - { 0x030F, 0xD8 }, - { 0x0310, 0x00 }, - - { 0x0114, 0x03 }, - { 0x0820, 0x0A }, - { 0x0821, 0x00 }, - { 0x0822, 0x00 }, - { 0x0823, 0x00 }, +static const struct cci_reg_sequence mipi_640mbps_19_2mhz_4l[] = { + { IMX258_REG_EXCK_FREQ, 0x1333 }, + { IMX258_REG_IVTPXCK_DIV, 5 }, + { IMX258_REG_IVTSYCK_DIV, 2 }, + { IMX258_REG_PREPLLCK_VT_DIV, 3 }, + { IMX258_REG_PLL_IVT_MPY, 100 }, + { IMX258_REG_IOPPXCK_DIV, 10 }, + { IMX258_REG_IOPSYCK_DIV, 1 }, + { IMX258_REG_PREPLLCK_OP_DIV, 2 }, + { IMX258_REG_PLL_IOP_MPY, 216 }, + { IMX258_REG_PLL_MULT_DRIV, 0 }, + + { IMX258_REG_CSI_LANE_MODE, 3 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H, 640 * 4 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L, 0 }, }; -static const struct imx258_reg mipi_642mbps_24mhz_2l[] = { - { 0x0136, 0x18 }, - { 0x0137, 0x00 }, - { 0x0301, 0x05 }, - { 0x0303, 0x02 }, - { 0x0305, 0x04 }, - { 0x0306, 0x00 }, - { 0x0307, 0x6B }, - { 0x0309, 0x0A }, - { 0x030B, 0x01 }, - { 0x030D, 0x02 }, - { 0x030E, 0x00 }, - { 0x030F, 0xD8 }, - { 0x0310, 0x00 }, - - { 0x0114, 0x01 }, - { 0x0820, 0x0A }, - { 0x0821, 0x00 }, - { 0x0822, 0x00 }, - { 0x0823, 0x00 }, +static const struct cci_reg_sequence mipi_642mbps_24mhz_2l[] = { + { IMX258_REG_EXCK_FREQ, 0x1800 }, + { IMX258_REG_IVTPXCK_DIV, 5 }, + { IMX258_REG_IVTSYCK_DIV, 2 }, + { IMX258_REG_PREPLLCK_VT_DIV, 4 }, + { IMX258_REG_PLL_IVT_MPY, 107 }, + { IMX258_REG_IOPPXCK_DIV, 10 }, + { IMX258_REG_IOPSYCK_DIV, 1 }, + { IMX258_REG_PREPLLCK_OP_DIV, 2 }, + { IMX258_REG_PLL_IOP_MPY, 216 }, + { IMX258_REG_PLL_MULT_DRIV, 0 }, + + { IMX258_REG_CSI_LANE_MODE, 1 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H, 642 * 2 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L, 0 }, }; -static const struct imx258_reg mipi_642mbps_24mhz_4l[] = { - { 0x0136, 0x18 }, - { 0x0137, 0x00 }, - { 0x0301, 0x05 }, - { 0x0303, 0x02 }, - { 0x0305, 0x04 }, - { 0x0306, 0x00 }, - { 0x0307, 0x6B }, - { 0x0309, 0x0A }, - { 0x030B, 0x01 }, - { 0x030D, 0x02 }, - { 0x030E, 0x00 }, - { 0x030F, 0xD8 }, - { 0x0310, 0x00 }, - - { 0x0114, 0x03 }, - { 0x0820, 0x0A }, - { 0x0821, 0x00 }, - { 0x0822, 0x00 }, - { 0x0823, 0x00 }, +static const struct cci_reg_sequence mipi_642mbps_24mhz_4l[] = { + { IMX258_REG_EXCK_FREQ, 0x1800 }, + { IMX258_REG_IVTPXCK_DIV, 5 }, + { IMX258_REG_IVTSYCK_DIV, 2 }, + { IMX258_REG_PREPLLCK_VT_DIV, 4 }, + { IMX258_REG_PLL_IVT_MPY, 107 }, + { IMX258_REG_IOPPXCK_DIV, 10 }, + { IMX258_REG_IOPSYCK_DIV, 1 }, + { IMX258_REG_PREPLLCK_OP_DIV, 2 }, + { IMX258_REG_PLL_IOP_MPY, 216 }, + { IMX258_REG_PLL_MULT_DRIV, 0 }, + + { IMX258_REG_CSI_LANE_MODE, 3 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_H, 642 * 4 }, + { IMX258_REG_REQ_LINK_BIT_RATE_MBPS_L, 0 }, }; -static const struct imx258_reg mode_common_regs[] = { - { 0x3051, 0x00 }, - { 0x6B11, 0xCF }, - { 0x7FF0, 0x08 }, - { 0x7FF1, 0x0F }, - { 0x7FF2, 0x08 }, - { 0x7FF3, 0x1B }, - { 0x7FF4, 0x23 }, - { 0x7FF5, 0x60 }, - { 0x7FF6, 0x00 }, - { 0x7FF7, 0x01 }, - { 0x7FF8, 0x00 }, - { 0x7FF9, 0x78 }, - { 0x7FFA, 0x00 }, - { 0x7FFB, 0x00 }, - { 0x7FFC, 0x00 }, - { 0x7FFD, 0x00 }, - { 0x7FFE, 0x00 }, - { 0x7FFF, 0x03 }, - { 0x7F76, 0x03 }, - { 0x7F77, 0xFE }, - { 0x7FA8, 0x03 }, - { 0x7FA9, 0xFE }, - { 0x7B24, 0x81 }, - { 0x6564, 0x07 }, - { 0x6B0D, 0x41 }, - { 0x653D, 0x04 }, - { 0x6B05, 0x8C }, - { 0x6B06, 0xF9 }, - { 0x6B08, 0x65 }, - { 0x6B09, 0xFC }, - { 0x6B0A, 0xCF }, - { 0x6B0B, 0xD2 }, - { 0x6700, 0x0E }, - { 0x6707, 0x0E }, - { 0x9104, 0x00 }, - { 0x4648, 0x7F }, - { 0x7420, 0x00 }, - { 0x7421, 0x1C }, - { 0x7422, 0x00 }, - { 0x7423, 0xD7 }, - { 0x5F04, 0x00 }, - { 0x5F05, 0xED }, - { 0x0112, 0x0A }, - { 0x0113, 0x0A }, - { 0x0342, 0x14 }, - { 0x0343, 0xE8 }, - { 0x0344, 0x00 }, - { 0x0345, 0x00 }, - { 0x0346, 0x00 }, - { 0x0347, 0x00 }, - { 0x0348, 0x10 }, - { 0x0349, 0x6F }, - { 0x034A, 0x0C }, - { 0x034B, 0x2F }, - { 0x0381, 0x01 }, - { 0x0383, 0x01 }, - { 0x0385, 0x01 }, - { 0x0387, 0x01 }, - { 0x0404, 0x00 }, - { 0x0408, 0x00 }, - { 0x0409, 0x00 }, - { 0x040A, 0x00 }, - { 0x040B, 0x00 }, - { 0x040C, 0x10 }, - { 0x040D, 0x70 }, - { 0x3038, 0x00 }, - { 0x303A, 0x00 }, - { 0x303B, 0x10 }, - { 0x300D, 0x00 }, - { 0x0350, 0x00 }, - { 0x0204, 0x00 }, - { 0x0205, 0x00 }, - { 0x020E, 0x01 }, - { 0x020F, 0x00 }, - { 0x0210, 0x01 }, - { 0x0211, 0x00 }, - { 0x0212, 0x01 }, - { 0x0213, 0x00 }, - { 0x0214, 0x01 }, - { 0x0215, 0x00 }, - { 0x7BCD, 0x00 }, - { 0x94DC, 0x20 }, - { 0x94DD, 0x20 }, - { 0x94DE, 0x20 }, - { 0x95DC, 0x20 }, - { 0x95DD, 0x20 }, - { 0x95DE, 0x20 }, - { 0x7FB0, 0x00 }, - { 0x9010, 0x3E }, - { 0x9419, 0x50 }, - { 0x941B, 0x50 }, - { 0x9519, 0x50 }, - { 0x951B, 0x50 }, - { 0x3030, 0x00 }, - { 0x3032, 0x00 }, - { 0x0220, 0x00 }, +static const struct cci_reg_sequence mode_common_regs[] = { + { CCI_REG8(0x3051), 0x00 }, + { CCI_REG8(0x6B11), 0xCF }, + { CCI_REG8(0x7FF0), 0x08 }, + { CCI_REG8(0x7FF1), 0x0F }, + { CCI_REG8(0x7FF2), 0x08 }, + { CCI_REG8(0x7FF3), 0x1B }, + { CCI_REG8(0x7FF4), 0x23 }, + { CCI_REG8(0x7FF5), 0x60 }, + { CCI_REG8(0x7FF6), 0x00 }, + { CCI_REG8(0x7FF7), 0x01 }, + { CCI_REG8(0x7FF8), 0x00 }, + { CCI_REG8(0x7FF9), 0x78 }, + { CCI_REG8(0x7FFA), 0x00 }, + { CCI_REG8(0x7FFB), 0x00 }, + { CCI_REG8(0x7FFC), 0x00 }, + { CCI_REG8(0x7FFD), 0x00 }, + { CCI_REG8(0x7FFE), 0x00 }, + { CCI_REG8(0x7FFF), 0x03 }, + { CCI_REG8(0x7F76), 0x03 }, + { CCI_REG8(0x7F77), 0xFE }, + { CCI_REG8(0x7FA8), 0x03 }, + { CCI_REG8(0x7FA9), 0xFE }, + { CCI_REG8(0x7B24), 0x81 }, + { CCI_REG8(0x6564), 0x07 }, + { CCI_REG8(0x6B0D), 0x41 }, + { CCI_REG8(0x653D), 0x04 }, + { CCI_REG8(0x6B05), 0x8C }, + { CCI_REG8(0x6B06), 0xF9 }, + { CCI_REG8(0x6B08), 0x65 }, + { CCI_REG8(0x6B09), 0xFC }, + { CCI_REG8(0x6B0A), 0xCF }, + { CCI_REG8(0x6B0B), 0xD2 }, + { CCI_REG8(0x6700), 0x0E }, + { CCI_REG8(0x6707), 0x0E }, + { CCI_REG8(0x9104), 0x00 }, + { CCI_REG8(0x4648), 0x7F }, + { CCI_REG8(0x7420), 0x00 }, + { CCI_REG8(0x7421), 0x1C }, + { CCI_REG8(0x7422), 0x00 }, + { CCI_REG8(0x7423), 0xD7 }, + { CCI_REG8(0x5F04), 0x00 }, + { CCI_REG8(0x5F05), 0xED }, + {IMX258_REG_CSI_DT_FMT, 0x0a0a}, + {IMX258_REG_LINE_LENGTH_PCK, 5352}, + {IMX258_REG_X_ADD_STA, 0}, + {IMX258_REG_Y_ADD_STA, 0}, + {IMX258_REG_X_ADD_END, 4207}, + {IMX258_REG_Y_ADD_END, 3119}, + {IMX258_REG_X_EVN_INC, 1}, + {IMX258_REG_X_ODD_INC, 1}, + {IMX258_REG_Y_EVN_INC, 1}, + {IMX258_REG_Y_ODD_INC, 1}, + {IMX258_REG_DIG_CROP_X_OFFSET, 0}, + {IMX258_REG_DIG_CROP_Y_OFFSET, 0}, + {IMX258_REG_DIG_CROP_IMAGE_WIDTH, 4208}, + {IMX258_REG_SCALE_MODE_EXT, 0}, + {IMX258_REG_SCALE_M_EXT, 16}, + {IMX258_REG_FORCE_FD_SUM, 0}, + {IMX258_REG_FRM_LENGTH_CTL, 0}, + {IMX258_REG_ANALOG_GAIN, 0}, + {IMX258_REG_GR_DIGITAL_GAIN, 256}, + {IMX258_REG_R_DIGITAL_GAIN, 256}, + {IMX258_REG_B_DIGITAL_GAIN, 256}, + {IMX258_REG_GB_DIGITAL_GAIN, 256}, + {IMX258_REG_AF_WINDOW_MODE, 0}, + { CCI_REG8(0x94DC), 0x20 }, + { CCI_REG8(0x94DD), 0x20 }, + { CCI_REG8(0x94DE), 0x20 }, + { CCI_REG8(0x95DC), 0x20 }, + { CCI_REG8(0x95DD), 0x20 }, + { CCI_REG8(0x95DE), 0x20 }, + { CCI_REG8(0x7FB0), 0x00 }, + { CCI_REG8(0x9010), 0x3E }, + { CCI_REG8(0x9419), 0x50 }, + { CCI_REG8(0x941B), 0x50 }, + { CCI_REG8(0x9519), 0x50 }, + { CCI_REG8(0x951B), 0x50 }, + {IMX258_REG_PHASE_PIX_OUTEN, 0}, + {IMX258_REG_PDPIX_DATA_RATE, 0}, + {IMX258_REG_HDR, 0}, }; -static const struct imx258_reg mode_4208x3120_regs[] = { - { 0x0900, 0x00 }, - { 0x0901, 0x11 }, - { 0x0401, 0x00 }, - { 0x0405, 0x10 }, - { 0x040E, 0x0C }, - { 0x040F, 0x30 }, - { 0x034C, 0x10 }, - { 0x034D, 0x70 }, - { 0x034E, 0x0C }, - { 0x034F, 0x30 }, +static const struct cci_reg_sequence mode_4208x3120_regs[] = { + {IMX258_REG_BINNING_MODE, 0}, + {IMX258_REG_BINNING_TYPE_V, 0x11}, + {IMX258_REG_SCALE_MODE, 0}, + {IMX258_REG_SCALE_M, 16}, + {IMX258_REG_DIG_CROP_IMAGE_HEIGHT, 3120}, + {IMX258_REG_X_OUT_SIZE, 4208}, + {IMX258_REG_Y_OUT_SIZE, 3120}, }; -static const struct imx258_reg mode_2104_1560_regs[] = { - { 0x0900, 0x01 }, - { 0x0901, 0x12 }, - { 0x0401, 0x01 }, - { 0x0405, 0x20 }, - { 0x040E, 0x06 }, - { 0x040F, 0x18 }, - { 0x034C, 0x08 }, - { 0x034D, 0x38 }, - { 0x034E, 0x06 }, - { 0x034F, 0x18 }, +static const struct cci_reg_sequence mode_2104_1560_regs[] = { + {IMX258_REG_BINNING_MODE, 1}, + {IMX258_REG_BINNING_TYPE_V, 0x12}, + {IMX258_REG_SCALE_MODE, 1}, + {IMX258_REG_SCALE_M, 32}, + {IMX258_REG_DIG_CROP_IMAGE_HEIGHT, 1560}, + {IMX258_REG_X_OUT_SIZE, 2104}, + {IMX258_REG_Y_OUT_SIZE, 1560}, }; -static const struct imx258_reg mode_1048_780_regs[] = { - { 0x0900, 0x01 }, - { 0x0901, 0x14 }, - { 0x0401, 0x01 }, - { 0x0405, 0x40 }, - { 0x040E, 0x03 }, - { 0x040F, 0x0C }, - { 0x034C, 0x04 }, - { 0x034D, 0x18 }, - { 0x034E, 0x03 }, - { 0x034F, 0x0C }, +static const struct cci_reg_sequence mode_1048_780_regs[] = { + {IMX258_REG_BINNING_MODE, 1}, + {IMX258_REG_BINNING_TYPE_V, 0x14}, + {IMX258_REG_SCALE_MODE, 1}, + {IMX258_REG_SCALE_M, 64}, + {IMX258_REG_DIG_CROP_IMAGE_HEIGHT, 780}, + {IMX258_REG_X_OUT_SIZE, 1048}, + {IMX258_REG_Y_OUT_SIZE, 780}, }; struct imx258_variant_cfg { - const struct imx258_reg *regs; + const struct cci_reg_sequence *regs; unsigned int num_regs; }; -static const struct imx258_reg imx258_cfg_regs[] = { - { 0x3052, 0x00 }, - { 0x4E21, 0x14 }, - { 0x7B25, 0x00 }, +static const struct cci_reg_sequence imx258_cfg_regs[] = { + { CCI_REG8(0x3052), 0x00 }, + { CCI_REG8(0x4E21), 0x14 }, + { CCI_REG8(0x7B25), 0x00 }, }; static const struct imx258_variant_cfg imx258_cfg = { @@ -473,10 +443,10 @@ static const struct imx258_variant_cfg imx258_cfg = { .num_regs = ARRAY_SIZE(imx258_cfg_regs), }; -static const struct imx258_reg imx258_pdaf_cfg_regs[] = { - { 0x3052, 0x01 }, - { 0x4E21, 0x10 }, - { 0x7B25, 0x01 }, +static const struct cci_reg_sequence imx258_pdaf_cfg_regs[] = { + { CCI_REG8(0x3052), 0x01 }, + { CCI_REG8(0x4E21), 0x10 }, + { CCI_REG8(0x7B25), 0x01 }, }; static const struct imx258_variant_cfg imx258_pdaf_cfg = { @@ -677,6 +647,7 @@ static const struct imx258_mode supported_modes[] = { struct imx258 { struct v4l2_subdev sd; struct media_pad pad; + struct regmap *regmap; const struct imx258_variant_cfg *variant_cfg; @@ -714,80 +685,6 @@ static inline struct imx258 *to_imx258(struct v4l2_subdev *_sd) return container_of(_sd, struct imx258, sd); } -/* Read registers up to 2 at a time */ -static int imx258_read_reg(struct imx258 *imx258, u16 reg, u32 len, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); - struct i2c_msg msgs[2]; - u8 addr_buf[2] = { reg >> 8, reg & 0xff }; - u8 data_buf[4] = { 0, }; - int ret; - - if (len > 4) - return -EINVAL; - - /* Write register address */ - msgs[0].addr = client->addr; - msgs[0].flags = 0; - msgs[0].len = ARRAY_SIZE(addr_buf); - msgs[0].buf = addr_buf; - - /* Read data from register */ - msgs[1].addr = client->addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = len; - msgs[1].buf = &data_buf[4 - len]; - - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret != ARRAY_SIZE(msgs)) - return -EIO; - - *val = get_unaligned_be32(data_buf); - - return 0; -} - -/* Write registers up to 2 at a time */ -static int imx258_write_reg(struct imx258 *imx258, u16 reg, u32 len, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); - u8 buf[6]; - - if (len > 4) - return -EINVAL; - - put_unaligned_be16(reg, buf); - put_unaligned_be32(val << (8 * (4 - len)), buf + 2); - if (i2c_master_send(client, buf, len + 2) != len + 2) - return -EIO; - - return 0; -} - -/* Write a list of registers */ -static int imx258_write_regs(struct imx258 *imx258, - const struct imx258_reg *regs, u32 len) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); - unsigned int i; - int ret; - - for (i = 0; i < len; i++) { - ret = imx258_write_reg(imx258, regs[i].address, 1, - regs[i].val); - if (ret) { - dev_err_ratelimited( - &client->dev, - "Failed to write reg 0x%4.4x. error = %d\n", - regs[i].address, ret); - - return ret; - } - } - - return 0; -} - /* Get bayer order based on flip setting. */ static u32 imx258_get_format_code(const struct imx258 *imx258) { @@ -825,31 +722,16 @@ static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) return 0; } -static int imx258_update_digital_gain(struct imx258 *imx258, u32 len, u32 val) +static int imx258_update_digital_gain(struct imx258 *imx258, u32 val) { - int ret; + int ret = 0; - ret = imx258_write_reg(imx258, IMX258_REG_GR_DIGITAL_GAIN, - IMX258_REG_VALUE_16BIT, - val); - if (ret) - return ret; - ret = imx258_write_reg(imx258, IMX258_REG_GB_DIGITAL_GAIN, - IMX258_REG_VALUE_16BIT, - val); - if (ret) - return ret; - ret = imx258_write_reg(imx258, IMX258_REG_R_DIGITAL_GAIN, - IMX258_REG_VALUE_16BIT, - val); - if (ret) - return ret; - ret = imx258_write_reg(imx258, IMX258_REG_B_DIGITAL_GAIN, - IMX258_REG_VALUE_16BIT, - val); - if (ret) - return ret; - return 0; + cci_write(imx258->regmap, IMX258_REG_GR_DIGITAL_GAIN, val, &ret); + cci_write(imx258->regmap, IMX258_REG_GB_DIGITAL_GAIN, val, &ret); + cci_write(imx258->regmap, IMX258_REG_R_DIGITAL_GAIN, val, &ret); + cci_write(imx258->regmap, IMX258_REG_B_DIGITAL_GAIN, val, &ret); + + return ret; } static void imx258_adjust_exposure_range(struct imx258 *imx258) @@ -888,53 +770,45 @@ static int imx258_set_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_ANALOGUE_GAIN: - ret = imx258_write_reg(imx258, IMX258_REG_ANALOG_GAIN, - IMX258_REG_VALUE_16BIT, - ctrl->val); + ret = cci_write(imx258->regmap, IMX258_REG_ANALOG_GAIN, + ctrl->val, NULL); break; case V4L2_CID_EXPOSURE: - ret = imx258_write_reg(imx258, IMX258_REG_EXPOSURE, - IMX258_REG_VALUE_16BIT, - ctrl->val); + ret = cci_write(imx258->regmap, IMX258_REG_EXPOSURE, + ctrl->val, NULL); break; case V4L2_CID_DIGITAL_GAIN: - ret = imx258_update_digital_gain(imx258, IMX258_REG_VALUE_16BIT, - ctrl->val); + ret = imx258_update_digital_gain(imx258, ctrl->val); break; case V4L2_CID_TEST_PATTERN: - ret = imx258_write_reg(imx258, IMX258_REG_TEST_PATTERN, - IMX258_REG_VALUE_16BIT, - ctrl->val); + ret = cci_write(imx258->regmap, IMX258_REG_TEST_PATTERN, + ctrl->val, NULL); break; case V4L2_CID_WIDE_DYNAMIC_RANGE: if (!ctrl->val) { - ret = imx258_write_reg(imx258, IMX258_REG_HDR, - IMX258_REG_VALUE_08BIT, - IMX258_HDR_RATIO_MIN); + ret = cci_write(imx258->regmap, IMX258_REG_HDR, + IMX258_HDR_RATIO_MIN, NULL); } else { - ret = imx258_write_reg(imx258, IMX258_REG_HDR, - IMX258_REG_VALUE_08BIT, - IMX258_HDR_ON); + ret = cci_write(imx258->regmap, IMX258_REG_HDR, + IMX258_HDR_ON, NULL); if (ret) break; - ret = imx258_write_reg(imx258, IMX258_REG_HDR_RATIO, - IMX258_REG_VALUE_08BIT, - BIT(IMX258_HDR_RATIO_MAX)); + ret = cci_write(imx258->regmap, IMX258_REG_HDR_RATIO, + BIT(IMX258_HDR_RATIO_MAX), NULL); } break; case V4L2_CID_VBLANK: - ret = imx258_write_reg(imx258, IMX258_REG_VTS, - IMX258_REG_VALUE_16BIT, - imx258->cur_mode->height + ctrl->val); + ret = cci_write(imx258->regmap, IMX258_REG_FRM_LENGTH_LINES, + imx258->cur_mode->height + ctrl->val, NULL); break; case V4L2_CID_VFLIP: case V4L2_CID_HFLIP: - ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL, - IMX258_REG_VALUE_08BIT, - (imx258->hflip->val ? - REG_CONFIG_MIRROR_HFLIP : 0) | - (imx258->vflip->val ? - REG_CONFIG_MIRROR_VFLIP : 0)); + ret = cci_write(imx258->regmap, REG_MIRROR_FLIP_CONTROL, + (imx258->hflip->val ? + REG_CONFIG_MIRROR_HFLIP : 0) | + (imx258->vflip->val ? + REG_CONFIG_MIRROR_VFLIP : 0), + NULL); break; default: dev_info(&client->dev, @@ -1144,8 +1018,7 @@ static int imx258_start_streaming(struct imx258 *imx258) const struct imx258_link_freq_config *link_freq_cfg; int ret, link_freq_index; - ret = imx258_write_reg(imx258, IMX258_REG_RESET, IMX258_REG_VALUE_08BIT, - 0x01); + ret = cci_write(imx258->regmap, IMX258_REG_RESET, 0x01, NULL); if (ret) { dev_err(&client->dev, "%s failed to reset sensor\n", __func__); return ret; @@ -1159,30 +1032,30 @@ static int imx258_start_streaming(struct imx258 *imx258) link_freq_cfg = &imx258->link_freq_configs[link_freq_index]; reg_list = &link_freq_cfg->link_cfg[imx258->lane_mode_idx].reg_list; - ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs); + ret = cci_multi_reg_write(imx258->regmap, reg_list->regs, reg_list->num_of_regs, NULL); if (ret) { dev_err(&client->dev, "%s failed to set plls\n", __func__); return ret; } - ret = imx258_write_regs(imx258, mode_common_regs, - ARRAY_SIZE(mode_common_regs)); + ret = cci_multi_reg_write(imx258->regmap, mode_common_regs, + ARRAY_SIZE(mode_common_regs), NULL); if (ret) { dev_err(&client->dev, "%s failed to set common regs\n", __func__); return ret; } - ret = imx258_write_regs(imx258, imx258->variant_cfg->regs, - imx258->variant_cfg->num_regs); + ret = cci_multi_reg_write(imx258->regmap, imx258->variant_cfg->regs, + imx258->variant_cfg->num_regs, NULL); if (ret) { dev_err(&client->dev, "%s failed to set variant config\n", __func__); return ret; } - ret = imx258_write_reg(imx258, IMX258_CLK_BLANK_STOP, - IMX258_REG_VALUE_08BIT, - !!(imx258->csi2_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)); + ret = cci_write(imx258->regmap, IMX258_CLK_BLANK_STOP, + !!(imx258->csi2_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK), + NULL); if (ret) { dev_err(&client->dev, "%s failed to set clock lane mode\n", __func__); return ret; @@ -1190,7 +1063,7 @@ static int imx258_start_streaming(struct imx258 *imx258) /* Apply default values of current mode */ reg_list = &imx258->cur_mode->reg_list; - ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs); + ret = cci_multi_reg_write(imx258->regmap, reg_list->regs, reg_list->num_of_regs, NULL); if (ret) { dev_err(&client->dev, "%s failed to set mode\n", __func__); return ret; @@ -1202,9 +1075,8 @@ static int imx258_start_streaming(struct imx258 *imx258) return ret; /* set stream on register */ - return imx258_write_reg(imx258, IMX258_REG_MODE_SELECT, - IMX258_REG_VALUE_08BIT, - IMX258_MODE_STREAMING); + return cci_write(imx258->regmap, IMX258_REG_MODE_SELECT, + IMX258_MODE_STREAMING, NULL); } /* Stop streaming */ @@ -1214,8 +1086,8 @@ static int imx258_stop_streaming(struct imx258 *imx258) int ret; /* set stream off register */ - ret = imx258_write_reg(imx258, IMX258_REG_MODE_SELECT, - IMX258_REG_VALUE_08BIT, IMX258_MODE_STANDBY); + ret = cci_write(imx258->regmap, IMX258_REG_MODE_SELECT, + IMX258_MODE_STANDBY, NULL); if (ret) dev_err(&client->dev, "%s failed to set stream\n", __func__); @@ -1302,10 +1174,10 @@ static int imx258_identify_module(struct imx258 *imx258) { struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); int ret; - u32 val; + u64 val; - ret = imx258_read_reg(imx258, IMX258_REG_CHIP_ID, - IMX258_REG_VALUE_16BIT, &val); + ret = cci_read(imx258->regmap, IMX258_REG_CHIP_ID, + &val, NULL); if (ret) { dev_err(&client->dev, "failed to read chip id %x\n", IMX258_CHIP_ID); @@ -1313,7 +1185,7 @@ static int imx258_identify_module(struct imx258 *imx258) } if (val != IMX258_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x\n", + dev_err(&client->dev, "chip id mismatch: %x!=%llx\n", IMX258_CHIP_ID, val); return -EIO; } @@ -1493,6 +1365,13 @@ static int imx258_probe(struct i2c_client *client) if (!imx258) return -ENOMEM; + imx258->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(imx258->regmap)) { + ret = PTR_ERR(imx258->regmap); + dev_err(&client->dev, "failed to initialize CCI: %d\n", ret); + return ret; + } + ret = imx258_get_regulators(imx258, client); if (ret) return dev_err_probe(&client->dev, ret, -- cgit v1.2.3 From 7d30b8efae814bc73541720ca188dc205cd7d2b8 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 20 May 2024 10:28:20 +0300 Subject: media: async: Warn on attept to create an ancillary link to a non-subdev Warn if a lens or a flash async sub-device is bound to a non-sub-device notifier. This isn't meaningful and unlikely to happen in practice but print a warning as we nevertheless won't consider this an error. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-async.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index c477723c07bf..81a9b5473969 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -323,8 +323,12 @@ static int v4l2_async_create_ancillary_links(struct v4l2_async_notifier *n, sd->entity.function != MEDIA_ENT_F_FLASH) return 0; - if (!n->sd) + if (!n->sd) { + dev_warn(notifier_dev(n), + "not a sub-device notifier, not creating an ancillary link for %s!\n", + dev_name(sd->dev)); return 0; + } link = media_create_ancillary_link(&n->sd->entity, &sd->entity); -- cgit v1.2.3 From ce44dc1feab5f038edb39dc443b32eae3ac1234d Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Fri, 10 May 2024 17:56:06 +0200 Subject: media: staging: max96712: Store format in subdev active state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create and store the subdevice format in the subdevices active state. This change do not have a huge effect on the driver as it do not yet support changing the format. Signed-off-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/staging/media/max96712/max96712.c | 37 +++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/drivers/staging/media/max96712/max96712.c b/drivers/staging/media/max96712/max96712.c index ea67bcf69c9d..6bdbccbee05a 100644 --- a/drivers/staging/media/max96712/max96712.c +++ b/drivers/staging/media/max96712/max96712.c @@ -242,21 +242,34 @@ static const struct v4l2_subdev_video_ops max96712_video_ops = { .s_stream = max96712_s_stream, }; -static int max96712_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) +static int max96712_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { - format->format.width = 1920; - format->format.height = 1080; - format->format.code = MEDIA_BUS_FMT_RGB888_1X24; - format->format.field = V4L2_FIELD_NONE; + static const struct v4l2_mbus_framefmt default_fmt = { + .width = 1920, + .height = 1080, + .code = MEDIA_BUS_FMT_RGB888_1X24, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, + .quantization = V4L2_QUANTIZATION_DEFAULT, + .xfer_func = V4L2_XFER_FUNC_DEFAULT, + }; + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(state, 0); + *fmt = default_fmt; return 0; } +static const struct v4l2_subdev_internal_ops max96712_internal_ops = { + .init_state = max96712_init_state, +}; + static const struct v4l2_subdev_pad_ops max96712_pad_ops = { - .get_fmt = max96712_get_pad_format, - .set_fmt = max96712_get_pad_format, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = v4l2_subdev_get_fmt, }; static const struct v4l2_subdev_ops max96712_subdev_ops = { @@ -293,6 +306,7 @@ static int max96712_v4l2_register(struct max96712_priv *priv) long pixel_rate; int ret; + priv->sd.internal_ops = &max96712_internal_ops; v4l2_i2c_subdev_init(&priv->sd, priv->client, &max96712_subdev_ops); priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; @@ -324,6 +338,11 @@ static int max96712_v4l2_register(struct max96712_priv *priv) v4l2_set_subdevdata(&priv->sd, priv); + priv->sd.state_lock = priv->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&priv->sd); + if (ret) + goto error; + ret = v4l2_async_register_subdev(&priv->sd); if (ret < 0) { dev_err(&priv->client->dev, "Unable to register subdevice\n"); -- cgit v1.2.3 From 3a5c59ad926b8944f7ef0fd4bda5b90ca325ede5 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 30 May 2024 13:38:07 +0300 Subject: media: ipu6: Rework CSI-2 sub-device streaming control CSI-2 sub-device streaming control should use {enable,disable}_streams pad ops and not s_stream video ops as the sub-device supports streams. Fix this by removing driver-implemented stream management and moving sensor streaming control to the CSI-2 sub-device sub-driver. Fixes: a11a5570a09d ("media: intel/ipu6: add IPU6 CSI2 receiver v4l2 sub-device") Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c | 98 +++++++++++--------------- drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h | 2 - drivers/media/pci/intel/ipu6/ipu6-isys-queue.c | 3 - drivers/media/pci/intel/ipu6/ipu6-isys-video.c | 43 ++--------- 4 files changed, 49 insertions(+), 97 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c index b9ce4324996d..051898ce53f4 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c @@ -345,42 +345,61 @@ static int ipu6_isys_csi2_set_stream(struct v4l2_subdev *sd, return ret; } -static int set_stream(struct v4l2_subdev *sd, int enable) +static int ipu6_isys_csi2_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) { struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd); struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd); - struct device *dev = &csi2->isys->adev->auxdev.dev; struct ipu6_isys_csi2_timing timing = { }; - unsigned int nlanes; + struct v4l2_subdev *remote_sd; + struct media_pad *remote_pad; + u64 sink_streams; int ret; - dev_dbg(dev, "csi2 stream %s callback\n", enable ? "on" : "off"); - - if (!enable) { - csi2->stream_count--; - if (csi2->stream_count) - return 0; - - ipu6_isys_csi2_set_stream(sd, &timing, 0, enable); - return 0; - } - - if (csi2->stream_count) { - csi2->stream_count++; - return 0; - } + remote_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]); + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); - nlanes = csi2->nlanes; + sink_streams = v4l2_subdev_state_xlate_streams(state, CSI2_PAD_SRC, + CSI2_PAD_SINK, + &streams_mask); ret = ipu6_isys_csi2_calc_timing(csi2, &timing, CSI2_ACCINV); if (ret) return ret; - ret = ipu6_isys_csi2_set_stream(sd, &timing, nlanes, enable); + ret = ipu6_isys_csi2_set_stream(sd, &timing, csi2->nlanes, true); if (ret) return ret; - csi2->stream_count++; + ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index, + sink_streams); + if (ret) { + ipu6_isys_csi2_set_stream(sd, NULL, 0, false); + return ret; + } + + return 0; +} + +static int ipu6_isys_csi2_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct v4l2_subdev *remote_sd; + struct media_pad *remote_pad; + u64 sink_streams; + + sink_streams = v4l2_subdev_state_xlate_streams(state, CSI2_PAD_SRC, + CSI2_PAD_SINK, + &streams_mask); + + remote_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]); + remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); + + ipu6_isys_csi2_set_stream(sd, NULL, 0, false); + + v4l2_subdev_disable_streams(remote_sd, remote_pad->index, sink_streams); return 0; } @@ -475,10 +494,6 @@ static int ipu6_isys_csi2_get_sel(struct v4l2_subdev *sd, return ret; } -static const struct v4l2_subdev_video_ops csi2_sd_video_ops = { - .s_stream = set_stream, -}; - static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = { .get_fmt = v4l2_subdev_get_fmt, .set_fmt = ipu6_isys_subdev_set_fmt, @@ -486,11 +501,12 @@ static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = { .set_selection = ipu6_isys_csi2_set_sel, .enum_mbus_code = ipu6_isys_subdev_enum_mbus_code, .set_routing = ipu6_isys_subdev_set_routing, + .enable_streams = ipu6_isys_csi2_enable_streams, + .disable_streams = ipu6_isys_csi2_disable_streams, }; static const struct v4l2_subdev_ops csi2_sd_ops = { .core = &csi2_sd_core_ops, - .video = &csi2_sd_video_ops, .pad = &csi2_sd_pad_ops, }; @@ -631,33 +647,3 @@ int ipu6_isys_csi2_get_remote_desc(u32 source_stream, return 0; } - -void ipu6_isys_set_csi2_streams_status(struct ipu6_isys_video *av, bool status) -{ - struct ipu6_isys_stream *stream = av->stream; - struct v4l2_subdev *sd = &stream->asd->sd; - struct v4l2_subdev_state *state; - struct media_pad *r_pad; - unsigned int i; - u32 r_stream; - - r_pad = media_pad_remote_pad_first(&av->pad); - r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index); - - state = v4l2_subdev_lock_and_get_active_state(sd); - - for (i = 0; i < state->stream_configs.num_configs; i++) { - struct v4l2_subdev_stream_config *cfg = - &state->stream_configs.configs[i]; - - if (cfg->pad == r_pad->index && r_stream == cfg->stream) { - dev_dbg(&av->isys->adev->auxdev.dev, - "%s: pad:%u, stream:%u, status:%u\n", - sd->entity.name, r_pad->index, r_stream, - status); - cfg->enabled = status; - } - } - - v4l2_subdev_unlock_state(state); -} diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h index eba6b29386ea..bc8594c94f99 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h @@ -45,7 +45,6 @@ struct ipu6_isys_csi2 { u32 receiver_errors; unsigned int nlanes; unsigned int port; - unsigned int stream_count; }; struct ipu6_isys_csi2_timing { @@ -77,6 +76,5 @@ int ipu6_isys_csi2_get_remote_desc(u32 source_stream, struct ipu6_isys_csi2 *csi2, struct media_entity *source_entity, struct v4l2_mbus_frame_desc_entry *entry); -void ipu6_isys_set_csi2_streams_status(struct ipu6_isys_video *av, bool status); #endif /* IPU6_ISYS_CSI2_H */ diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c index 40a8ebfcfce2..6bea7754875d 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c @@ -551,7 +551,6 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) stream->nr_queues); list_add(&aq->node, &stream->queues); - ipu6_isys_set_csi2_streams_status(av, true); ipu6_isys_configure_stream_watermark(av, true); ipu6_isys_update_stream_watermark(av, true); @@ -598,8 +597,6 @@ static void stop_streaming(struct vb2_queue *q) struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq); struct ipu6_isys_stream *stream = av->stream; - ipu6_isys_set_csi2_streams_status(av, false); - mutex_lock(&stream->mutex); ipu6_isys_update_stream_watermark(av, false); diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c index c8a33e1e910c..e41d40243abd 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c @@ -990,9 +990,7 @@ int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, struct v4l2_subdev_state *subdev_state; struct device *dev = &av->isys->adev->auxdev.dev; struct v4l2_subdev *sd; - struct v4l2_subdev *ssd; struct media_pad *r_pad; - struct media_pad *s_pad; u32 sink_pad, sink_stream; u64 r_stream; u64 stream_mask = 0; @@ -1003,7 +1001,6 @@ int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, if (WARN(!stream->source_entity, "No source entity for stream\n")) return -ENODEV; - ssd = media_entity_to_v4l2_subdev(stream->source_entity); sd = &stream->asd->sd; r_pad = media_pad_remote_pad_first(&av->pad); r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index); @@ -1017,27 +1014,15 @@ int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, if (ret) return ret; - s_pad = media_pad_remote_pad_first(&stream->asd->pad[sink_pad]); - stream_mask = get_stream_mask_by_pipeline(av); if (!state) { stop_streaming_firmware(av); - /* stop external sub-device now. */ - dev_dbg(dev, "disable streams 0x%llx of %s\n", stream_mask, - ssd->name); - ret = v4l2_subdev_disable_streams(ssd, s_pad->index, - stream_mask); - if (ret) { - dev_err(dev, "disable streams of %s failed with %d\n", - ssd->name, ret); - return ret; - } - /* stop sub-device which connects with video */ - dev_dbg(dev, "stream off entity %s pad:%d\n", sd->name, - r_pad->index); - ret = v4l2_subdev_call(sd, video, s_stream, state); + dev_dbg(dev, "stream off entity %s pad:%d mask:0x%llx\n", + sd->name, r_pad->index, stream_mask); + ret = v4l2_subdev_disable_streams(sd, r_pad->index, + stream_mask); if (ret) { dev_err(dev, "stream off %s failed with %d\n", sd->name, ret); @@ -1052,34 +1037,20 @@ int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state, } /* start sub-device which connects with video */ - dev_dbg(dev, "stream on %s pad %d\n", sd->name, r_pad->index); - ret = v4l2_subdev_call(sd, video, s_stream, state); + dev_dbg(dev, "stream on %s pad %d mask 0x%llx\n", sd->name, + r_pad->index, stream_mask); + ret = v4l2_subdev_enable_streams(sd, r_pad->index, stream_mask); if (ret) { dev_err(dev, "stream on %s failed with %d\n", sd->name, ret); goto out_media_entity_stop_streaming_firmware; } - - /* start external sub-device now. */ - dev_dbg(dev, "enable streams 0x%llx of %s\n", stream_mask, - ssd->name); - ret = v4l2_subdev_enable_streams(ssd, s_pad->index, - stream_mask); - if (ret) { - dev_err(dev, - "enable streams 0x%llx of %s failed with %d\n", - stream_mask, stream->source_entity->name, ret); - goto out_media_entity_stop_streaming; - } } av->streaming = state; return 0; -out_media_entity_stop_streaming: - v4l2_subdev_disable_streams(sd, r_pad->index, BIT(r_stream)); - out_media_entity_stop_streaming_firmware: stop_streaming_firmware(av); -- cgit v1.2.3 From 4e628f95e743c8fd37b5daef87b05681d724137e Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:04 +0300 Subject: media: subdev: Add privacy led helpers Add helper functions to enable and disable the privacy led. This moves the #if from the call site to the privacy led functions, and makes adding privacy led support to v4l2_subdev_enable/disable_streams() cleaner. Reviewed-by: Umang Jain Reviewed-by: Laurent Pinchart Tested-by: Umang Jain Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-subdev.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 8470d6eda9a3..2fa4a48dad20 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -148,6 +148,23 @@ static int subdev_close(struct file *file) } #endif /* CONFIG_VIDEO_V4L2_SUBDEV_API */ +static void v4l2_subdev_enable_privacy_led(struct v4l2_subdev *sd) +{ +#if IS_REACHABLE(CONFIG_LEDS_CLASS) + if (!IS_ERR_OR_NULL(sd->privacy_led)) + led_set_brightness(sd->privacy_led, + sd->privacy_led->max_brightness); +#endif +} + +static void v4l2_subdev_disable_privacy_led(struct v4l2_subdev *sd) +{ +#if IS_REACHABLE(CONFIG_LEDS_CLASS) + if (!IS_ERR_OR_NULL(sd->privacy_led)) + led_set_brightness(sd->privacy_led, 0); +#endif +} + static inline int check_which(u32 which) { if (which != V4L2_SUBDEV_FORMAT_TRY && @@ -452,15 +469,10 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable) if (!ret) { sd->enabled_streams = enable ? BIT(0) : 0; -#if IS_REACHABLE(CONFIG_LEDS_CLASS) - if (!IS_ERR_OR_NULL(sd->privacy_led)) { - if (enable) - led_set_brightness(sd->privacy_led, - sd->privacy_led->max_brightness); - else - led_set_brightness(sd->privacy_led, 0); - } -#endif + if (enable) + v4l2_subdev_enable_privacy_led(sd); + else + v4l2_subdev_disable_privacy_led(sd); } return ret; -- cgit v1.2.3 From 68e36241b74683e36533b0089966881d0b7d866b Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:05 +0300 Subject: media: subdev: Use v4l2_subdev_has_op() in v4l2_subdev_enable/disable_streams() Use v4l2_subdev_has_op() in v4l2_subdev_enable/disable_streams() instead of open coding the same. Reviewed-by: Umang Jain Reviewed-by: Laurent Pinchart Tested-by: Umang Jain Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-subdev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 2fa4a48dad20..56df3906eee6 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -2188,7 +2188,7 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, return 0; /* Fallback on .s_stream() if .enable_streams() isn't available. */ - if (!sd->ops->pad || !sd->ops->pad->enable_streams) + if (!v4l2_subdev_has_op(sd, pad, enable_streams)) return v4l2_subdev_enable_streams_fallback(sd, pad, streams_mask); @@ -2305,7 +2305,7 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, return 0; /* Fallback on .s_stream() if .disable_streams() isn't available. */ - if (!sd->ops->pad || !sd->ops->pad->disable_streams) + if (!v4l2_subdev_has_op(sd, pad, disable_streams)) return v4l2_subdev_disable_streams_fallback(sd, pad, streams_mask); -- cgit v1.2.3 From e003fd9c1e3883a96451d24174dc5bad2c248341 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:06 +0300 Subject: media: subdev: Add checks for subdev features Add some checks to verify that the subdev driver implements required features. A subdevice that supports streams (V4L2_SUBDEV_FL_STREAMS) must do one of the following: - Implement neither .enable/disable_streams() nor .s_stream(), if the subdev is part of a video driver that uses an internal method to enable the subdev. - Implement only .enable/disable_streams(), if support for legacy sink-side subdevices is not needed. - Implement .enable/disable_streams() and .s_stream(), if support for legacy sink-side subdevices is needed. At the moment the framework doesn't check this requirement. Add the check. Reviewed-by: Umang Jain Reviewed-by: Laurent Pinchart Tested-by: Umang Jain Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-subdev.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 56df3906eee6..09b4122d9ab7 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1588,6 +1588,33 @@ int __v4l2_subdev_init_finalize(struct v4l2_subdev *sd, const char *name, struct lock_class_key *key) { struct v4l2_subdev_state *state; + struct device *dev = sd->dev; + bool has_disable_streams; + bool has_enable_streams; + bool has_s_stream; + + /* Check that the subdevice implements the required features */ + + has_s_stream = v4l2_subdev_has_op(sd, video, s_stream); + has_enable_streams = v4l2_subdev_has_op(sd, pad, enable_streams); + has_disable_streams = v4l2_subdev_has_op(sd, pad, disable_streams); + + if (has_enable_streams != has_disable_streams) { + dev_err(dev, + "subdev '%s' must implement both or neither of .enable_streams() and .disable_streams()\n", + sd->name); + return -EINVAL; + } + + if (sd->flags & V4L2_SUBDEV_FL_STREAMS) { + if (has_s_stream && !has_enable_streams) { + dev_err(dev, + "subdev '%s' must implement .enable/disable_streams()\n", + sd->name); + + return -EINVAL; + } + } state = __v4l2_subdev_state_alloc(sd, name, key); if (IS_ERR(state)) -- cgit v1.2.3 From 1d7804281df3f09f0a109d00406e859a00bae7ae Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:07 +0300 Subject: media: subdev: Fix use of sd->enabled_streams in call_s_stream() call_s_stream() uses sd->enabled_streams to track whether streaming has already been enabled. However, v4l2_subdev_enable/disable_streams_fallback(), which was the original user of this field, already uses it, and v4l2_subdev_enable/disable_streams_fallback() will call call_s_stream(). This leads to a conflict as both functions set the field. Afaics, both functions set the field to the same value, so it won't cause a runtime bug, but it's still wrong and if we, e.g., change how v4l2_subdev_enable/disable_streams_fallback() operates we might easily cause bugs. Fix this by adding a new field, 's_stream_enabled', for call_s_stream(). Reviewed-by: Umang Jain Reviewed-by: Laurent Pinchart Tested-by: Umang Jain Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-subdev.c | 8 ++------ include/media/v4l2-subdev.h | 3 +++ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 09b4122d9ab7..8675ad0688af 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -451,12 +451,8 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable) * The .s_stream() operation must never be called to start or stop an * already started or stopped subdev. Catch offenders but don't return * an error yet to avoid regressions. - * - * As .s_stream() is mutually exclusive with the .enable_streams() and - * .disable_streams() operation, we can use the enabled_streams field - * to store the subdev streaming state. */ - if (WARN_ON(!!sd->enabled_streams == !!enable)) + if (WARN_ON(sd->s_stream_enabled == !!enable)) return 0; ret = sd->ops->video->s_stream(sd, enable); @@ -467,7 +463,7 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable) } if (!ret) { - sd->enabled_streams = enable ? BIT(0) : 0; + sd->s_stream_enabled = enable; if (enable) v4l2_subdev_enable_privacy_led(sd); diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index e30c463d90e5..d3f15882927b 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1045,6 +1045,8 @@ struct v4l2_subdev_platform_data { * v4l2_subdev_enable_streams() and * v4l2_subdev_disable_streams() helper functions for fallback * cases. + * @s_stream_enabled: Tracks whether streaming has been enabled with s_stream. + * This is only for call_s_stream() internal use. * * Each instance of a subdev driver should create this struct, either * stand-alone or embedded in a larger struct. @@ -1093,6 +1095,7 @@ struct v4l2_subdev { */ struct v4l2_subdev_state *active_state; u64 enabled_streams; + bool s_stream_enabled; }; -- cgit v1.2.3 From 61d6c8c896c1ccde350c281817847a32b0c6b83b Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:08 +0300 Subject: media: subdev: Improve v4l2_subdev_enable/disable_streams_fallback v4l2_subdev_enable/disable_streams_fallback() supports falling back to .s_stream() for subdevs with a single source pad. It also tracks the enabled streams for that one pad in the sd->enabled_streams field. Tracking the enabled streams with sd->enabled_streams does not make sense, as with .s_stream() there can only be a single stream per pad. Thus, as the v4l2_subdev_enable/disable_streams_fallback() only supports a single source pad, all we really need is a boolean which tells whether streaming has been enabled on this pad or not. However, as we only need a true/false state for a pad (instead of tracking which streams have been enabled for a pad), we can easily extend the fallback mechanism to support multiple source pads as we only need to keep track of which pads have been enabled. Change the sd->enabled_streams field to sd->enabled_pads, which is a 64-bit bitmask tracking the enabled source pads. With this change we can remove the restriction that v4l2_subdev_enable/disable_streams_fallback() only supports a single source pad. Reviewed-by: Laurent Pinchart Tested-by: Umang Jain Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-subdev.c | 68 ++++++++++++++++++++--------------- include/media/v4l2-subdev.h | 9 +++-- 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 8675ad0688af..0231acf7168e 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -2159,37 +2159,43 @@ static int v4l2_subdev_enable_streams_fallback(struct v4l2_subdev *sd, u32 pad, u64 streams_mask) { struct device *dev = sd->entity.graph_obj.mdev->dev; - unsigned int i; int ret; /* * The subdev doesn't implement pad-based stream enable, fall back - * on the .s_stream() operation. This can only be done for subdevs that - * have a single source pad, as sd->enabled_streams is global to the - * subdev. + * to the .s_stream() operation. */ if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) return -EOPNOTSUPP; - for (i = 0; i < sd->entity.num_pads; ++i) { - if (i != pad && sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) - return -EOPNOTSUPP; - } + /* + * .s_stream() means there is no streams support, so the only allowed + * stream is the implicit stream 0. + */ + if (streams_mask != BIT_ULL(0)) + return -EOPNOTSUPP; + + /* + * We use a 64-bit bitmask for tracking enabled pads, so only subdevices + * with 64 pads or less can be supported. + */ + if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE) + return -EOPNOTSUPP; - if (sd->enabled_streams & streams_mask) { - dev_dbg(dev, "set of streams %#llx already enabled on %s:%u\n", - streams_mask, sd->entity.name, pad); + if (sd->enabled_pads & BIT_ULL(pad)) { + dev_dbg(dev, "pad %u already enabled on %s\n", + pad, sd->entity.name); return -EALREADY; } - /* Start streaming when the first streams are enabled. */ - if (!sd->enabled_streams) { + /* Start streaming when the first pad is enabled. */ + if (!sd->enabled_pads) { ret = v4l2_subdev_call(sd, video, s_stream, 1); if (ret) return ret; } - sd->enabled_streams |= streams_mask; + sd->enabled_pads |= BIT_ULL(pad); return 0; } @@ -2276,37 +2282,43 @@ static int v4l2_subdev_disable_streams_fallback(struct v4l2_subdev *sd, u32 pad, u64 streams_mask) { struct device *dev = sd->entity.graph_obj.mdev->dev; - unsigned int i; int ret; /* - * If the subdev doesn't implement pad-based stream enable, fall back - * on the .s_stream() operation. This can only be done for subdevs that - * have a single source pad, as sd->enabled_streams is global to the - * subdev. + * If the subdev doesn't implement pad-based stream enable, fall back + * to the .s_stream() operation. */ if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) return -EOPNOTSUPP; - for (i = 0; i < sd->entity.num_pads; ++i) { - if (i != pad && sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) - return -EOPNOTSUPP; - } + /* + * .s_stream() means there is no streams support, so the only allowed + * stream is the implicit stream 0. + */ + if (streams_mask != BIT_ULL(0)) + return -EOPNOTSUPP; + + /* + * We use a 64-bit bitmask for tracking enabled pads, so only subdevices + * with 64 pads or less can be supported. + */ + if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE) + return -EOPNOTSUPP; - if ((sd->enabled_streams & streams_mask) != streams_mask) { - dev_dbg(dev, "set of streams %#llx already disabled on %s:%u\n", - streams_mask, sd->entity.name, pad); + if (!(sd->enabled_pads & BIT_ULL(pad))) { + dev_dbg(dev, "pad %u already disabled on %s\n", + pad, sd->entity.name); return -EALREADY; } /* Stop streaming when the last streams are disabled. */ - if (!(sd->enabled_streams & ~streams_mask)) { + if (!(sd->enabled_pads & ~BIT_ULL(pad))) { ret = v4l2_subdev_call(sd, video, s_stream, 0); if (ret) return ret; } - sd->enabled_streams &= ~streams_mask; + sd->enabled_pads &= ~BIT_ULL(pad); return 0; } diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index d3f15882927b..f191cf00c840 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1041,10 +1041,9 @@ struct v4l2_subdev_platform_data { * @active_state: Active state for the subdev (NULL for subdevs tracking the * state internally). Initialized by calling * v4l2_subdev_init_finalize(). - * @enabled_streams: Bitmask of enabled streams used by - * v4l2_subdev_enable_streams() and - * v4l2_subdev_disable_streams() helper functions for fallback - * cases. + * @enabled_pads: Bitmask of enabled pads used by v4l2_subdev_enable_streams() + * and v4l2_subdev_disable_streams() helper functions for + * fallback cases. * @s_stream_enabled: Tracks whether streaming has been enabled with s_stream. * This is only for call_s_stream() internal use. * @@ -1094,7 +1093,7 @@ struct v4l2_subdev { * doesn't support it. */ struct v4l2_subdev_state *active_state; - u64 enabled_streams; + u64 enabled_pads; bool s_stream_enabled; }; -- cgit v1.2.3 From 5f3ce14fae742d1d23061c3122d93edb879ebf53 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:09 +0300 Subject: media: subdev: Add v4l2_subdev_is_streaming() Add a helper function which returns whether the subdevice is streaming, i.e. if .s_stream or .enable_streams has been called successfully. Reviewed-by: Umang Jain Reviewed-by: Laurent Pinchart Tested-by: Umang Jain Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-subdev.c | 25 +++++++++++++++++++++++++ include/media/v4l2-subdev.h | 13 +++++++++++++ 2 files changed, 38 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 0231acf7168e..f256d65cf90f 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -2474,6 +2474,31 @@ void v4l2_subdev_notify_event(struct v4l2_subdev *sd, } EXPORT_SYMBOL_GPL(v4l2_subdev_notify_event); +bool v4l2_subdev_is_streaming(struct v4l2_subdev *sd) +{ + struct v4l2_subdev_state *state; + + if (!v4l2_subdev_has_op(sd, pad, enable_streams)) + return sd->s_stream_enabled; + + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) + return !!sd->enabled_pads; + + state = v4l2_subdev_get_locked_active_state(sd); + + for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) { + const struct v4l2_subdev_stream_config *cfg; + + cfg = &state->stream_configs.configs[i]; + + if (cfg->enabled) + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_is_streaming); + int v4l2_subdev_get_privacy_led(struct v4l2_subdev *sd) { #if IS_REACHABLE(CONFIG_LEDS_CLASS) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index f191cf00c840..23c970d09870 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1956,4 +1956,17 @@ extern const struct v4l2_subdev_ops v4l2_subdev_call_wrappers; void v4l2_subdev_notify_event(struct v4l2_subdev *sd, const struct v4l2_event *ev); +/** + * v4l2_subdev_is_streaming() - Returns if the subdevice is streaming + * @sd: The subdevice + * + * v4l2_subdev_is_streaming() tells if the subdevice is currently streaming. + * "Streaming" here means whether .s_stream() or .enable_streams() has been + * successfully called, and the streaming has not yet been disabled. + * + * If the subdevice implements .enable_streams() this function must be called + * while holding the active state lock. + */ +bool v4l2_subdev_is_streaming(struct v4l2_subdev *sd); + #endif /* _V4L2_SUBDEV_H */ -- cgit v1.2.3 From 585d8fd5ebb9a946dc5c4931e77f6f6cefbdd501 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:10 +0300 Subject: media: subdev: Support privacy led in v4l2_subdev_enable/disable_streams() We support camera privacy leds with the .s_stream() operation, in call_s_stream(), but we don't have that support when the subdevice implements .enable/disable_streams() operations. Add the support by enabling the led when the first stream for a subdevice is enabled, and disabling the led then the last stream is disabled. Reviewed-by: Umang Jain Reviewed-by: Laurent Pinchart Tested-by: Umang Jain Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-subdev.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index f256d65cf90f..daf3636c5aab 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -2205,6 +2205,7 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, { struct device *dev = sd->entity.graph_obj.mdev->dev; struct v4l2_subdev_state *state; + bool already_streaming; u64 found_streams = 0; unsigned int i; int ret; @@ -2253,6 +2254,8 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, dev_dbg(dev, "enable streams %u:%#llx\n", pad, streams_mask); + already_streaming = v4l2_subdev_is_streaming(sd); + /* Call the .enable_streams() operation. */ ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad, streams_mask); @@ -2271,6 +2274,9 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, cfg->enabled = true; } + if (!already_streaming) + v4l2_subdev_enable_privacy_led(sd); + done: v4l2_subdev_unlock_state(state); @@ -2395,6 +2401,9 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, } done: + if (!v4l2_subdev_is_streaming(sd)) + v4l2_subdev_disable_privacy_led(sd); + v4l2_subdev_unlock_state(state); return ret; -- cgit v1.2.3 From 86862307606b9f07d36097f7c9f018ad04f36ce0 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:11 +0300 Subject: media: subdev: Refactor v4l2_subdev_enable/disable_streams() Add two internal helper functions, v4l2_subdev_collect_streams() and v4l2_subdev_set_streams_enabled(), which allows us to refactor v4l2_subdev_enable/disable_streams() functions. This (I think) makes the code a bit easier to read, and lets us more easily add new functionality in the helper functions in the following patch. Reviewed-by: Laurent Pinchart Tested-by: Umang Jain Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-subdev.c | 109 +++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 49 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index daf3636c5aab..cad7e2479f2a 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -2155,6 +2155,42 @@ out: } EXPORT_SYMBOL_GPL(v4l2_subdev_routing_validate); +static void v4l2_subdev_collect_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask, + u64 *found_streams, + u64 *enabled_streams) +{ + *found_streams = 0; + *enabled_streams = 0; + + for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) { + const struct v4l2_subdev_stream_config *cfg = + &state->stream_configs.configs[i]; + + if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream))) + continue; + + *found_streams |= BIT_ULL(cfg->stream); + if (cfg->enabled) + *enabled_streams |= BIT_ULL(cfg->stream); + } +} + +static void v4l2_subdev_set_streams_enabled(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask, + bool enabled) +{ + for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) { + struct v4l2_subdev_stream_config *cfg = + &state->stream_configs.configs[i]; + + if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream))) + cfg->enabled = enabled; + } +} + static int v4l2_subdev_enable_streams_fallback(struct v4l2_subdev *sd, u32 pad, u64 streams_mask) { @@ -2206,8 +2242,8 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, struct device *dev = sd->entity.graph_obj.mdev->dev; struct v4l2_subdev_state *state; bool already_streaming; - u64 found_streams = 0; - unsigned int i; + u64 enabled_streams; + u64 found_streams; int ret; /* A few basic sanity checks first. */ @@ -2228,22 +2264,9 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, * Verify that the requested streams exist and that they are not * already enabled. */ - for (i = 0; i < state->stream_configs.num_configs; ++i) { - struct v4l2_subdev_stream_config *cfg = - &state->stream_configs.configs[i]; - if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream))) - continue; - - found_streams |= BIT_ULL(cfg->stream); - - if (cfg->enabled) { - dev_dbg(dev, "stream %u already enabled on %s:%u\n", - cfg->stream, sd->entity.name, pad); - ret = -EALREADY; - goto done; - } - } + v4l2_subdev_collect_streams(sd, state, pad, streams_mask, + &found_streams, &enabled_streams); if (found_streams != streams_mask) { dev_dbg(dev, "streams 0x%llx not found on %s:%u\n", @@ -2252,6 +2275,13 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, goto done; } + if (enabled_streams) { + dev_dbg(dev, "streams 0x%llx already enabled on %s:%u\n", + enabled_streams, sd->entity.name, pad); + ret = -EALREADY; + goto done; + } + dev_dbg(dev, "enable streams %u:%#llx\n", pad, streams_mask); already_streaming = v4l2_subdev_is_streaming(sd); @@ -2266,13 +2296,7 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, } /* Mark the streams as enabled. */ - for (i = 0; i < state->stream_configs.num_configs; ++i) { - struct v4l2_subdev_stream_config *cfg = - &state->stream_configs.configs[i]; - - if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream))) - cfg->enabled = true; - } + v4l2_subdev_set_streams_enabled(sd, state, pad, streams_mask, true); if (!already_streaming) v4l2_subdev_enable_privacy_led(sd); @@ -2334,8 +2358,8 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, { struct device *dev = sd->entity.graph_obj.mdev->dev; struct v4l2_subdev_state *state; - u64 found_streams = 0; - unsigned int i; + u64 enabled_streams; + u64 found_streams; int ret; /* A few basic sanity checks first. */ @@ -2356,22 +2380,9 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, * Verify that the requested streams exist and that they are not * already disabled. */ - for (i = 0; i < state->stream_configs.num_configs; ++i) { - struct v4l2_subdev_stream_config *cfg = - &state->stream_configs.configs[i]; - if (cfg->pad != pad || !(streams_mask & BIT_ULL(cfg->stream))) - continue; - - found_streams |= BIT_ULL(cfg->stream); - - if (!cfg->enabled) { - dev_dbg(dev, "stream %u already disabled on %s:%u\n", - cfg->stream, sd->entity.name, pad); - ret = -EALREADY; - goto done; - } - } + v4l2_subdev_collect_streams(sd, state, pad, streams_mask, + &found_streams, &enabled_streams); if (found_streams != streams_mask) { dev_dbg(dev, "streams 0x%llx not found on %s:%u\n", @@ -2380,6 +2391,13 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, goto done; } + if (enabled_streams != streams_mask) { + dev_dbg(dev, "streams 0x%llx already disabled on %s:%u\n", + streams_mask & ~enabled_streams, sd->entity.name, pad); + ret = -EALREADY; + goto done; + } + dev_dbg(dev, "disable streams %u:%#llx\n", pad, streams_mask); /* Call the .disable_streams() operation. */ @@ -2391,14 +2409,7 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, goto done; } - /* Mark the streams as disabled. */ - for (i = 0; i < state->stream_configs.num_configs; ++i) { - struct v4l2_subdev_stream_config *cfg = - &state->stream_configs.configs[i]; - - if (cfg->pad == pad && (streams_mask & BIT_ULL(cfg->stream))) - cfg->enabled = false; - } + v4l2_subdev_set_streams_enabled(sd, state, pad, streams_mask, false); done: if (!v4l2_subdev_is_streaming(sd)) -- cgit v1.2.3 From b62949ddaa52e7612d3bbd90095079f97946c821 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:12 +0300 Subject: media: subdev: Support single-stream case in v4l2_subdev_enable/disable_streams() At the moment the v4l2_subdev_enable/disable_streams() functions call fallback helpers to handle the case where the subdev only implements .s_stream(), and the main function handles the case where the subdev implements streams (V4L2_SUBDEV_FL_STREAMS, which implies .enable/disable_streams()). What is missing is support for subdevs which do not implement streams support, but do implement .enable/disable_streams(). Example cases of these subdevices are single-stream cameras, where using .enable/disable_streams() is not required but helps us remove the users of the legacy .s_stream(), and subdevices with multiple source pads (but single stream per pad), where .enable/disable_streams() allows the subdevice to control the enable/disable state per pad. The two single-streams cases (.s_stream() and .enable/disable_streams()) are very similar, and with small changes we can change the v4l2_subdev_enable/disable_streams() functions to support all three cases, without needing separate fallback functions. A few potentially problematic details, though: - For the single-streams cases we use sd->enabled_pads field, which limits the number of pads for the subdevice to 64. For simplicity I added the check for this limitation to the beginning of the function, and it also applies to the streams case. - The fallback functions only allowed the target pad to be a source pad. It is not very clear to me why this check was needed, but it was not needed in the streams case. However, I doubt the v4l2_subdev_enable/disable_streams() code has ever been tested with sink pads, so to be on the safe side, I added the same check to the v4l2_subdev_enable/disable_streams() functions. Reviewed-by: Laurent Pinchart Tested-by: Umang Jain Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-subdev.c | 194 +++++++++++++++------------------- 1 file changed, 86 insertions(+), 108 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index cad7e2479f2a..2a236f7e9f2b 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -2161,6 +2161,13 @@ static void v4l2_subdev_collect_streams(struct v4l2_subdev *sd, u64 *found_streams, u64 *enabled_streams) { + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) { + *found_streams = BIT_ULL(0); + *enabled_streams = + (sd->enabled_pads & BIT_ULL(pad)) ? BIT_ULL(0) : 0; + return; + } + *found_streams = 0; *enabled_streams = 0; @@ -2182,6 +2189,14 @@ static void v4l2_subdev_set_streams_enabled(struct v4l2_subdev *sd, u32 pad, u64 streams_mask, bool enabled) { + if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS)) { + if (enabled) + sd->enabled_pads |= BIT_ULL(pad); + else + sd->enabled_pads &= ~BIT_ULL(pad); + return; + } + for (unsigned int i = 0; i < state->stream_configs.num_configs; ++i) { struct v4l2_subdev_stream_config *cfg = &state->stream_configs.configs[i]; @@ -2191,51 +2206,6 @@ static void v4l2_subdev_set_streams_enabled(struct v4l2_subdev *sd, } } -static int v4l2_subdev_enable_streams_fallback(struct v4l2_subdev *sd, u32 pad, - u64 streams_mask) -{ - struct device *dev = sd->entity.graph_obj.mdev->dev; - int ret; - - /* - * The subdev doesn't implement pad-based stream enable, fall back - * to the .s_stream() operation. - */ - if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) - return -EOPNOTSUPP; - - /* - * .s_stream() means there is no streams support, so the only allowed - * stream is the implicit stream 0. - */ - if (streams_mask != BIT_ULL(0)) - return -EOPNOTSUPP; - - /* - * We use a 64-bit bitmask for tracking enabled pads, so only subdevices - * with 64 pads or less can be supported. - */ - if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE) - return -EOPNOTSUPP; - - if (sd->enabled_pads & BIT_ULL(pad)) { - dev_dbg(dev, "pad %u already enabled on %s\n", - pad, sd->entity.name); - return -EALREADY; - } - - /* Start streaming when the first pad is enabled. */ - if (!sd->enabled_pads) { - ret = v4l2_subdev_call(sd, video, s_stream, 1); - if (ret) - return ret; - } - - sd->enabled_pads |= BIT_ULL(pad); - - return 0; -} - int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, u64 streams_mask) { @@ -2244,21 +2214,33 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, bool already_streaming; u64 enabled_streams; u64 found_streams; + bool use_s_stream; int ret; /* A few basic sanity checks first. */ if (pad >= sd->entity.num_pads) return -EINVAL; + if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) + return -EOPNOTSUPP; + + /* + * We use a 64-bit bitmask for tracking enabled pads, so only subdevices + * with 64 pads or less can be supported. + */ + if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE) + return -EOPNOTSUPP; + if (!streams_mask) return 0; /* Fallback on .s_stream() if .enable_streams() isn't available. */ - if (!v4l2_subdev_has_op(sd, pad, enable_streams)) - return v4l2_subdev_enable_streams_fallback(sd, pad, - streams_mask); + use_s_stream = !v4l2_subdev_has_op(sd, pad, enable_streams); - state = v4l2_subdev_lock_and_get_active_state(sd); + if (!use_s_stream) + state = v4l2_subdev_lock_and_get_active_state(sd); + else + state = NULL; /* * Verify that the requested streams exist and that they are not @@ -2286,9 +2268,18 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, already_streaming = v4l2_subdev_is_streaming(sd); - /* Call the .enable_streams() operation. */ - ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad, - streams_mask); + if (!use_s_stream) { + /* Call the .enable_streams() operation. */ + ret = v4l2_subdev_call(sd, pad, enable_streams, state, pad, + streams_mask); + } else { + /* Start streaming when the first pad is enabled. */ + if (!already_streaming) + ret = v4l2_subdev_call(sd, video, s_stream, 1); + else + ret = 0; + } + if (ret) { dev_dbg(dev, "enable streams %u:%#llx failed: %d\n", pad, streams_mask, ret); @@ -2298,34 +2289,39 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad, /* Mark the streams as enabled. */ v4l2_subdev_set_streams_enabled(sd, state, pad, streams_mask, true); - if (!already_streaming) + /* + * TODO: When all the drivers have been changed to use + * v4l2_subdev_enable_streams() and v4l2_subdev_disable_streams(), + * instead of calling .s_stream() operation directly, we can remove + * the privacy LED handling from call_s_stream() and do it here + * for all cases. + */ + if (!use_s_stream && !already_streaming) v4l2_subdev_enable_privacy_led(sd); done: - v4l2_subdev_unlock_state(state); + if (!use_s_stream) + v4l2_subdev_unlock_state(state); return ret; } EXPORT_SYMBOL_GPL(v4l2_subdev_enable_streams); -static int v4l2_subdev_disable_streams_fallback(struct v4l2_subdev *sd, u32 pad, - u64 streams_mask) +int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, + u64 streams_mask) { struct device *dev = sd->entity.graph_obj.mdev->dev; + struct v4l2_subdev_state *state; + u64 enabled_streams; + u64 found_streams; + bool use_s_stream; int ret; - /* - * If the subdev doesn't implement pad-based stream enable, fall back - * to the .s_stream() operation. - */ - if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) - return -EOPNOTSUPP; + /* A few basic sanity checks first. */ + if (pad >= sd->entity.num_pads) + return -EINVAL; - /* - * .s_stream() means there is no streams support, so the only allowed - * stream is the implicit stream 0. - */ - if (streams_mask != BIT_ULL(0)) + if (!(sd->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)) return -EOPNOTSUPP; /* @@ -2335,46 +2331,16 @@ static int v4l2_subdev_disable_streams_fallback(struct v4l2_subdev *sd, u32 pad, if (pad >= sizeof(sd->enabled_pads) * BITS_PER_BYTE) return -EOPNOTSUPP; - if (!(sd->enabled_pads & BIT_ULL(pad))) { - dev_dbg(dev, "pad %u already disabled on %s\n", - pad, sd->entity.name); - return -EALREADY; - } - - /* Stop streaming when the last streams are disabled. */ - if (!(sd->enabled_pads & ~BIT_ULL(pad))) { - ret = v4l2_subdev_call(sd, video, s_stream, 0); - if (ret) - return ret; - } - - sd->enabled_pads &= ~BIT_ULL(pad); - - return 0; -} - -int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, - u64 streams_mask) -{ - struct device *dev = sd->entity.graph_obj.mdev->dev; - struct v4l2_subdev_state *state; - u64 enabled_streams; - u64 found_streams; - int ret; - - /* A few basic sanity checks first. */ - if (pad >= sd->entity.num_pads) - return -EINVAL; - if (!streams_mask) return 0; /* Fallback on .s_stream() if .disable_streams() isn't available. */ - if (!v4l2_subdev_has_op(sd, pad, disable_streams)) - return v4l2_subdev_disable_streams_fallback(sd, pad, - streams_mask); + use_s_stream = !v4l2_subdev_has_op(sd, pad, disable_streams); - state = v4l2_subdev_lock_and_get_active_state(sd); + if (!use_s_stream) + state = v4l2_subdev_lock_and_get_active_state(sd); + else + state = NULL; /* * Verify that the requested streams exist and that they are not @@ -2400,9 +2366,19 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, dev_dbg(dev, "disable streams %u:%#llx\n", pad, streams_mask); - /* Call the .disable_streams() operation. */ - ret = v4l2_subdev_call(sd, pad, disable_streams, state, pad, - streams_mask); + if (!use_s_stream) { + /* Call the .disable_streams() operation. */ + ret = v4l2_subdev_call(sd, pad, disable_streams, state, pad, + streams_mask); + } else { + /* Stop streaming when the last streams are disabled. */ + + if (!(sd->enabled_pads & ~BIT_ULL(pad))) + ret = v4l2_subdev_call(sd, video, s_stream, 0); + else + ret = 0; + } + if (ret) { dev_dbg(dev, "disable streams %u:%#llx failed: %d\n", pad, streams_mask, ret); @@ -2412,10 +2388,12 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad, v4l2_subdev_set_streams_enabled(sd, state, pad, streams_mask, false); done: - if (!v4l2_subdev_is_streaming(sd)) - v4l2_subdev_disable_privacy_led(sd); + if (!use_s_stream) { + if (!v4l2_subdev_is_streaming(sd)) + v4l2_subdev_disable_privacy_led(sd); - v4l2_subdev_unlock_state(state); + v4l2_subdev_unlock_state(state); + } return ret; } -- cgit v1.2.3 From 93c726f41afb8407d43e0bea66d716f6446c9e46 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:13 +0300 Subject: media: subdev: Support non-routing subdevs in v4l2_subdev_s_stream_helper() At the moment v4l2_subdev_s_stream_helper() only works for subdevices that support routing. As enable/disable_streams now also works for subdevices without routing, improve v4l2_subdev_s_stream_helper() to do the same. Reviewed-by: Laurent Pinchart Tested-by: Umang Jain Signed-off-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-subdev.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 2a236f7e9f2b..4f71199bf592 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -2422,15 +2422,24 @@ int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable) if (WARN_ON(pad_index == -1)) return -EINVAL; - /* - * As there's a single source pad, just collect all the source streams. - */ - state = v4l2_subdev_lock_and_get_active_state(sd); + if (sd->flags & V4L2_SUBDEV_FL_STREAMS) { + /* + * As there's a single source pad, just collect all the source + * streams. + */ + state = v4l2_subdev_lock_and_get_active_state(sd); - for_each_active_route(&state->routing, route) - source_mask |= BIT_ULL(route->source_stream); + for_each_active_route(&state->routing, route) + source_mask |= BIT_ULL(route->source_stream); - v4l2_subdev_unlock_state(state); + v4l2_subdev_unlock_state(state); + } else { + /* + * For non-streams subdevices, there's a single implicit stream + * per pad. + */ + source_mask = BIT_ULL(0); + } if (enable) return v4l2_subdev_enable_streams(sd, pad_index, source_mask); -- cgit v1.2.3 From f8e9662e4da6d4e394a3d9cf0d335863650d712d Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Apr 2024 18:39:14 +0300 Subject: media: subdev: Improve s_stream documentation Now that enable/disable_streams operations are available for single-stream subdevices too, there's no reason to use the old s_stream operation on new drivers. Extend the documentation reflecting this. Signed-off-by: Tomi Valkeinen Reviewed-by: Umang Jain Reviewed-by: Laurent Pinchart Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- include/media/v4l2-subdev.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 23c970d09870..df66365576dd 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -450,6 +450,15 @@ enum v4l2_subdev_pre_streamon_flags { * already started or stopped subdev. Also see call_s_stream wrapper in * v4l2-subdev.c. * + * New drivers should instead implement &v4l2_subdev_pad_ops.enable_streams + * and &v4l2_subdev_pad_ops.disable_streams operations, and use + * v4l2_subdev_s_stream_helper for the &v4l2_subdev_video_ops.s_stream + * operation to support legacy users. + * + * Drivers should also not call the .s_stream() subdev operation directly, + * but use the v4l2_subdev_enable_streams() and + * v4l2_subdev_disable_streams() helpers. + * * @g_pixelaspect: callback to return the pixelaspect ratio. * * @s_rx_buffer: set a host allocated memory buffer for the subdev. The subdev -- cgit v1.2.3 From f7374d07432d983fcd60fa281897df9a615f7895 Mon Sep 17 00:00:00 2001 From: Umang Jain Date: Tue, 2 Apr 2024 15:37:50 +0530 Subject: media: dt-bindings: media: Add bindings for IMX283 - Add dt-bindings documentation for Sony IMX283 sensor driver - Add MAINTAINERS entry for Sony IMX283 binding documentation Reviewed-by: Rob Herring Reviewed-by: Laurent Pinchart Signed-off-by: Umang Jain Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .../devicetree/bindings/media/i2c/sony,imx283.yaml | 107 +++++++++++++++++++++ MAINTAINERS | 8 ++ 2 files changed, 115 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/sony,imx283.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx283.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx283.yaml new file mode 100644 index 000000000000..e4f49f1435a5 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx283.yaml @@ -0,0 +1,107 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2024 Ideas on Board Oy +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/sony,imx283.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sony IMX283 Sensor + +maintainers: + - Kieran Bingham + - Umang Jain + +description: + IMX283 sensor is a Sony CMOS active pixel digital image sensor with an active + array size of 5472H x 3648V. It is programmable through I2C interface. The + I2C client address is fixed to 0x1a as per sensor data sheet. Image data is + sent through MIPI CSI-2. + +properties: + compatible: + const: sony,imx283 + + reg: + maxItems: 1 + + clocks: + description: Clock frequency from 6 to 24 MHz. + maxItems: 1 + + vadd-supply: + description: Analog power supply (2.9V) + + vdd1-supply: + description: Interface power supply (1.8V) + + vdd2-supply: + description: Digital power supply (1.2V) + + reset-gpios: + description: Sensor reset (XCLR) GPIO + maxItems: 1 + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + anyOf: + - items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + + link-frequencies: true + + required: + - data-lanes + - link-frequencies + + required: + - endpoint + +required: + - compatible + - reg + - clocks + - port + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + camera@1a { + compatible = "sony,imx283"; + reg = <0x1a>; + clocks = <&imx283_clk>; + + assigned-clocks = <&imx283_clk>; + assigned-clock-parents = <&imx283_clk_parent>; + assigned-clock-rates = <12000000>; + + vadd-supply = <&camera_vadd_2v9>; + vdd1-supply = <&camera_vdd1_1v8>; + vdd2-supply = <&camera_vdd2_1v2>; + + port { + imx283: endpoint { + remote-endpoint = <&cam>; + data-lanes = <1 2 3 4>; + link-frequencies = /bits/ 64 <360000000>; + }; + }; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index f1d905f15180..1390fca4d5af 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20815,6 +20815,14 @@ T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/sony,imx274.yaml F: drivers/media/i2c/imx274.c +SONY IMX283 SENSOR DRIVER +M: Kieran Bingham +M: Umang Jain +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media_tree.git +F: Documentation/devicetree/bindings/media/i2c/sony,imx283.yaml + SONY IMX290 SENSOR DRIVER M: Manivannan Sadhasivam L: linux-media@vger.kernel.org -- cgit v1.2.3 From ccb4eb4496fa94a9131c63ad0c412ebfe7f9ac35 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Tue, 2 Apr 2024 15:37:51 +0530 Subject: media: i2c: Add imx283 camera sensor driver Add a v4l2 subdevice driver for the Sony IMX283 image sensor. The IMX283 is a 20MP Diagonal 15.86 mm (Type 1) CMOS Image Sensor with Square Pixel for Color Cameras. The following features are supported: - Manual exposure an gain control support - vblank/hblank/link freq control support - Test pattern support control - Arbitrary horizontal and vertical cropping - Supported resolution: - 5472x3648 @ 20fps (SRGGB12) - 5472x3648 @ 25fps (SRGGB10) - 2736x1824 @ 50fps (SRGGB12) Signed-off-by: Kieran Bingham Signed-off-by: Umang Jain [Sakari Ailus: Take upstream runtime PM API changes into account.] Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- MAINTAINERS | 1 + drivers/media/i2c/Kconfig | 10 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/imx283.c | 1605 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1617 insertions(+) create mode 100644 drivers/media/i2c/imx283.c diff --git a/MAINTAINERS b/MAINTAINERS index 1390fca4d5af..1bd50b5895f8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20822,6 +20822,7 @@ L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/sony,imx283.yaml +F: drivers/media/i2c/imx283.c SONY IMX290 SENSOR DRIVER M: Manivannan Sadhasivam diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 612b0625b176..9450de9eedb6 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -154,6 +154,16 @@ config VIDEO_IMX274 This is a V4L2 sensor driver for the Sony IMX274 CMOS image sensor. +config VIDEO_IMX283 + tristate "Sony IMX283 sensor support" + select V4L2_CCI_I2C + help + This is a V4L2 sensor driver for the Sony IMX283 + CMOS image sensor. + + To compile this driver as a module, choose M here: the + module will be called imx283. + config VIDEO_IMX290 tristate "Sony IMX290 sensor support" select REGMAP_I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index dfbe6448b549..0fbd81f9f420 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_VIDEO_IMX214) += imx214.o obj-$(CONFIG_VIDEO_IMX219) += imx219.o obj-$(CONFIG_VIDEO_IMX258) += imx258.o obj-$(CONFIG_VIDEO_IMX274) += imx274.o +obj-$(CONFIG_VIDEO_IMX283) += imx283.o obj-$(CONFIG_VIDEO_IMX290) += imx290.o obj-$(CONFIG_VIDEO_IMX296) += imx296.o obj-$(CONFIG_VIDEO_IMX319) += imx319.o diff --git a/drivers/media/i2c/imx283.c b/drivers/media/i2c/imx283.c new file mode 100644 index 000000000000..6428eb5394a9 --- /dev/null +++ b/drivers/media/i2c/imx283.c @@ -0,0 +1,1605 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * V4L2 Support for the IMX283 + * + * Diagonal 15.86 mm (Type 1) CMOS Image Sensor with Square Pixel for Color + * Cameras. + * + * Copyright (C) 2024 Ideas on Board Oy. + * + * Based on Sony IMX283 driver prepared by Will Whang + * + * Based on Sony imx477 camera driver + * Copyright (C) 2019-2020 Raspberry Pi (Trading) Ltd + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Chip ID */ +#define IMX283_REG_CHIP_ID CCI_REG8(0x3000) +#define IMX283_CHIP_ID 0x0b // Default power on state + +#define IMX283_REG_STANDBY CCI_REG8(0x3000) +#define IMX283_ACTIVE 0 +#define IMX283_STANDBY BIT(0) +#define IMX283_STBLOGIC BIT(1) +#define IMX283_STBMIPI BIT(2) +#define IMX283_STBDV BIT(3) +#define IMX283_SLEEP BIT(4) + +#define IMX283_REG_CLAMP CCI_REG8(0x3001) +#define IMX283_CLPSQRST BIT(4) + +#define IMX283_REG_PLSTMG08 CCI_REG8(0x3003) +#define IMX283_PLSTMG08_VAL 0x77 + +#define IMX283_REG_MDSEL1 CCI_REG8(0x3004) +#define IMX283_REG_MDSEL2 CCI_REG8(0x3005) +#define IMX283_REG_MDSEL3 CCI_REG8(0x3006) +#define IMX283_MDSEL3_VCROP_EN BIT(5) +#define IMX283_REG_MDSEL4 CCI_REG8(0x3007) +#define IMX283_MDSEL4_VCROP_EN (BIT(4) | BIT(6)) + +#define IMX283_REG_SVR CCI_REG16_LE(0x3009) + +#define IMX283_REG_HTRIMMING CCI_REG8(0x300b) +#define IMX283_MDVREV BIT(0) /* VFLIP */ +#define IMX283_HTRIMMING_EN BIT(4) + +#define IMX283_REG_VWINPOS CCI_REG16_LE(0x300f) +#define IMX283_REG_VWIDCUT CCI_REG16_LE(0x3011) + +#define IMX283_REG_MDSEL7 CCI_REG16_LE(0x3013) + +/* CSI Clock Configuration */ +#define IMX283_REG_TCLKPOST CCI_REG8(0x3018) +#define IMX283_REG_THSPREPARE CCI_REG8(0x301a) +#define IMX283_REG_THSZERO CCI_REG8(0x301c) +#define IMX283_REG_THSTRAIL CCI_REG8(0x301e) +#define IMX283_REG_TCLKTRAIL CCI_REG8(0x3020) +#define IMX283_REG_TCLKPREPARE CCI_REG8(0x3022) +#define IMX283_REG_TCLKZERO CCI_REG16_LE(0x3024) +#define IMX283_REG_TLPX CCI_REG8(0x3026) +#define IMX283_REG_THSEXIT CCI_REG8(0x3028) +#define IMX283_REG_TCLKPRE CCI_REG8(0x302a) +#define IMX283_REG_SYSMODE CCI_REG8(0x3104) + +#define IMX283_REG_Y_OUT_SIZE CCI_REG16_LE(0x302f) +#define IMX283_REG_WRITE_VSIZE CCI_REG16_LE(0x3031) +#define IMX283_REG_OB_SIZE_V CCI_REG8(0x3033) + +/* HMAX internal HBLANK */ +#define IMX283_REG_HMAX CCI_REG16_LE(0x3036) +#define IMX283_HMAX_MAX (BIT(16) - 1) + +/* VMAX internal VBLANK */ +#define IMX283_REG_VMAX CCI_REG24_LE(0x3038) +#define IMX283_VMAX_MAX (BIT(16) - 1) + +/* SHR internal */ +#define IMX283_REG_SHR CCI_REG16_LE(0x303b) +#define IMX283_SHR_MIN 11 + +/* + * Analog gain control + * Gain [dB] = -20log{(2048 - value [10:0]) /2048} + * Range: 0dB to approximately +27dB + */ +#define IMX283_REG_ANALOG_GAIN CCI_REG16_LE(0x3042) +#define IMX283_ANA_GAIN_MIN 0 +#define IMX283_ANA_GAIN_MAX 1957 +#define IMX283_ANA_GAIN_STEP 1 +#define IMX283_ANA_GAIN_DEFAULT 0x0 + +/* + * Digital gain control + * Gain [dB] = value * 6 + * Range: 0dB to +18db + */ +#define IMX283_REG_DIGITAL_GAIN CCI_REG8(0x3044) +#define IMX283_DGTL_GAIN_MIN 0 +#define IMX283_DGTL_GAIN_MAX 3 +#define IMX283_DGTL_GAIN_DEFAULT 0 +#define IMX283_DGTL_GAIN_STEP 1 + +#define IMX283_REG_HTRIMMING_START CCI_REG16_LE(0x3058) +#define IMX283_REG_HTRIMMING_END CCI_REG16_LE(0x305a) + +#define IMX283_REG_MDSEL18 CCI_REG16_LE(0x30f6) + +/* Master Mode Operation Control */ +#define IMX283_REG_XMSTA CCI_REG8(0x3105) +#define IMX283_XMSTA BIT(0) + +#define IMX283_REG_SYNCDRV CCI_REG8(0x3107) +#define IMX283_SYNCDRV_XHS_XVS (0xa0 | 0x02) +#define IMX283_SYNCDRV_HIZ (0xa0 | 0x03) + +/* PLL Standby */ +#define IMX283_REG_STBPL CCI_REG8(0x320b) +#define IMX283_STBPL_NORMAL 0x00 +#define IMX283_STBPL_STANDBY 0x03 + +/* Input Frequency Setting */ +#define IMX283_REG_PLRD1 CCI_REG8(0x36c1) +#define IMX283_REG_PLRD2 CCI_REG16_LE(0x36c2) +#define IMX283_REG_PLRD3 CCI_REG8(0x36f7) +#define IMX283_REG_PLRD4 CCI_REG8(0x36f8) + +#define IMX283_REG_PLSTMG02 CCI_REG8(0x36aa) +#define IMX283_PLSTMG02_VAL 0x00 + +#define IMX283_REG_EBD_X_OUT_SIZE CCI_REG16_LE(0x3a54) + +/* Test pattern generator */ +#define IMX283_REG_TPG_CTRL CCI_REG8(0x3156) +#define IMX283_TPG_CTRL_CLKEN BIT(0) +#define IMX283_TPG_CTRL_PATEN BIT(4) + +#define IMX283_REG_TPG_PAT CCI_REG8(0x3157) +#define IMX283_TPG_PAT_ALL_000 0x00 +#define IMX283_TPG_PAT_ALL_FFF 0x01 +#define IMX283_TPG_PAT_ALL_555 0x02 +#define IMX283_TPG_PAT_ALL_AAA 0x03 +#define IMX283_TPG_PAT_H_COLOR_BARS 0x0a +#define IMX283_TPG_PAT_V_COLOR_BARS 0x0b + +/* Exposure control */ +#define IMX283_EXPOSURE_MIN 52 +#define IMX283_EXPOSURE_STEP 1 +#define IMX283_EXPOSURE_DEFAULT 1000 +#define IMX283_EXPOSURE_MAX 49865 + +#define IMAGE_PAD 0 + +#define IMX283_XCLR_MIN_DELAY_US (1 * USEC_PER_MSEC) +#define IMX283_XCLR_DELAY_RANGE_US (1 * USEC_PER_MSEC) + +/* IMX283 native and active pixel array size. */ +static const struct v4l2_rect imx283_native_area = { + .top = 0, + .left = 0, + .width = 5592, + .height = 3710, +}; + +static const struct v4l2_rect imx283_active_area = { + .top = 40, + .left = 108, + .width = 5472, + .height = 3648, +}; + +struct imx283_reg_list { + unsigned int num_of_regs; + const struct cci_reg_sequence *regs; +}; + +/* Mode : resolution and related config values */ +struct imx283_mode { + unsigned int mode; + + /* Bits per pixel */ + unsigned int bpp; + + /* Frame width */ + unsigned int width; + + /* Frame height */ + unsigned int height; + + /* + * Minimum horizontal timing in pixel-units + * + * Note that HMAX is written in 72MHz units, and the datasheet assumes a + * 720MHz link frequency. Convert datasheet values with the following: + * + * For 12 bpp modes (480Mbps) convert with: + * hmax = [hmax in 72MHz units] * 480 / 72 + * + * For 10 bpp modes (576Mbps) convert with: + * hmax = [hmax in 72MHz units] * 576 / 72 + */ + u32 min_hmax; + + /* minimum V-timing in lines */ + u32 min_vmax; + + /* default H-timing */ + u32 default_hmax; + + /* default V-timing */ + u32 default_vmax; + + /* minimum SHR */ + u32 min_shr; + + /* + * Per-mode vertical crop constants used to calculate values + * of IMX283REG_WIDCUT and IMX283_REG_VWINPOS. + */ + u32 veff; + u32 vst; + u32 vct; + + /* Horizontal and vertical binning ratio */ + u8 hbin_ratio; + u8 vbin_ratio; + + /* Optical Blanking */ + u32 horizontal_ob; + u32 vertical_ob; + + /* Analog crop rectangle. */ + struct v4l2_rect crop; +}; + +struct imx283_input_frequency { + unsigned int mhz; + unsigned int reg_count; + struct cci_reg_sequence regs[4]; +}; + +static const struct imx283_input_frequency imx283_frequencies[] = { + { + .mhz = 6 * HZ_PER_MHZ, + .reg_count = 4, + .regs = { + { IMX283_REG_PLRD1, 0x00 }, + { IMX283_REG_PLRD2, 0x00f0 }, + { IMX283_REG_PLRD3, 0x00 }, + { IMX283_REG_PLRD4, 0xc0 }, + }, + }, + { + .mhz = 12 * HZ_PER_MHZ, + .reg_count = 4, + .regs = { + { IMX283_REG_PLRD1, 0x01 }, + { IMX283_REG_PLRD2, 0x00f0 }, + { IMX283_REG_PLRD3, 0x01 }, + { IMX283_REG_PLRD4, 0xc0 }, + }, + }, + { + .mhz = 18 * HZ_PER_MHZ, + .reg_count = 4, + .regs = { + { IMX283_REG_PLRD1, 0x01 }, + { IMX283_REG_PLRD2, 0x00a0 }, + { IMX283_REG_PLRD3, 0x01 }, + { IMX283_REG_PLRD4, 0x80 }, + }, + }, + { + .mhz = 24 * HZ_PER_MHZ, + .reg_count = 4, + .regs = { + { IMX283_REG_PLRD1, 0x02 }, + { IMX283_REG_PLRD2, 0x00f0 }, + { IMX283_REG_PLRD3, 0x02 }, + { IMX283_REG_PLRD4, 0xc0 }, + }, + }, +}; + +enum imx283_modes { + IMX283_MODE_0, + IMX283_MODE_1, + IMX283_MODE_1A, + IMX283_MODE_1S, + IMX283_MODE_2, + IMX283_MODE_2A, + IMX283_MODE_3, + IMX283_MODE_4, + IMX283_MODE_5, + IMX283_MODE_6, +}; + +struct imx283_readout_mode { + u8 mdsel1; + u8 mdsel2; + u8 mdsel3; + u8 mdsel4; +}; + +static const struct imx283_readout_mode imx283_readout_modes[] = { + /* All pixel scan modes */ + [IMX283_MODE_0] = { 0x04, 0x03, 0x10, 0x00 }, /* 12 bit */ + [IMX283_MODE_1] = { 0x04, 0x01, 0x00, 0x00 }, /* 10 bit */ + [IMX283_MODE_1A] = { 0x04, 0x01, 0x20, 0x50 }, /* 10 bit */ + [IMX283_MODE_1S] = { 0x04, 0x41, 0x20, 0x50 }, /* 10 bit */ + + /* Horizontal / Vertical 2/2-line binning */ + [IMX283_MODE_2] = { 0x0d, 0x11, 0x50, 0x00 }, /* 12 bit */ + [IMX283_MODE_2A] = { 0x0d, 0x11, 0x70, 0x50 }, /* 12 bit */ + + /* Horizontal / Vertical 3/3-line binning */ + [IMX283_MODE_3] = { 0x1e, 0x18, 0x10, 0x00 }, /* 12 bit */ + + /* Vertical 2/9 subsampling, horizontal 3 binning cropping */ + [IMX283_MODE_4] = { 0x29, 0x18, 0x30, 0x50 }, /* 12 bit */ + + /* Vertical 2/19 subsampling binning, horizontal 3 binning */ + [IMX283_MODE_5] = { 0x2d, 0x18, 0x10, 0x00 }, /* 12 bit */ + + /* Vertical 2 binning horizontal 2/4, subsampling 16:9 cropping */ + [IMX283_MODE_6] = { 0x18, 0x21, 0x00, 0x09 }, /* 10 bit */ + + /* + * New modes should make sure the offset period is complied. + * See imx283_exposure() for reference. + */ +}; + +static const struct cci_reg_sequence mipi_data_rate_1440Mbps[] = { + /* The default register settings provide the 1440Mbps rate */ + { CCI_REG8(0x36c5), 0x00 }, /* Undocumented */ + { CCI_REG8(0x3ac4), 0x00 }, /* Undocumented */ + + { IMX283_REG_STBPL, 0x00 }, + { IMX283_REG_TCLKPOST, 0xa7 }, + { IMX283_REG_THSPREPARE, 0x6f }, + { IMX283_REG_THSZERO, 0x9f }, + { IMX283_REG_THSTRAIL, 0x5f }, + { IMX283_REG_TCLKTRAIL, 0x5f }, + { IMX283_REG_TCLKPREPARE, 0x6f }, + { IMX283_REG_TCLKZERO, 0x017f }, + { IMX283_REG_TLPX, 0x4f }, + { IMX283_REG_THSEXIT, 0x47 }, + { IMX283_REG_TCLKPRE, 0x07 }, + { IMX283_REG_SYSMODE, 0x02 }, +}; + +static const struct cci_reg_sequence mipi_data_rate_720Mbps[] = { + /* Undocumented Additions "For 720MBps" Setting */ + { CCI_REG8(0x36c5), 0x01 }, /* Undocumented */ + { CCI_REG8(0x3ac4), 0x01 }, /* Undocumented */ + + { IMX283_REG_STBPL, 0x00 }, + { IMX283_REG_TCLKPOST, 0x77 }, + { IMX283_REG_THSPREPARE, 0x37 }, + { IMX283_REG_THSZERO, 0x67 }, + { IMX283_REG_THSTRAIL, 0x37 }, + { IMX283_REG_TCLKTRAIL, 0x37 }, + { IMX283_REG_TCLKPREPARE, 0x37 }, + { IMX283_REG_TCLKZERO, 0xdf }, + { IMX283_REG_TLPX, 0x2f }, + { IMX283_REG_THSEXIT, 0x47 }, + { IMX283_REG_TCLKPRE, 0x0f }, + { IMX283_REG_SYSMODE, 0x02 }, +}; + +static const s64 link_frequencies[] = { + 720 * HZ_PER_MHZ, /* 1440 Mbps lane data rate */ + 360 * HZ_PER_MHZ, /* 720 Mbps data lane rate */ +}; + +static const struct imx283_reg_list link_freq_reglist[] = { + { /* 720 MHz */ + .num_of_regs = ARRAY_SIZE(mipi_data_rate_1440Mbps), + .regs = mipi_data_rate_1440Mbps, + }, + { /* 360 MHz */ + .num_of_regs = ARRAY_SIZE(mipi_data_rate_720Mbps), + .regs = mipi_data_rate_720Mbps, + }, +}; + +#define CENTERED_RECTANGLE(rect, _width, _height) \ + { \ + .left = rect.left + ((rect.width - (_width)) / 2), \ + .top = rect.top + ((rect.height - (_height)) / 2), \ + .width = (_width), \ + .height = (_height), \ + } + +/* Mode configs */ +static const struct imx283_mode supported_modes_12bit[] = { + { + /* 20MPix 21.40 fps readout mode 0 */ + .mode = IMX283_MODE_0, + .bpp = 12, + .width = 5472, + .height = 3648, + .min_hmax = 5914, /* 887 @ 480MHz/72MHz */ + .min_vmax = 3793, /* Lines */ + + .veff = 3694, + .vst = 0, + .vct = 0, + + .hbin_ratio = 1, + .vbin_ratio = 1, + + /* 20.00 FPS */ + .default_hmax = 6000, /* 900 @ 480MHz/72MHz */ + .default_vmax = 4000, + + .min_shr = 11, + .horizontal_ob = 96, + .vertical_ob = 16, + .crop = CENTERED_RECTANGLE(imx283_active_area, 5472, 3648), + }, + { + /* + * Readout mode 2 : 2/2 binned mode (2736x1824) + */ + .mode = IMX283_MODE_2, + .bpp = 12, + .width = 2736, + .height = 1824, + .min_hmax = 2414, /* Pixels (362 * 480MHz/72MHz + padding) */ + .min_vmax = 3840, /* Lines */ + + /* 50.00 FPS */ + .default_hmax = 2500, /* 375 @ 480MHz/72Mhz */ + .default_vmax = 3840, + + .veff = 1824, + .vst = 0, + .vct = 0, + + .hbin_ratio = 2, + .vbin_ratio = 2, + + .min_shr = 12, + .horizontal_ob = 48, + .vertical_ob = 4, + + .crop = CENTERED_RECTANGLE(imx283_active_area, 5472, 3648), + }, +}; + +static const struct imx283_mode supported_modes_10bit[] = { + { + /* 20MPix 25.48 fps readout mode 1 */ + .mode = IMX283_MODE_1, + .bpp = 10, + .width = 5472, + .height = 3648, + .min_hmax = 5960, /* 745 @ 576MHz / 72MHz */ + .min_vmax = 3793, + + /* 25.00 FPS */ + .default_hmax = 6000, /* 750 @ 576MHz / 72MHz */ + .default_vmax = 3840, + + .min_shr = 10, + .horizontal_ob = 96, + .vertical_ob = 16, + .crop = CENTERED_RECTANGLE(imx283_active_area, 5472, 3648), + }, +}; + +static const u32 imx283_mbus_codes[] = { + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SRGGB10_1X10, +}; + +/* regulator supplies */ +static const char *const imx283_supply_name[] = { + "vadd", /* Analog (2.9V) supply */ + "vdd1", /* Supply Voltage 2 (1.8V) supply */ + "vdd2", /* Supply Voltage 3 (1.2V) supply */ +}; + +struct imx283 { + struct device *dev; + struct regmap *cci; + + const struct imx283_input_frequency *freq; + + struct v4l2_subdev sd; + struct media_pad pad; + + struct clk *xclk; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[ARRAY_SIZE(imx283_supply_name)]; + + /* V4L2 Controls */ + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vflip; + + unsigned long link_freq_bitmap; + + u16 hmax; + u32 vmax; +}; + +static inline struct imx283 *to_imx283(struct v4l2_subdev *sd) +{ + return container_of_const(sd, struct imx283, sd); +} + +static inline void get_mode_table(unsigned int code, + const struct imx283_mode **mode_list, + unsigned int *num_modes) +{ + switch (code) { + case MEDIA_BUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SBGGR12_1X12: + *mode_list = supported_modes_12bit; + *num_modes = ARRAY_SIZE(supported_modes_12bit); + break; + + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SBGGR10_1X10: + *mode_list = supported_modes_10bit; + *num_modes = ARRAY_SIZE(supported_modes_10bit); + break; + default: + *mode_list = NULL; + *num_modes = 0; + break; + } +} + +/* Calculate the Pixel Rate based on the current mode */ +static u64 imx283_pixel_rate(struct imx283 *imx283, + const struct imx283_mode *mode) +{ + u64 link_frequency = link_frequencies[__ffs(imx283->link_freq_bitmap)]; + unsigned int bpp = mode->bpp; + const unsigned int ddr = 2; /* Double Data Rate */ + const unsigned int lanes = 4; /* Only 4 lane support */ + u64 numerator = link_frequency * ddr * lanes; + + do_div(numerator, bpp); + + return numerator; +} + +/* Convert from a variable pixel_rate to 72 MHz clock cycles */ +static u64 imx283_internal_clock(unsigned int pixel_rate, unsigned int pixels) +{ + /* + * Determine the following operation without overflow: + * pixels = 72 Mhz / pixel_rate + * + * The internal clock at 72MHz and Pixel Rate (between 240 and 576MHz) + * can easily overflow this calculation, so pre-divide to simplify. + */ + const u32 iclk_pre = 72; + const u32 pclk_pre = pixel_rate / HZ_PER_MHZ; + u64 numerator = pixels * iclk_pre; + + do_div(numerator, pclk_pre); + + return numerator; +} + +/* Internal clock (72MHz) to Pixel Rate clock (Variable) */ +static u64 imx283_iclk_to_pix(unsigned int pixel_rate, unsigned int cycles) +{ + /* + * Determine the following operation without overflow: + * cycles * pixel_rate / 72 MHz + * + * The internal clock at 72MHz and Pixel Rate (between 240 and 576MHz) + * can easily overflow this calculation, so pre-divide to simplify. + */ + const u32 iclk_pre = 72; + const u32 pclk_pre = pixel_rate / HZ_PER_MHZ; + u64 numerator = cycles * pclk_pre; + + do_div(numerator, iclk_pre); + + return numerator; +} + +/* Determine the exposure based on current hmax, vmax and a given SHR */ +static u32 imx283_exposure(struct imx283 *imx283, + const struct imx283_mode *mode, u64 shr) +{ + u32 svr = 0; /* SVR feature is not currently supported */ + u32 offset; + u64 numerator; + + /* Number of clocks per internal offset period */ + offset = mode->mode == IMX283_MODE_0 ? 209 : 157; + numerator = (imx283->vmax * (svr + 1) - shr) * imx283->hmax + offset; + + do_div(numerator, imx283->hmax); + + return clamp(numerator, 0, U32_MAX); +} + +static void imx283_exposure_limits(struct imx283 *imx283, + const struct imx283_mode *mode, + s64 *min_exposure, s64 *max_exposure) +{ + u32 svr = 0; /* SVR feature is not currently supported */ + u64 min_shr = mode->min_shr; + /* Global Shutter is not supported */ + u64 max_shr = (svr + 1) * imx283->vmax - 4; + + max_shr = min(max_shr, BIT(16) - 1); + + *min_exposure = imx283_exposure(imx283, mode, max_shr); + *max_exposure = imx283_exposure(imx283, mode, min_shr); +} + +/* + * Integration Time [s] = [ {VMAX x (SVR + 1) – (SHR)} x HMAX + offset ] + * / [ 72 x 10^6 ] + */ +static u32 imx283_shr(struct imx283 *imx283, const struct imx283_mode *mode, + u32 exposure) +{ + u32 svr = 0; /* SVR feature is not currently supported */ + u32 offset; + u64 temp; + + /* Number of clocks per internal offset period */ + offset = mode->mode == IMX283_MODE_0 ? 209 : 157; + temp = ((u64)exposure * imx283->hmax - offset); + do_div(temp, imx283->hmax); + + return (imx283->vmax * (svr + 1) - temp); +} + +static const char * const imx283_tpg_menu[] = { + "Disabled", + "All 000h", + "All FFFh", + "All 555h", + "All AAAh", + "Horizontal color bars", + "Vertical color bars", +}; + +static const int imx283_tpg_val[] = { + IMX283_TPG_PAT_ALL_000, + IMX283_TPG_PAT_ALL_000, + IMX283_TPG_PAT_ALL_FFF, + IMX283_TPG_PAT_ALL_555, + IMX283_TPG_PAT_ALL_AAA, + IMX283_TPG_PAT_H_COLOR_BARS, + IMX283_TPG_PAT_V_COLOR_BARS, +}; + +static int imx283_update_test_pattern(struct imx283 *imx283, u32 pattern_index) +{ + int ret; + + if (pattern_index >= ARRAY_SIZE(imx283_tpg_val)) + return -EINVAL; + + if (!pattern_index) + return cci_write(imx283->cci, IMX283_REG_TPG_CTRL, 0x00, NULL); + + ret = cci_write(imx283->cci, IMX283_REG_TPG_PAT, + imx283_tpg_val[pattern_index], NULL); + if (ret) + return ret; + + return cci_write(imx283->cci, IMX283_REG_TPG_CTRL, + IMX283_TPG_CTRL_CLKEN | IMX283_TPG_CTRL_PATEN, NULL); +} + +static int imx283_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx283 *imx283 = container_of(ctrl->handler, struct imx283, + ctrl_handler); + const struct imx283_mode *mode; + struct v4l2_mbus_framefmt *fmt; + const struct imx283_mode *mode_list; + struct v4l2_subdev_state *state; + unsigned int num_modes; + u64 shr, pixel_rate; + int ret = 0; + + state = v4l2_subdev_get_locked_active_state(&imx283->sd); + fmt = v4l2_subdev_state_get_format(state, 0); + + get_mode_table(fmt->code, &mode_list, &num_modes); + mode = v4l2_find_nearest_size(mode_list, num_modes, width, height, + fmt->width, fmt->height); + + /* + * The VBLANK control may change the limits of usable exposure, so check + * and adjust if necessary. + */ + if (ctrl->id == V4L2_CID_VBLANK) { + /* Honour the VBLANK limits when setting exposure. */ + s64 current_exposure, max_exposure, min_exposure; + + imx283->vmax = mode->height + ctrl->val; + + imx283_exposure_limits(imx283, mode, + &min_exposure, &max_exposure); + + current_exposure = imx283->exposure->val; + current_exposure = clamp(current_exposure, min_exposure, + max_exposure); + + __v4l2_ctrl_modify_range(imx283->exposure, min_exposure, + max_exposure, 1, current_exposure); + } + + /* + * Applying V4L2 control value only happens + * when power is up for streaming + */ + if (!pm_runtime_get_if_active(imx283->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + shr = imx283_shr(imx283, mode, ctrl->val); + dev_dbg(imx283->dev, "V4L2_CID_EXPOSURE : %d - SHR: %lld\n", + ctrl->val, shr); + ret = cci_write(imx283->cci, IMX283_REG_SHR, shr, NULL); + break; + + case V4L2_CID_HBLANK: + pixel_rate = imx283_pixel_rate(imx283, mode); + imx283->hmax = imx283_internal_clock(pixel_rate, mode->width + ctrl->val); + dev_dbg(imx283->dev, "V4L2_CID_HBLANK : %d HMAX : %u\n", + ctrl->val, imx283->hmax); + ret = cci_write(imx283->cci, IMX283_REG_HMAX, imx283->hmax, NULL); + break; + + case V4L2_CID_VBLANK: + imx283->vmax = mode->height + ctrl->val; + dev_dbg(imx283->dev, "V4L2_CID_VBLANK : %d VMAX : %u\n", + ctrl->val, imx283->vmax); + ret = cci_write(imx283->cci, IMX283_REG_VMAX, imx283->vmax, NULL); + break; + + case V4L2_CID_ANALOGUE_GAIN: + ret = cci_write(imx283->cci, IMX283_REG_ANALOG_GAIN, ctrl->val, NULL); + break; + + case V4L2_CID_DIGITAL_GAIN: + ret = cci_write(imx283->cci, IMX283_REG_DIGITAL_GAIN, ctrl->val, NULL); + break; + + case V4L2_CID_VFLIP: + /* + * VFLIP is managed by BIT(0) of IMX283_REG_HTRIMMING address, hence + * both need to be set simultaneously. + */ + if (ctrl->val) { + cci_write(imx283->cci, IMX283_REG_HTRIMMING, + IMX283_HTRIMMING_EN | IMX283_MDVREV, &ret); + } else { + cci_write(imx283->cci, IMX283_REG_HTRIMMING, + IMX283_HTRIMMING_EN, &ret); + } + break; + + case V4L2_CID_TEST_PATTERN: + ret = imx283_update_test_pattern(imx283, ctrl->val); + break; + + default: + dev_err(imx283->dev, "ctrl(id:0x%x, val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(imx283->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx283_ctrl_ops = { + .s_ctrl = imx283_set_ctrl, +}; + +static int imx283_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(imx283_mbus_codes)) + return -EINVAL; + + code->code = imx283_mbus_codes[code->index]; + + return 0; +} + +static int imx283_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + const struct imx283_mode *mode_list; + unsigned int num_modes; + + get_mode_table(fse->code, &mode_list, &num_modes); + + if (fse->index >= num_modes) + return -EINVAL; + + fse->min_width = mode_list[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = mode_list[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static void imx283_update_image_pad_format(struct imx283 *imx283, + const struct imx283_mode *mode, + struct v4l2_mbus_framefmt *format) +{ + format->width = mode->width; + format->height = mode->height; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_RAW; + format->ycbcr_enc = V4L2_YCBCR_ENC_601; + format->quantization = V4L2_QUANTIZATION_FULL_RANGE; + format->xfer_func = V4L2_XFER_FUNC_NONE; +} + +static int imx283_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct imx283 *imx283 = to_imx283(sd); + struct v4l2_mbus_framefmt *format; + const struct imx283_mode *mode; + struct v4l2_rect *crop; + + /* Initialize try_fmt */ + format = v4l2_subdev_state_get_format(state, IMAGE_PAD); + + mode = &supported_modes_12bit[0]; + format->code = MEDIA_BUS_FMT_SRGGB12_1X12; + imx283_update_image_pad_format(imx283, mode, format); + + /* Initialize crop rectangle to mode default */ + crop = v4l2_subdev_state_get_crop(state, IMAGE_PAD); + *crop = mode->crop; + + return 0; +} + +static void imx283_set_framing_limits(struct imx283 *imx283, + const struct imx283_mode *mode) +{ + u64 pixel_rate = imx283_pixel_rate(imx283, mode); + u64 min_hblank, max_hblank, def_hblank; + + /* Initialise hmax and vmax for exposure calculations */ + imx283->hmax = imx283_internal_clock(pixel_rate, mode->default_hmax); + imx283->vmax = mode->default_vmax; + + /* + * Horizontal Blanking + * Convert the HMAX_MAX (72MHz) to Pixel rate values for HBLANK_MAX + */ + min_hblank = mode->min_hmax - mode->width; + max_hblank = imx283_iclk_to_pix(pixel_rate, IMX283_HMAX_MAX) - mode->width; + def_hblank = mode->default_hmax - mode->width; + __v4l2_ctrl_modify_range(imx283->hblank, min_hblank, max_hblank, 1, + def_hblank); + __v4l2_ctrl_s_ctrl(imx283->hblank, def_hblank); + + /* Vertical Blanking */ + __v4l2_ctrl_modify_range(imx283->vblank, mode->min_vmax - mode->height, + IMX283_VMAX_MAX - mode->height, 1, + mode->default_vmax - mode->height); + __v4l2_ctrl_s_ctrl(imx283->vblank, mode->default_vmax - mode->height); +} + +static int imx283_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *format; + const struct imx283_mode *mode; + struct imx283 *imx283 = to_imx283(sd); + const struct imx283_mode *mode_list; + unsigned int num_modes; + + get_mode_table(fmt->format.code, &mode_list, &num_modes); + + mode = v4l2_find_nearest_size(mode_list, num_modes, width, height, + fmt->format.width, fmt->format.height); + + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_601; + fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; + + format = v4l2_subdev_state_get_format(sd_state, 0); + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + imx283_set_framing_limits(imx283, mode); + + *format = fmt->format; + + return 0; +} + +static int imx283_standby_cancel(struct imx283 *imx283) +{ + unsigned int link_freq_idx; + int ret = 0; + + cci_write(imx283->cci, IMX283_REG_STANDBY, + IMX283_STBLOGIC | IMX283_STBDV, &ret); + + /* Configure PLL clocks based on the xclk */ + cci_multi_reg_write(imx283->cci, imx283->freq->regs, + imx283->freq->reg_count, &ret); + + dev_dbg(imx283->dev, "Using clk freq %ld MHz", + imx283->freq->mhz / HZ_PER_MHZ); + + /* Initialise communication */ + cci_write(imx283->cci, IMX283_REG_PLSTMG08, IMX283_PLSTMG08_VAL, &ret); + cci_write(imx283->cci, IMX283_REG_PLSTMG02, IMX283_PLSTMG02_VAL, &ret); + + /* Enable PLL */ + cci_write(imx283->cci, IMX283_REG_STBPL, IMX283_STBPL_NORMAL, &ret); + + /* Configure the MIPI link speed */ + link_freq_idx = __ffs(imx283->link_freq_bitmap); + cci_multi_reg_write(imx283->cci, link_freq_reglist[link_freq_idx].regs, + link_freq_reglist[link_freq_idx].num_of_regs, + &ret); + + /* 1st Stabilisation period of 1 ms or more */ + usleep_range(1000, 2000); + + /* Activate */ + cci_write(imx283->cci, IMX283_REG_STANDBY, IMX283_ACTIVE, &ret); + + /* 2nd Stabilisation period of 19ms or more */ + usleep_range(19000, 20000); + + cci_write(imx283->cci, IMX283_REG_CLAMP, IMX283_CLPSQRST, &ret); + cci_write(imx283->cci, IMX283_REG_XMSTA, 0, &ret); + cci_write(imx283->cci, IMX283_REG_SYNCDRV, IMX283_SYNCDRV_XHS_XVS, &ret); + + return ret; +} + +/* Start streaming */ +static int imx283_start_streaming(struct imx283 *imx283, + struct v4l2_subdev_state *state) +{ + const struct imx283_readout_mode *readout; + const struct imx283_mode *mode; + const struct v4l2_mbus_framefmt *fmt; + const struct imx283_mode *mode_list; + unsigned int num_modes; + u32 v_widcut; + s32 v_pos; + u32 write_v_size; + u32 y_out_size; + int ret = 0; + + fmt = v4l2_subdev_state_get_format(state, 0); + get_mode_table(fmt->code, &mode_list, &num_modes); + mode = v4l2_find_nearest_size(mode_list, num_modes, width, height, + fmt->width, fmt->height); + + ret = imx283_standby_cancel(imx283); + if (ret) { + dev_err(imx283->dev, "failed to cancel standby\n"); + return ret; + } + + /* + * Set the readout mode registers. + * MDSEL3 and MDSEL4 are updated to enable Arbitrary Vertical Cropping. + */ + readout = &imx283_readout_modes[mode->mode]; + cci_write(imx283->cci, IMX283_REG_MDSEL1, readout->mdsel1, &ret); + cci_write(imx283->cci, IMX283_REG_MDSEL2, readout->mdsel2, &ret); + cci_write(imx283->cci, IMX283_REG_MDSEL3, + readout->mdsel3 | IMX283_MDSEL3_VCROP_EN, &ret); + cci_write(imx283->cci, IMX283_REG_MDSEL4, + readout->mdsel4 | IMX283_MDSEL4_VCROP_EN, &ret); + + /* Mode 1S specific entries from the Readout Drive Mode Tables */ + if (mode->mode == IMX283_MODE_1S) { + cci_write(imx283->cci, IMX283_REG_MDSEL7, 0x01, &ret); + cci_write(imx283->cci, IMX283_REG_MDSEL18, 0x1098, &ret); + } + + if (ret) { + dev_err(imx283->dev, "failed to set readout\n"); + return ret; + } + + /* Initialise SVR. Unsupported for now - Always 0 */ + cci_write(imx283->cci, IMX283_REG_SVR, 0x00, &ret); + + dev_dbg(imx283->dev, "Mode: Size %d x %d\n", mode->width, mode->height); + dev_dbg(imx283->dev, "Analogue Crop (in the mode) %d,%d %dx%d\n", + mode->crop.left, + mode->crop.top, + mode->crop.width, + mode->crop.height); + + y_out_size = mode->crop.height / mode->vbin_ratio; + write_v_size = y_out_size + mode->vertical_ob; + /* + * cropping start position = (VWINPOS – Vst) × 2 + * cropping width = Veff – (VWIDCUT – Vct) × 2 + */ + v_pos = imx283->vflip->val ? + ((-mode->crop.top / mode->vbin_ratio) / 2) + mode->vst : + ((mode->crop.top / mode->vbin_ratio) / 2) + mode->vst; + v_widcut = ((mode->veff - y_out_size) / 2) + mode->vct; + + cci_write(imx283->cci, IMX283_REG_Y_OUT_SIZE, y_out_size, &ret); + cci_write(imx283->cci, IMX283_REG_WRITE_VSIZE, write_v_size, &ret); + cci_write(imx283->cci, IMX283_REG_VWIDCUT, v_widcut, &ret); + cci_write(imx283->cci, IMX283_REG_VWINPOS, v_pos, &ret); + + cci_write(imx283->cci, IMX283_REG_OB_SIZE_V, mode->vertical_ob, &ret); + + /* TODO: Validate mode->crop is fully contained within imx283_native_area */ + cci_write(imx283->cci, IMX283_REG_HTRIMMING_START, mode->crop.left, &ret); + cci_write(imx283->cci, IMX283_REG_HTRIMMING_END, + mode->crop.left + mode->crop.width, &ret); + + /* Disable embedded data */ + cci_write(imx283->cci, IMX283_REG_EBD_X_OUT_SIZE, 0, &ret); + + /* Apply customized values from controls (HMAX/VMAX/SHR) */ + ret = __v4l2_ctrl_handler_setup(imx283->sd.ctrl_handler); + + return ret; +} + +static int imx283_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct imx283 *imx283 = to_imx283(sd); + int ret; + + if (pad != IMAGE_PAD) + return -EINVAL; + + ret = pm_runtime_get_sync(imx283->dev); + if (ret < 0) { + pm_runtime_put_noidle(imx283->dev); + return ret; + } + + ret = imx283_start_streaming(imx283, state); + if (ret) + goto err_rpm_put; + + return 0; + +err_rpm_put: + pm_runtime_mark_last_busy(imx283->dev); + pm_runtime_put_autosuspend(imx283->dev); + + return ret; +} + +static int imx283_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct imx283 *imx283 = to_imx283(sd); + int ret; + + if (pad != IMAGE_PAD) + return -EINVAL; + + ret = cci_write(imx283->cci, IMX283_REG_STANDBY, IMX283_STBLOGIC, NULL); + if (ret) + dev_err(imx283->dev, "Failed to stop stream\n"); + + pm_runtime_mark_last_busy(imx283->dev); + pm_runtime_put_autosuspend(imx283->dev); + + return ret; +} + +/* Power/clock management functions */ +static int imx283_power_on(struct imx283 *imx283) +{ + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(imx283_supply_name), + imx283->supplies); + if (ret) { + dev_err(imx283->dev, "failed to enable regulators\n"); + return ret; + } + + ret = clk_prepare_enable(imx283->xclk); + if (ret) { + dev_err(imx283->dev, "failed to enable clock\n"); + goto reg_off; + } + + gpiod_set_value_cansleep(imx283->reset_gpio, 0); + + usleep_range(IMX283_XCLR_MIN_DELAY_US, + IMX283_XCLR_MIN_DELAY_US + IMX283_XCLR_DELAY_RANGE_US); + + return 0; + +reg_off: + regulator_bulk_disable(ARRAY_SIZE(imx283_supply_name), imx283->supplies); + return ret; +} + +static int imx283_power_off(struct imx283 *imx283) +{ + gpiod_set_value_cansleep(imx283->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(imx283_supply_name), imx283->supplies); + clk_disable_unprepare(imx283->xclk); + + return 0; +} + +static int imx283_runtime_resume(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx283 *imx283 = to_imx283(sd); + + return imx283_power_on(imx283); +} + +static int imx283_runtime_suspend(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx283 *imx283 = to_imx283(sd); + + imx283_power_off(imx283); + + return 0; +} + +static int imx283_get_regulators(struct imx283 *imx283) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(imx283_supply_name); i++) + imx283->supplies[i].supply = imx283_supply_name[i]; + + return devm_regulator_bulk_get(imx283->dev, + ARRAY_SIZE(imx283_supply_name), + imx283->supplies); +} + +/* Verify chip ID */ +static int imx283_identify_module(struct imx283 *imx283) +{ + int ret; + u64 val; + + ret = cci_read(imx283->cci, IMX283_REG_CHIP_ID, &val, NULL); + if (ret) { + dev_err(imx283->dev, "failed to read chip id %x, with error %d\n", + IMX283_CHIP_ID, ret); + return ret; + } + + if (val != IMX283_CHIP_ID) { + dev_err(imx283->dev, "chip id mismatch: %x!=%llx\n", + IMX283_CHIP_ID, val); + return -EIO; + } + + return 0; +} + +static int imx283_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + sel->r = *v4l2_subdev_state_get_crop(sd_state, 0); + return 0; + } + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r = imx283_native_area; + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r = imx283_active_area; + return 0; + default: + return -EINVAL; + } +} + +static const struct v4l2_subdev_core_ops imx283_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops imx283_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops imx283_pad_ops = { + .enum_mbus_code = imx283_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = imx283_set_pad_format, + .get_selection = imx283_get_selection, + .enum_frame_size = imx283_enum_frame_size, + .enable_streams = imx283_enable_streams, + .disable_streams = imx283_disable_streams, +}; + +static const struct v4l2_subdev_internal_ops imx283_internal_ops = { + .init_state = imx283_init_state, +}; + +static const struct v4l2_subdev_ops imx283_subdev_ops = { + .core = &imx283_core_ops, + .video = &imx283_video_ops, + .pad = &imx283_pad_ops, +}; + +/* Initialize control handlers */ +static int imx283_init_controls(struct imx283 *imx283) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl *link_freq; + const struct imx283_mode *mode = &supported_modes_12bit[0]; + u64 min_hblank, max_hblank, def_hblank; + u64 pixel_rate; + int ret; + + ctrl_hdlr = &imx283->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 16); + if (ret) + return ret; + + /* + * Create the controls here, but mode specific limits are setup + * in the imx283_set_framing_limits() call below. + */ + + /* By default, PIXEL_RATE is read only */ + pixel_rate = imx283_pixel_rate(imx283, mode); + v4l2_ctrl_new_std(ctrl_hdlr, &imx283_ctrl_ops, + V4L2_CID_PIXEL_RATE, pixel_rate, + pixel_rate, 1, pixel_rate); + + link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx283_ctrl_ops, + V4L2_CID_LINK_FREQ, + __fls(imx283->link_freq_bitmap), + __ffs(imx283->link_freq_bitmap), + link_frequencies); + if (link_freq) + link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + /* Initialise vblank/hblank/exposure based on the current mode. */ + imx283->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx283_ctrl_ops, + V4L2_CID_VBLANK, + mode->min_vmax - mode->height, + IMX283_VMAX_MAX, 1, + mode->default_vmax - mode->height); + + min_hblank = mode->min_hmax - mode->width; + max_hblank = imx283_iclk_to_pix(pixel_rate, IMX283_HMAX_MAX) - mode->width; + def_hblank = mode->default_hmax - mode->width; + imx283->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx283_ctrl_ops, + V4L2_CID_HBLANK, min_hblank, max_hblank, + 1, def_hblank); + + imx283->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx283_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX283_EXPOSURE_MIN, + IMX283_EXPOSURE_MAX, + IMX283_EXPOSURE_STEP, + IMX283_EXPOSURE_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx283_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + IMX283_ANA_GAIN_MIN, IMX283_ANA_GAIN_MAX, + IMX283_ANA_GAIN_STEP, IMX283_ANA_GAIN_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx283_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + IMX283_DGTL_GAIN_MIN, IMX283_DGTL_GAIN_MAX, + IMX283_DGTL_GAIN_STEP, IMX283_DGTL_GAIN_DEFAULT); + + imx283->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx283_ctrl_ops, V4L2_CID_VFLIP, + 0, 1, 1, 0); + if (imx283->vflip) + imx283->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx283_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx283_tpg_menu) - 1, + 0, 0, imx283_tpg_menu); + + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + dev_err(imx283->dev, "control init failed (%d)\n", ret); + goto error; + } + + ret = v4l2_fwnode_device_parse(imx283->dev, &props); + if (ret) + goto error; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx283_ctrl_ops, + &props); + if (ret) + goto error; + + imx283->sd.ctrl_handler = ctrl_hdlr; + + mutex_lock(imx283->ctrl_handler.lock); + + /* Setup exposure and frame/line length limits. */ + imx283_set_framing_limits(imx283, mode); + + mutex_unlock(imx283->ctrl_handler.lock); + + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ret; +} + +static int imx283_parse_endpoint(struct imx283 *imx283) +{ + struct fwnode_handle *fwnode; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct fwnode_handle *ep; + int ret; + + fwnode = dev_fwnode(imx283->dev); + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) { + dev_err(imx283->dev, "Failed to get next endpoint\n"); + return -ENXIO; + } + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return ret; + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) { + dev_err(imx283->dev, + "number of CSI2 data lanes %d is not supported\n", + bus_cfg.bus.mipi_csi2.num_data_lanes); + ret = -EINVAL; + goto done_endpoint_free; + } + + ret = v4l2_link_freq_to_bitmap(imx283->dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + link_frequencies, ARRAY_SIZE(link_frequencies), + &imx283->link_freq_bitmap); + +done_endpoint_free: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +}; + +static int imx283_probe(struct i2c_client *client) +{ + struct imx283 *imx283; + unsigned int i; + unsigned int xclk_freq; + int ret; + + imx283 = devm_kzalloc(&client->dev, sizeof(*imx283), GFP_KERNEL); + if (!imx283) + return -ENOMEM; + + imx283->dev = &client->dev; + + v4l2_i2c_subdev_init(&imx283->sd, client, &imx283_subdev_ops); + + imx283->cci = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(imx283->cci)) { + ret = PTR_ERR(imx283->cci); + dev_err(imx283->dev, "failed to initialize CCI: %d\n", ret); + return ret; + } + + /* Get system clock (xclk) */ + imx283->xclk = devm_clk_get(imx283->dev, NULL); + if (IS_ERR(imx283->xclk)) { + return dev_err_probe(imx283->dev, PTR_ERR(imx283->xclk), + "failed to get xclk\n"); + } + + xclk_freq = clk_get_rate(imx283->xclk); + for (i = 0; i < ARRAY_SIZE(imx283_frequencies); i++) { + if (xclk_freq == imx283_frequencies[i].mhz) { + imx283->freq = &imx283_frequencies[i]; + break; + } + } + if (!imx283->freq) { + dev_err(imx283->dev, "xclk frequency unsupported: %d Hz\n", xclk_freq); + return -EINVAL; + } + + ret = imx283_get_regulators(imx283); + if (ret) { + return dev_err_probe(imx283->dev, ret, + "failed to get regulators\n"); + } + + ret = imx283_parse_endpoint(imx283); + if (ret) { + dev_err(imx283->dev, "failed to parse endpoint configuration\n"); + return ret; + } + + /* Request optional enable pin */ + imx283->reset_gpio = devm_gpiod_get_optional(imx283->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(imx283->reset_gpio)) + return dev_err_probe(imx283->dev, PTR_ERR(imx283->reset_gpio), + "failed to get reset GPIO\n"); + + /* + * The sensor must be powered for imx283_identify_module() + * to be able to read the CHIP_ID register + */ + ret = imx283_power_on(imx283); + if (ret) + return ret; + + ret = imx283_identify_module(imx283); + if (ret) + goto error_power_off; + + /* + * Enable runtime PM with autosuspend. As the device has been powered + * manually, mark it as active, and increase the usage count without + * resuming the device. + */ + pm_runtime_set_active(imx283->dev); + pm_runtime_get_noresume(imx283->dev); + pm_runtime_enable(imx283->dev); + pm_runtime_set_autosuspend_delay(imx283->dev, 1000); + pm_runtime_use_autosuspend(imx283->dev); + + /* This needs the pm runtime to be registered. */ + ret = imx283_init_controls(imx283); + if (ret) + goto error_pm; + + /* Initialize subdev */ + imx283->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + imx283->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + imx283->sd.internal_ops = &imx283_internal_ops; + + /* Initialize source pads */ + imx283->pad.flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&imx283->sd.entity, 1, &imx283->pad); + if (ret) { + dev_err(imx283->dev, "failed to init entity pads: %d\n", ret); + goto error_handler_free; + } + + imx283->sd.state_lock = imx283->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx283->sd); + if (ret < 0) { + dev_err(imx283->dev, "subdev init error: %d\n", ret); + goto error_media_entity; + } + + ret = v4l2_async_register_subdev_sensor(&imx283->sd); + if (ret < 0) { + dev_err(imx283->dev, "failed to register sensor sub-device: %d\n", ret); + goto error_subdev_cleanup; + } + + /* + * Decrease the PM usage count. The device will get suspended after the + * autosuspend delay, turning the power off. + */ + pm_runtime_mark_last_busy(imx283->dev); + pm_runtime_put_autosuspend(imx283->dev); + + return 0; + +error_subdev_cleanup: + v4l2_subdev_cleanup(&imx283->sd); + +error_media_entity: + media_entity_cleanup(&imx283->sd.entity); + +error_handler_free: + v4l2_ctrl_handler_free(imx283->sd.ctrl_handler); + +error_pm: + pm_runtime_disable(imx283->dev); + pm_runtime_set_suspended(imx283->dev); +error_power_off: + imx283_power_off(imx283); + + return ret; +} + +static void imx283_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx283 *imx283 = to_imx283(sd); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(&imx283->sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(imx283->sd.ctrl_handler); + + pm_runtime_disable(imx283->dev); + if (!pm_runtime_status_suspended(imx283->dev)) + imx283_power_off(imx283); + pm_runtime_set_suspended(imx283->dev); +} + +static DEFINE_RUNTIME_DEV_PM_OPS(imx283_pm_ops, imx283_runtime_suspend, + imx283_runtime_resume, NULL); + +static const struct of_device_id imx283_dt_ids[] = { + { .compatible = "sony,imx283" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx283_dt_ids); + +static struct i2c_driver imx283_i2c_driver = { + .driver = { + .name = "imx283", + .pm = pm_ptr(&imx283_pm_ops), + .of_match_table = imx283_dt_ids, + }, + .probe = imx283_probe, + .remove = imx283_remove, +}; +module_i2c_driver(imx283_i2c_driver); + +MODULE_AUTHOR("Will Whang "); +MODULE_AUTHOR("Kieran Bingham "); +MODULE_AUTHOR("Umang Jain "); +MODULE_DESCRIPTION("Sony IMX283 Sensor Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 3b11a323b6082542430637140cd75c7d119b9a22 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Fri, 24 May 2024 17:48:48 +0100 Subject: media: i2c: dw9768: remove unused struct 'regval_list' 'regval_list' is unused since the original commit 859891228e56 ("media: i2c: dw9768: Add DW9768 VCM driver") Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/dw9768.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/media/i2c/dw9768.c b/drivers/media/i2c/dw9768.c index daabbece8c7e..18ef2b35c9aa 100644 --- a/drivers/media/i2c/dw9768.c +++ b/drivers/media/i2c/dw9768.c @@ -115,11 +115,6 @@ static inline struct dw9768 *sd_to_dw9768(struct v4l2_subdev *subdev) return container_of(subdev, struct dw9768, sd); } -struct regval_list { - u8 reg_num; - u8 value; -}; - struct dw9768_aac_mode_ot_multi { u32 aac_mode_enum; u32 ot_multi_base100; -- cgit v1.2.3 From 1fb40556bd61eb35f71258afabc5bd2bc7fc4f3c Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Fri, 24 May 2024 17:48:49 +0100 Subject: media: i2c: ks0127: remove unused struct 'adjust' 'adjust' has been unused since original commit fbe60daac4c3 ("V4L/DVB (3916): AverMedia 6 Eyes AVS6EYES support"). Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ks0127.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/media/i2c/ks0127.c b/drivers/media/i2c/ks0127.c index 5c583f57e3f3..9d0a763cd503 100644 --- a/drivers/media/i2c/ks0127.c +++ b/drivers/media/i2c/ks0127.c @@ -175,14 +175,6 @@ MODULE_LICENSE("GPL"); * mga_dev : represents one ks0127 chip. ****************************************************************************/ -struct adjust { - int contrast; - int bright; - int hue; - int ugain; - int vgain; -}; - struct ks0127 { struct v4l2_subdev sd; v4l2_std_id norm; -- cgit v1.2.3 From 12e14941c169c5b81e20eecedd43694c313b449e Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Fri, 24 May 2024 17:48:50 +0100 Subject: media: i2c: tw9910: remove unused strust 'regval_list' 'regval_list' has been unused since commit 398994c1e104 ("V4L/DVB (13666): tw9910: modify V/H outpit pin setting to use VALID"). Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/tw9910.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c index 905af98c7d53..6dffaaa9ed56 100644 --- a/drivers/media/i2c/tw9910.c +++ b/drivers/media/i2c/tw9910.c @@ -212,11 +212,6 @@ * structure */ -struct regval_list { - unsigned char reg_num; - unsigned char value; -}; - struct tw9910_scale_ctrl { char *name; unsigned short width; -- cgit v1.2.3 From 6062bf32ff3fb89361bba93eda9377ad18d45a25 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Fri, 24 May 2024 17:48:51 +0100 Subject: media: i2c: adv7511: remove unused struct 'i2c_reg_value' 'i2c_reg_value' is unused since the original commit 5a544cce2177 ("[media] adv7511: add new video encoder"). Remove it. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/adv7511-v4l2.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c index 79946e9c7401..261871be833f 100644 --- a/drivers/media/i2c/adv7511-v4l2.c +++ b/drivers/media/i2c/adv7511-v4l2.c @@ -62,11 +62,6 @@ MODULE_LICENSE("GPL v2"); ********************************************************************** */ -struct i2c_reg_value { - unsigned char reg; - unsigned char value; -}; - struct adv7511_state_edid { /* total number of blocks */ u32 blocks; -- cgit v1.2.3 From da1d582aa7e349dd46351f2168911d2f0e058e9f Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 23 May 2024 13:07:14 +0300 Subject: media: ipu6: Print CSR messages using debug level It's entirely normal CSR will return non-indicative values after its reset (0). There's no need to warn the user about that. Suggested-by: Bingbu Cao Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu6/ipu6-buttress.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6-buttress.c b/drivers/media/pci/intel/ipu6/ipu6-buttress.c index 23c537e7ce1e..e47f84c30e10 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-buttress.c +++ b/drivers/media/pci/intel/ipu6/ipu6-buttress.c @@ -163,8 +163,8 @@ int ipu6_buttress_ipc_reset(struct ipu6_device *isp, writel(ENTRY, isp->base + ipc->csr_out); break; default: - dev_warn_ratelimited(&isp->pdev->dev, - "Unexpected CSR 0x%x\n", val); + dev_dbg_ratelimited(&isp->pdev->dev, + "Unexpected CSR 0x%x\n", val); break; } } while (retries--); -- cgit v1.2.3 From 7417b1b1f36cc214dc458e717278a27a912d3b51 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 28 May 2024 14:00:51 +0300 Subject: media: v4l2-cci: Always assign *val Always assign *val to 0 in cci_read(). This has the benefit of not requiring initialisation of the variables data is read to using cci_read(). Once smatch is fixed, it could catch the use of uninitialised reads. Signed-off-by: Sakari Ailus Tested-by: Benjamin Mugnier Reviewed-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-cci.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-cci.c b/drivers/media/v4l2-core/v4l2-cci.c index ee3475bed37f..1ff94affbaf3 100644 --- a/drivers/media/v4l2-core/v4l2-cci.c +++ b/drivers/media/v4l2-core/v4l2-cci.c @@ -23,6 +23,15 @@ int cci_read(struct regmap *map, u32 reg, u64 *val, int *err) u8 buf[8]; int ret; + /* + * TODO: Fix smatch. Assign *val to 0 here in order to avoid + * failing a smatch check on caller when the caller proceeds to + * read *val without initialising it on caller's side. *val is set + * to a valid value whenever this function returns 0 but smatch + * can't figure that out currently. + */ + *val = 0; + if (err && *err) return *err; -- cgit v1.2.3 From e73412fdeb541e777b3fd50b665781c1856422b5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 9 May 2024 00:40:43 +0300 Subject: media: v4l2-subdev: Fix v4l2_subdev_state_get_format() documentation The documentation of the v4l2_subdev_state_get_format() macro incorrectly references __v4l2_subdev_state_get_format() instead of __v4l2_subdev_state_gen_call(). Fix it, and also update the list of similar macros to add the missing v4l2_subdev_state_get_interval(). Suggested-by: Sakari Ailus Signed-off-by: Laurent Pinchart Reviewed-by: Sakari Ailus Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- include/media/v4l2-subdev.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index df66365576dd..ed339f0116bf 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1351,12 +1351,12 @@ void v4l2_subdev_cleanup(struct v4l2_subdev *sd); */ /* * Wrap v4l2_subdev_state_get_format(), allowing the function to be called with - * two or three arguments. The purpose of the __v4l2_subdev_state_get_format() + * two or three arguments. The purpose of the __v4l2_subdev_state_gen_call() * macro below is to come up with the name of the function or macro to call, * using the last two arguments (_stream and _pad). The selected function or * macro is then called using the arguments specified by the caller. A similar - * arrangement is used for v4l2_subdev_state_crop() and - * v4l2_subdev_state_compose() below. + * arrangement is used for v4l2_subdev_state_crop(), v4l2_subdev_state_compose() + * and v4l2_subdev_state_get_interval() below. */ #define v4l2_subdev_state_get_format(state, pad, ...) \ __v4l2_subdev_state_gen_call(format, ##__VA_ARGS__, , _pad) \ -- cgit v1.2.3 From 85af84852f115d3c9d4b45b0500315e630baa82c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 9 May 2024 00:40:43 +0300 Subject: media: v4l2-subdev: Provide const-aware subdev state accessors It would be useful to mark instances of v4l2_subdev_state structures as const when code needs to access them read-only. This isn't currently possible, as the v4l2_subdev_state_get_*() accessor functions take a non-const pointer to the state. Use _Generic() to provide two different versions of the accessors, for const and non-const states respectively. The former returns a const pointer to the requested format, rectangle or interval, implementing const-correctness. The latter returns a non-const pointer, preserving the current behaviour for drivers. Signed-off-by: Laurent Pinchart Reviewed-by: Sakari Ailus Reviewed-by: Tomi Valkeinen [Sakari Ailus: Drop the word "below" from the text.] Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- include/media/v4l2-subdev.h | 54 ++++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index ed339f0116bf..1b74e037e440 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1337,6 +1337,16 @@ void v4l2_subdev_cleanup(struct v4l2_subdev *sd); #define __v4l2_subdev_state_gen_call(NAME, _1, ARG, ...) \ __v4l2_subdev_state_get_ ## NAME ## ARG +/* + * A macro to constify the return value of the state accessors when the state + * parameter is const. + */ +#define __v4l2_subdev_state_constify_ret(state, value) \ + _Generic(state, \ + const struct v4l2_subdev_state *: (const typeof(*(value)) *)(value), \ + struct v4l2_subdev_state *: (value) \ + ) + /** * v4l2_subdev_state_get_format() - Get pointer to a stream format * @state: subdevice state @@ -1352,15 +1362,20 @@ void v4l2_subdev_cleanup(struct v4l2_subdev *sd); /* * Wrap v4l2_subdev_state_get_format(), allowing the function to be called with * two or three arguments. The purpose of the __v4l2_subdev_state_gen_call() - * macro below is to come up with the name of the function or macro to call, - * using the last two arguments (_stream and _pad). The selected function or - * macro is then called using the arguments specified by the caller. A similar - * arrangement is used for v4l2_subdev_state_crop(), v4l2_subdev_state_compose() - * and v4l2_subdev_state_get_interval() below. - */ -#define v4l2_subdev_state_get_format(state, pad, ...) \ - __v4l2_subdev_state_gen_call(format, ##__VA_ARGS__, , _pad) \ - (state, pad, ##__VA_ARGS__) + * macro is to come up with the name of the function or macro to call, using + * the last two arguments (_stream and _pad). The selected function or macro is + * then called using the arguments specified by the caller. The + * __v4l2_subdev_state_constify_ret() macro constifies the returned pointer + * when the state is const, allowing the state accessors to guarantee + * const-correctness in all cases. + * + * A similar arrangement is used for v4l2_subdev_state_crop(), + * v4l2_subdev_state_compose() and v4l2_subdev_state_get_interval() below. + */ +#define v4l2_subdev_state_get_format(state, pad, ...) \ + __v4l2_subdev_state_constify_ret(state, \ + __v4l2_subdev_state_gen_call(format, ##__VA_ARGS__, , _pad) \ + ((struct v4l2_subdev_state *)state, pad, ##__VA_ARGS__)) #define __v4l2_subdev_state_get_format_pad(state, pad) \ __v4l2_subdev_state_get_format(state, pad, 0) struct v4l2_mbus_framefmt * @@ -1379,9 +1394,10 @@ __v4l2_subdev_state_get_format(struct v4l2_subdev_state *state, * For stream-unaware drivers the crop rectangle for the corresponding pad is * returned. If the pad does not exist, NULL is returned. */ -#define v4l2_subdev_state_get_crop(state, pad, ...) \ - __v4l2_subdev_state_gen_call(crop, ##__VA_ARGS__, , _pad) \ - (state, pad, ##__VA_ARGS__) +#define v4l2_subdev_state_get_crop(state, pad, ...) \ + __v4l2_subdev_state_constify_ret(state, \ + __v4l2_subdev_state_gen_call(crop, ##__VA_ARGS__, , _pad) \ + ((struct v4l2_subdev_state *)state, pad, ##__VA_ARGS__)) #define __v4l2_subdev_state_get_crop_pad(state, pad) \ __v4l2_subdev_state_get_crop(state, pad, 0) struct v4l2_rect * @@ -1400,9 +1416,10 @@ __v4l2_subdev_state_get_crop(struct v4l2_subdev_state *state, unsigned int pad, * For stream-unaware drivers the compose rectangle for the corresponding pad is * returned. If the pad does not exist, NULL is returned. */ -#define v4l2_subdev_state_get_compose(state, pad, ...) \ - __v4l2_subdev_state_gen_call(compose, ##__VA_ARGS__, , _pad) \ - (state, pad, ##__VA_ARGS__) +#define v4l2_subdev_state_get_compose(state, pad, ...) \ + __v4l2_subdev_state_constify_ret(state, \ + __v4l2_subdev_state_gen_call(compose, ##__VA_ARGS__, , _pad) \ + ((struct v4l2_subdev_state *)state, pad, ##__VA_ARGS__)) #define __v4l2_subdev_state_get_compose_pad(state, pad) \ __v4l2_subdev_state_get_compose(state, pad, 0) struct v4l2_rect * @@ -1421,9 +1438,10 @@ __v4l2_subdev_state_get_compose(struct v4l2_subdev_state *state, * For stream-unaware drivers the frame interval for the corresponding pad is * returned. If the pad does not exist, NULL is returned. */ -#define v4l2_subdev_state_get_interval(state, pad, ...) \ - __v4l2_subdev_state_gen_call(interval, ##__VA_ARGS__, , _pad) \ - (state, pad, ##__VA_ARGS__) +#define v4l2_subdev_state_get_interval(state, pad, ...) \ + __v4l2_subdev_state_constify_ret(state, \ + __v4l2_subdev_state_gen_call(interval, ##__VA_ARGS__, , _pad) \ + ((struct v4l2_subdev_state *)state, pad, ##__VA_ARGS__)) #define __v4l2_subdev_state_get_interval_pad(state, pad) \ __v4l2_subdev_state_get_interval(state, pad, 0) struct v4l2_fract * -- cgit v1.2.3 From 91eef099f88e17ad7ac2bc1cb7484796d8595642 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 9 May 2024 00:40:45 +0300 Subject: media: rkisp1: Mark subdev state pointers as const Several subdev state pointers are used to access the state read-only. Mark them as const. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Nicolas Dufresne Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c | 8 ++++---- drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c index e45a213baf49..91301d17d356 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c @@ -173,7 +173,7 @@ static void rkisp1_gasket_disable(struct rkisp1_device *rkisp1) * or at the frame end interrupt */ static void rkisp1_config_ism(struct rkisp1_isp *isp, - struct v4l2_subdev_state *sd_state) + const struct v4l2_subdev_state *sd_state) { const struct v4l2_rect *src_crop = v4l2_subdev_state_get_crop(sd_state, @@ -201,7 +201,7 @@ static void rkisp1_config_ism(struct rkisp1_isp *isp, * configure ISP blocks with input format, size...... */ static int rkisp1_config_isp(struct rkisp1_isp *isp, - struct v4l2_subdev_state *sd_state, + const struct v4l2_subdev_state *sd_state, enum v4l2_mbus_type mbus_type, u32 mbus_flags) { struct rkisp1_device *rkisp1 = isp->rkisp1; @@ -309,7 +309,7 @@ static int rkisp1_config_isp(struct rkisp1_isp *isp, if (src_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) { rkisp1_params_disable(&rkisp1->params); } else { - struct v4l2_mbus_framefmt *src_frm; + const struct v4l2_mbus_framefmt *src_frm; src_frm = v4l2_subdev_state_get_format(sd_state, RKISP1_ISP_PAD_SOURCE_VIDEO); @@ -429,7 +429,7 @@ static void rkisp1_config_clk(struct rkisp1_isp *isp) } static int rkisp1_isp_start(struct rkisp1_isp *isp, - struct v4l2_subdev_state *sd_state, + const struct v4l2_subdev_state *sd_state, struct media_pad *source) { struct rkisp1_device *rkisp1 = isp->rkisp1; diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c index 6f3931ca5b51..1fa991227fa9 100644 --- a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c +++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c @@ -135,11 +135,11 @@ static void rkisp1_dcrop_disable(struct rkisp1_resizer *rsz, /* configure dual-crop unit */ static void rkisp1_dcrop_config(struct rkisp1_resizer *rsz, - struct v4l2_subdev_state *sd_state) + const struct v4l2_subdev_state *sd_state) { struct rkisp1_device *rkisp1 = rsz->rkisp1; - struct v4l2_mbus_framefmt *sink_fmt; - struct v4l2_rect *sink_crop; + const struct v4l2_mbus_framefmt *sink_fmt; + const struct v4l2_rect *sink_crop; u32 dc_ctrl; sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK); @@ -264,7 +264,7 @@ static void rkisp1_rsz_config_regs(struct rkisp1_resizer *rsz, } static void rkisp1_rsz_config(struct rkisp1_resizer *rsz, - struct v4l2_subdev_state *sd_state, + const struct v4l2_subdev_state *sd_state, enum rkisp1_shadow_regs_when when) { const struct rkisp1_rsz_yuv_mbus_info *sink_yuv_info, *src_yuv_info; -- cgit v1.2.3 From 1bcbc00878083cddc13787a5961aadb8c62a5065 Mon Sep 17 00:00:00 2001 From: Julien Massot Date: Tue, 30 Apr 2024 15:19:27 +0200 Subject: dt-bindings: media: add Maxim MAX96717 GMSL2 Serializer Add DT bindings for Maxim MAX96717 GMSL2 Serializer. Reviewed-by: Conor Dooley Signed-off-by: Julien Massot Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .../bindings/media/i2c/maxim,max96717.yaml | 157 +++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/maxim,max96717.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/maxim,max96717.yaml b/Documentation/devicetree/bindings/media/i2c/maxim,max96717.yaml new file mode 100644 index 000000000000..d1e8ba6e368e --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/maxim,max96717.yaml @@ -0,0 +1,157 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2024 Collabora Ltd. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/maxim,max96717.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MAX96717 CSI-2 to GMSL2 Serializer + +maintainers: + - Julien Massot + +description: + The MAX96717 serializer converts MIPI CSI-2 D-PHY formatted input + into GMSL2 serial outputs. The device allows the GMSL2 link to + simultaneously transmit bidirectional control-channel data while forward + video transmissions are in progress. The MAX96717 can connect to one + remotely located deserializer using industry-standard coax or STP + interconnects. The device cans operate in pixel or tunnel mode. In pixel mode + the MAX96717 can select the MIPI datatype, while the tunnel mode forward all the MIPI + data received by the serializer. + The MAX96717 supports Reference Over Reverse (channel), + to generate a clock output for the sensor from the GMSL reverse channel. + + The GMSL2 serial link operates at a fixed rate of 3Gbps or 6Gbps in the + forward direction and 187.5Mbps in the reverse direction. + MAX96717F only supports a fixed rate of 3Gbps in the forward direction. + +properties: + compatible: + oneOf: + - const: maxim,max96717f + - items: + - enum: + - maxim,max96717 + - const: maxim,max96717f + + '#gpio-cells': + const: 2 + description: + First cell is the GPIO pin number, second cell is the flags. The GPIO pin + number must be in range of [0, 10]. + + gpio-controller: true + + '#clock-cells': + const: 0 + + reg: + maxItems: 1 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: CSI-2 Input port + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + minItems: 1 + maxItems: 4 + + lane-polarities: + minItems: 1 + maxItems: 5 + + required: + - data-lanes + + port@1: + $ref: /schemas/graph.yaml#/properties/port + unevaluatedProperties: false + description: GMSL Output port + + required: + - port@1 + + i2c-gate: + $ref: /schemas/i2c/i2c-gate.yaml + unevaluatedProperties: false + description: + The MAX96717 will forward the I2C requests from the + incoming GMSL2 link. Therefore, it supports an i2c-gate + subnode to configure a sensor. + +required: + - compatible + - reg + - ports + +additionalProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + serializer: serializer@40 { + compatible = "maxim,max96717f"; + reg = <0x40>; + gpio-controller; + #gpio-cells = <2>; + #clock-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + max96717f_csi_in: endpoint { + data-lanes = <1 2 3 4>; + remote-endpoint = <&sensor_out>; + }; + }; + + port@1 { + reg = <1>; + max96917f_gmsl_out: endpoint { + remote-endpoint = <&deser_gmsl_in>; + }; + }; + }; + + i2c-gate { + #address-cells = <1>; + #size-cells = <0>; + sensor@10 { + compatible = "st,st-vgxy61"; + reg = <0x10>; + reset-gpios = <&serializer 0 GPIO_ACTIVE_LOW>; + clocks = <&serializer>; + VCORE-supply = <&v1v2>; + VDDIO-supply = <&v1v8>; + VANA-supply = <&v2v8>; + port { + sensor_out: endpoint { + data-lanes = <1 2 3 4>; + remote-endpoint = <&max96717f_csi_in>; + }; + }; + }; + }; + }; + }; +... -- cgit v1.2.3 From 331a1c0407dd86af83ee0f2fc16e6a6a85f9f885 Mon Sep 17 00:00:00 2001 From: Julien Massot Date: Tue, 30 Apr 2024 15:19:28 +0200 Subject: dt-bindings: media: add Maxim MAX96714 GMSL2 Deserializer Add DT bindings for Maxim MAX96714 GMSL2 Deserializer. Reviewed-by: Conor Dooley Signed-off-by: Julien Massot Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .../bindings/media/i2c/maxim,max96714.yaml | 174 +++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/maxim,max96714.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/maxim,max96714.yaml b/Documentation/devicetree/bindings/media/i2c/maxim,max96714.yaml new file mode 100644 index 000000000000..3ace50e11921 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/maxim,max96714.yaml @@ -0,0 +1,174 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2024 Collabora Ltd. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/maxim,max96714.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim MAX96714 GMSL2 to CSI-2 Deserializer + +maintainers: + - Julien Massot + +description: + The MAX96714 deserializer converts GMSL2 serial inputs into MIPI + CSI-2 D-PHY formatted output. The device allows the GMSL2 link to + simultaneously transmit bidirectional control-channel data while forward + video transmissions are in progress. The MAX96714 can connect to one + remotely located serializer using industry-standard coax or STP + interconnects. The device cans operate in pixel or tunnel mode. In pixel mode + the MAX96714 can select individual video stream, while the tunnel mode forward all + the MIPI data received by the serializer. + + The GMSL2 serial link operates at a fixed rate of 3Gbps or 6Gbps in the + forward direction and 187.5Mbps in the reverse direction. + MAX96714F only supports a fixed rate of 3Gbps in the forward direction. + +properties: + compatible: + oneOf: + - const: maxim,max96714f + - items: + - enum: + - maxim,max96714 + - const: maxim,max96714f + + reg: + maxItems: 1 + + powerdown-gpios: + maxItems: 1 + description: + Specifier for the GPIO connected to the PWDNB pin. + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + unevaluatedProperties: false + description: GMSL Input + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + description: + Endpoint for GMSL2-Link port. + + port@1: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: CSI-2 Output port + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + minItems: 1 + maxItems: 4 + + lane-polarities: + minItems: 1 + maxItems: 5 + + link-frequencies: + maxItems: 1 + + required: + - data-lanes + + required: + - port@1 + + i2c-gate: + $ref: /schemas/i2c/i2c-gate.yaml + unevaluatedProperties: false + description: + The MAX96714 will pass through and forward the I2C requests from the + incoming I2C bus over the GMSL2 link. Therefore it supports an i2c-gate + subnode to configure a serializer. + + port0-poc-supply: + description: Regulator providing Power over Coax for the GMSL port + +required: + - compatible + - reg + - ports + +additionalProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + deserializer@28 { + compatible = "maxim,max96714f"; + reg = <0x28>; + powerdown-gpios = <&main_gpio0 37 GPIO_ACTIVE_LOW>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + max96714_gmsl_in: endpoint { + remote-endpoint = <&max96917f_gmsl_out>; + }; + }; + + port@1 { + reg = <1>; + max96714_csi_out: endpoint { + data-lanes = <1 2 3 4>; + link-frequencies = /bits/ 64 <400000000>; + remote-endpoint = <&csi_in>; + }; + }; + }; + + i2c-gate { + #address-cells = <1>; + #size-cells = <0>; + + serializer@40 { + compatible = "maxim,max96717f"; + reg = <0x40>; + gpio-controller; + #gpio-cells = <2>; + #clock-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + max96717f_csi_in: endpoint { + data-lanes = <1 2>; + lane-polarities = <1 0 1>; + remote-endpoint = <&sensor_out>; + }; + }; + + port@1 { + reg = <1>; + max96917f_gmsl_out: endpoint { + remote-endpoint = <&max96714_gmsl_in>; + }; + }; + }; + }; + }; + }; + }; +... -- cgit v1.2.3 From 50f222455793c2769bdde1152f930f0b1cc05634 Mon Sep 17 00:00:00 2001 From: Julien Massot Date: Tue, 30 Apr 2024 15:19:29 +0200 Subject: media: i2c: add MAX96717 driver This driver handles the MAX96717 serializer in tunnel mode. All incoming CSI traffic will be tunneled through the GMSL2 link. The MAX96717 driver can handle MAX96717 and MAX96717F variants with the same "maxim,max96717f" compatible. Signed-off-by: Julien Massot [Sakari Ailus: Adapt to changed i2c_mux_add_adapter arguments.] Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- MAINTAINERS | 7 + drivers/media/i2c/Kconfig | 17 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/max96717.c | 928 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 953 insertions(+) create mode 100644 drivers/media/i2c/max96717.c diff --git a/MAINTAINERS b/MAINTAINERS index 1bd50b5895f8..5e9b1cb24bee 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13514,6 +13514,13 @@ S: Maintained F: Documentation/devicetree/bindings/media/i2c/maxim,max96712.yaml F: drivers/staging/media/max96712/max96712.c +MAX96717 GMSL2 SERIALIZER DRIVER +M: Julien Massot +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/maxim,max96717.yaml +F: drivers/media/i2c/max96717.c + MAX9860 MONO AUDIO VOICE CODEC DRIVER M: Peter Rosin L: alsa-devel@alsa-project.org (moderated for non-subscribers) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 9450de9eedb6..8f0f92a7e0f0 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1586,6 +1586,23 @@ config VIDEO_DS90UB960 Device driver for the Texas Instruments DS90UB960 FPD-Link III Deserializer and DS90UB9702 FPD-Link IV Deserializer. +config VIDEO_MAX96717 + tristate "Maxim MAX96717 GMSL2 Serializer support" + depends on OF && I2C && VIDEO_DEV && COMMON_CLK + select I2C_MUX + select MEDIA_CONTROLLER + select GPIOLIB + select V4L2_CCI_I2C + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + help + Device driver for the Maxim MAX96717 GMSL2 Serializer. + MAX96717 serializers convert video on a MIPI CSI-2 + input to a GMSL2 output. + + To compile this driver as a module, choose M here: the + module will be called max96717. + endmenu endif # VIDEO_DEV diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 0fbd81f9f420..e0e4fcdae12a 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_VIDEO_LM3646) += lm3646.o obj-$(CONFIG_VIDEO_M52790) += m52790.o obj-$(CONFIG_VIDEO_MAX9271_LIB) += max9271.o obj-$(CONFIG_VIDEO_MAX9286) += max9286.o +obj-$(CONFIG_VIDEO_MAX96717) += max96717.o obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o obj-$(CONFIG_VIDEO_MT9M001) += mt9m001.o diff --git a/drivers/media/i2c/max96717.c b/drivers/media/i2c/max96717.c new file mode 100644 index 000000000000..62df822a193f --- /dev/null +++ b/drivers/media/i2c/max96717.c @@ -0,0 +1,928 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim GMSL2 Serializer Driver + * + * Copyright (C) 2024 Collabora Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MAX96717_DEVICE_ID 0xbf +#define MAX96717F_DEVICE_ID 0xc8 +#define MAX96717_PORTS 2 +#define MAX96717_PAD_SINK 0 +#define MAX96717_PAD_SOURCE 1 + +#define MAX96717_DEFAULT_CLKOUT_RATE 24000000UL + +/* DEV */ +#define MAX96717_REG3 CCI_REG8(0x3) +#define MAX96717_RCLKSEL GENMASK(1, 0) +#define RCLKSEL_REF_PLL CCI_REG8(0x3) +#define MAX96717_REG6 CCI_REG8(0x6) +#define RCLKEN BIT(5) +#define MAX96717_DEV_ID CCI_REG8(0xd) +#define MAX96717_DEV_REV CCI_REG8(0xe) +#define MAX96717_DEV_REV_MASK GENMASK(3, 0) + +/* VID_TX Z */ +#define MAX96717_VIDEO_TX2 CCI_REG8(0x112) +#define MAX96717_VIDEO_PCLKDET BIT(7) + +/* GPIO */ +#define MAX96717_NUM_GPIO 11 +#define MAX96717_GPIO_REG_A(gpio) CCI_REG8(0x2be + (gpio) * 3) +#define MAX96717_GPIO_OUT BIT(4) +#define MAX96717_GPIO_IN BIT(3) +#define MAX96717_GPIO_RX_EN BIT(2) +#define MAX96717_GPIO_TX_EN BIT(1) +#define MAX96717_GPIO_OUT_DIS BIT(0) + +/* FRONTTOP */ +/* MAX96717 only have CSI port 'B' */ +#define MAX96717_FRONTOP0 CCI_REG8(0x308) +#define MAX96717_START_PORT_B BIT(5) + +/* MIPI_RX */ +#define MAX96717_MIPI_RX1 CCI_REG8(0x331) +#define MAX96717_MIPI_LANES_CNT GENMASK(5, 4) +#define MAX96717_MIPI_RX2 CCI_REG8(0x332) /* phy1 Lanes map */ +#define MAX96717_PHY2_LANES_MAP GENMASK(7, 4) +#define MAX96717_MIPI_RX3 CCI_REG8(0x333) /* phy2 Lanes map */ +#define MAX96717_PHY1_LANES_MAP GENMASK(3, 0) +#define MAX96717_MIPI_RX4 CCI_REG8(0x334) /* phy1 lane polarities */ +#define MAX96717_PHY1_LANES_POL GENMASK(6, 4) +#define MAX96717_MIPI_RX5 CCI_REG8(0x335) /* phy2 lane polarities */ +#define MAX96717_PHY2_LANES_POL GENMASK(2, 0) + +/* MIPI_RX_EXT */ +#define MAX96717_MIPI_RX_EXT11 CCI_REG8(0x383) +#define MAX96717_TUN_MODE BIT(7) + +/* REF_VTG */ +#define REF_VTG0 CCI_REG8(0x3f0) +#define REFGEN_PREDEF_EN BIT(6) +#define REFGEN_PREDEF_FREQ_MASK GENMASK(5, 4) +#define REFGEN_PREDEF_FREQ_ALT BIT(3) +#define REFGEN_RST BIT(1) +#define REFGEN_EN BIT(0) + +/* MISC */ +#define PIO_SLEW_1 CCI_REG8(0x570) + +struct max96717_priv { + struct i2c_client *client; + struct regmap *regmap; + struct i2c_mux_core *mux; + struct v4l2_mbus_config_mipi_csi2 mipi_csi2; + struct v4l2_subdev sd; + struct media_pad pads[MAX96717_PORTS]; + struct v4l2_async_notifier notifier; + struct v4l2_subdev *source_sd; + u16 source_sd_pad; + u64 enabled_source_streams; + u8 pll_predef_index; + struct clk_hw clk_hw; + struct gpio_chip gpio_chip; +}; + +static inline struct max96717_priv *sd_to_max96717(struct v4l2_subdev *sd) +{ + return container_of(sd, struct max96717_priv, sd); +} + +static inline struct max96717_priv *clk_hw_to_max96717(struct clk_hw *hw) +{ + return container_of(hw, struct max96717_priv, clk_hw); +} + +static int max96717_i2c_mux_select(struct i2c_mux_core *mux, u32 chan) +{ + return 0; +} + +static int max96717_i2c_mux_init(struct max96717_priv *priv) +{ + priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev, + 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE, + max96717_i2c_mux_select, NULL); + if (!priv->mux) + return -ENOMEM; + + return i2c_mux_add_adapter(priv->mux, 0, 0); +} + +static inline int max96717_start_csi(struct max96717_priv *priv, bool start) +{ + return cci_update_bits(priv->regmap, MAX96717_FRONTOP0, + MAX96717_START_PORT_B, + start ? MAX96717_START_PORT_B : 0, NULL); +} + +static int max96717_gpiochip_get(struct gpio_chip *gpiochip, + unsigned int offset) +{ + struct max96717_priv *priv = gpiochip_get_data(gpiochip); + u64 val; + int ret; + + ret = cci_read(priv->regmap, MAX96717_GPIO_REG_A(offset), + &val, NULL); + if (ret) + return ret; + + if (val & MAX96717_GPIO_OUT_DIS) + return !!(val & MAX96717_GPIO_IN); + else + return !!(val & MAX96717_GPIO_OUT); +} + +static void max96717_gpiochip_set(struct gpio_chip *gpiochip, + unsigned int offset, int value) +{ + struct max96717_priv *priv = gpiochip_get_data(gpiochip); + + cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(offset), + MAX96717_GPIO_OUT, MAX96717_GPIO_OUT, NULL); +} + +static int max96717_gpio_get_direction(struct gpio_chip *gpiochip, + unsigned int offset) +{ + struct max96717_priv *priv = gpiochip_get_data(gpiochip); + u64 val; + int ret; + + ret = cci_read(priv->regmap, MAX96717_GPIO_REG_A(offset), &val, NULL); + if (ret < 0) + return ret; + + return !!(val & MAX96717_GPIO_OUT_DIS); +} + +static int max96717_gpio_direction_out(struct gpio_chip *gpiochip, + unsigned int offset, int value) +{ + struct max96717_priv *priv = gpiochip_get_data(gpiochip); + + return cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(offset), + MAX96717_GPIO_OUT_DIS | MAX96717_GPIO_OUT, + value ? MAX96717_GPIO_OUT : 0, NULL); +} + +static int max96717_gpio_direction_in(struct gpio_chip *gpiochip, + unsigned int offset) +{ + struct max96717_priv *priv = gpiochip_get_data(gpiochip); + + return cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(offset), + MAX96717_GPIO_OUT_DIS, MAX96717_GPIO_OUT_DIS, + NULL); +} + +static int max96717_gpiochip_probe(struct max96717_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct gpio_chip *gc = &priv->gpio_chip; + int i, ret = 0; + + gc->label = dev_name(dev); + gc->parent = dev; + gc->owner = THIS_MODULE; + gc->ngpio = MAX96717_NUM_GPIO; + gc->base = -1; + gc->can_sleep = true; + gc->get_direction = max96717_gpio_get_direction; + gc->direction_input = max96717_gpio_direction_in; + gc->direction_output = max96717_gpio_direction_out; + gc->set = max96717_gpiochip_set; + gc->get = max96717_gpiochip_get; + gc->of_gpio_n_cells = 2; + + /* Disable GPIO forwarding */ + for (i = 0; i < gc->ngpio; i++) + cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(i), + MAX96717_GPIO_RX_EN | MAX96717_GPIO_TX_EN, + 0, &ret); + + if (ret) + return ret; + + ret = devm_gpiochip_add_data(dev, gc, priv); + if (ret) { + dev_err(dev, "Unable to create gpio_chip\n"); + return ret; + } + + return 0; +} + +static int _max96717_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_krouting *routing) +{ + static const struct v4l2_mbus_framefmt format = { + .width = 1280, + .height = 1080, + .code = MEDIA_BUS_FMT_Y8_1X8, + .field = V4L2_FIELD_NONE, + }; + int ret; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); + if (ret) + return ret; + + ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); + if (ret) + return ret; + + return 0; +} + +static int max96717_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct max96717_priv *priv = sd_to_max96717(sd); + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->enabled_source_streams) + return -EBUSY; + + return _max96717_set_routing(sd, state, routing); +} + +static int max96717_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct max96717_priv *priv = sd_to_max96717(sd); + struct v4l2_mbus_framefmt *fmt; + u64 stream_source_mask; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && + priv->enabled_source_streams) + return -EBUSY; + + /* No transcoding, source and sink formats must match. */ + if (format->pad == MAX96717_PAD_SOURCE) + return v4l2_subdev_get_fmt(sd, state, format); + + /* Set sink format */ + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + /* Propagate to source format */ + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + *fmt = format->format; + + stream_source_mask = BIT(format->stream); + + return v4l2_subdev_state_xlate_streams(state, MAX96717_PAD_SOURCE, + MAX96717_PAD_SINK, + &stream_source_mask); +} + +static int max96717_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route routes[] = { + { + .sink_pad = MAX96717_PAD_SINK, + .sink_stream = 0, + .source_pad = MAX96717_PAD_SOURCE, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + }, + }; + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + return _max96717_set_routing(sd, state, &routing); +} + +static bool max96717_pipe_pclkdet(struct max96717_priv *priv) +{ + u64 val = 0; + + cci_read(priv->regmap, MAX96717_VIDEO_TX2, &val, NULL); + + return val & MAX96717_VIDEO_PCLKDET; +} + +static int max96717_log_status(struct v4l2_subdev *sd) +{ + struct max96717_priv *priv = sd_to_max96717(sd); + struct device *dev = &priv->client->dev; + + dev_info(dev, "Serializer: max96717\n"); + dev_info(dev, "Pipe: pclkdet:%d\n", max96717_pipe_pclkdet(priv)); + + return 0; +} + +static int max96717_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct max96717_priv *priv = sd_to_max96717(sd); + struct device *dev = &priv->client->dev; + u64 sink_streams; + int ret; + + sink_streams = v4l2_subdev_state_xlate_streams(state, + MAX96717_PAD_SOURCE, + MAX96717_PAD_SINK, + &streams_mask); + + if (!priv->enabled_source_streams) + max96717_start_csi(priv, true); + + ret = v4l2_subdev_enable_streams(priv->source_sd, priv->source_sd_pad, + sink_streams); + if (ret) { + dev_err(dev, "Fail to start streams:%llu on remote subdev\n", + sink_streams); + goto stop_csi; + } + + priv->enabled_source_streams |= streams_mask; + + return 0; + +stop_csi: + if (!priv->enabled_source_streams) + max96717_start_csi(priv, false); + return ret; +} + +static int max96717_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 streams_mask) +{ + struct max96717_priv *priv = sd_to_max96717(sd); + u64 sink_streams; + int ret; + + sink_streams = v4l2_subdev_state_xlate_streams(state, + MAX96717_PAD_SOURCE, + MAX96717_PAD_SINK, + &streams_mask); + + ret = v4l2_subdev_disable_streams(priv->source_sd, priv->source_sd_pad, + sink_streams); + if (ret) + return ret; + + priv->enabled_source_streams &= ~streams_mask; + + if (!priv->enabled_source_streams) + max96717_start_csi(priv, false); + + return 0; +} + +static const struct v4l2_subdev_pad_ops max96717_pad_ops = { + .enable_streams = max96717_enable_streams, + .disable_streams = max96717_disable_streams, + .set_routing = max96717_set_routing, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = max96717_set_fmt, +}; + +static const struct v4l2_subdev_core_ops max96717_subdev_core_ops = { + .log_status = max96717_log_status, +}; + +static const struct v4l2_subdev_internal_ops max96717_internal_ops = { + .init_state = max96717_init_state, +}; + +static const struct v4l2_subdev_ops max96717_subdev_ops = { + .core = &max96717_subdev_core_ops, + .pad = &max96717_pad_ops, +}; + +static const struct media_entity_operations max96717_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int max96717_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *source_subdev, + struct v4l2_async_connection *asd) +{ + struct max96717_priv *priv = sd_to_max96717(notifier->sd); + struct device *dev = &priv->client->dev; + int ret; + + ret = media_entity_get_fwnode_pad(&source_subdev->entity, + source_subdev->fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(dev, "Failed to find pad for %s\n", + source_subdev->name); + return ret; + } + + priv->source_sd = source_subdev; + priv->source_sd_pad = ret; + + ret = media_create_pad_link(&source_subdev->entity, priv->source_sd_pad, + &priv->sd.entity, 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(dev, "Unable to link %s:%u -> %s:0\n", + source_subdev->name, priv->source_sd_pad, + priv->sd.name); + return ret; + } + + return 0; +} + +static const struct v4l2_async_notifier_operations max96717_notify_ops = { + .bound = max96717_notify_bound, +}; + +static int max96717_v4l2_notifier_register(struct max96717_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct v4l2_async_connection *asd; + struct fwnode_handle *ep_fwnode; + int ret; + + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + MAX96717_PAD_SINK, 0, 0); + if (!ep_fwnode) { + dev_err(dev, "No graph endpoint\n"); + return -ENODEV; + } + + v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd); + + asd = v4l2_async_nf_add_fwnode_remote(&priv->notifier, ep_fwnode, + struct v4l2_async_connection); + + fwnode_handle_put(ep_fwnode); + + if (IS_ERR(asd)) { + dev_err(dev, "Failed to add subdev: %ld", PTR_ERR(asd)); + v4l2_async_nf_cleanup(&priv->notifier); + return PTR_ERR(asd); + } + + priv->notifier.ops = &max96717_notify_ops; + + ret = v4l2_async_nf_register(&priv->notifier); + if (ret) { + dev_err(dev, "Failed to register subdev_notifier"); + v4l2_async_nf_cleanup(&priv->notifier); + return ret; + } + + return 0; +} + +static int max96717_subdev_init(struct max96717_priv *priv) +{ + struct device *dev = &priv->client->dev; + int ret; + + v4l2_i2c_subdev_init(&priv->sd, priv->client, &max96717_subdev_ops); + priv->sd.internal_ops = &max96717_internal_ops; + + priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; + priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + priv->sd.entity.ops = &max96717_entity_ops; + + priv->pads[MAX96717_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + priv->pads[MAX96717_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&priv->sd.entity, 2, priv->pads); + if (ret) + return dev_err_probe(dev, ret, "Failed to init pads\n"); + + ret = v4l2_subdev_init_finalize(&priv->sd); + if (ret) { + dev_err_probe(dev, ret, + "v4l2 subdev init finalized failed\n"); + goto err_entity_cleanup; + } + ret = max96717_v4l2_notifier_register(priv); + if (ret) { + dev_err_probe(dev, ret, + "v4l2 subdev notifier register failed\n"); + goto err_free_state; + } + + ret = v4l2_async_register_subdev(&priv->sd); + if (ret) { + dev_err_probe(dev, ret, "v4l2_async_register_subdev error\n"); + goto err_unreg_notif; + } + + return 0; + +err_unreg_notif: + v4l2_async_nf_unregister(&priv->notifier); + v4l2_async_nf_cleanup(&priv->notifier); +err_free_state: + v4l2_subdev_cleanup(&priv->sd); +err_entity_cleanup: + media_entity_cleanup(&priv->sd.entity); + + return ret; +} + +static void max96717_subdev_uninit(struct max96717_priv *priv) +{ + v4l2_async_unregister_subdev(&priv->sd); + v4l2_async_nf_unregister(&priv->notifier); + v4l2_async_nf_cleanup(&priv->notifier); + v4l2_subdev_cleanup(&priv->sd); + media_entity_cleanup(&priv->sd.entity); +} + +struct max96717_pll_predef_freq { + unsigned long freq; + bool is_alt; + u8 val; +}; + +static const struct max96717_pll_predef_freq max96717_predef_freqs[] = { + { 13500000, true, 0 }, { 19200000, false, 0 }, + { 24000000, true, 1 }, { 27000000, false, 1 }, + { 37125000, false, 2 }, { 74250000, false, 3 }, +}; + +static unsigned long +max96717_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct max96717_priv *priv = clk_hw_to_max96717(hw); + + return max96717_predef_freqs[priv->pll_predef_index].freq; +} + +static unsigned int max96717_clk_find_best_index(struct max96717_priv *priv, + unsigned long rate) +{ + unsigned int i, idx; + unsigned long diff_new, diff_old; + + diff_old = U32_MAX; + idx = 0; + + for (i = 0; i < ARRAY_SIZE(max96717_predef_freqs); i++) { + diff_new = abs(rate - max96717_predef_freqs[i].freq); + if (diff_new < diff_old) { + diff_old = diff_new; + idx = i; + } + } + + return idx; +} + +static long max96717_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct max96717_priv *priv = clk_hw_to_max96717(hw); + struct device *dev = &priv->client->dev; + unsigned int idx; + + idx = max96717_clk_find_best_index(priv, rate); + + if (rate != max96717_predef_freqs[idx].freq) { + dev_warn(dev, "Request CLK freq:%lu, found CLK freq:%lu\n", + rate, max96717_predef_freqs[idx].freq); + } + + return max96717_predef_freqs[idx].freq; +} + +static int max96717_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct max96717_priv *priv = clk_hw_to_max96717(hw); + unsigned int val, idx; + int ret = 0; + + idx = max96717_clk_find_best_index(priv, rate); + + val = FIELD_PREP(REFGEN_PREDEF_FREQ_MASK, + max96717_predef_freqs[idx].val); + + if (max96717_predef_freqs[idx].is_alt) + val |= REFGEN_PREDEF_FREQ_ALT; + + val |= REFGEN_RST | REFGEN_PREDEF_EN; + + cci_write(priv->regmap, REF_VTG0, val, &ret); + cci_update_bits(priv->regmap, REF_VTG0, REFGEN_RST | REFGEN_EN, + REFGEN_EN, &ret); + if (ret) + return ret; + + priv->pll_predef_index = idx; + + return 0; +} + +static int max96717_clk_prepare(struct clk_hw *hw) +{ + struct max96717_priv *priv = clk_hw_to_max96717(hw); + + return cci_update_bits(priv->regmap, MAX96717_REG6, RCLKEN, + RCLKEN, NULL); +} + +static void max96717_clk_unprepare(struct clk_hw *hw) +{ + struct max96717_priv *priv = clk_hw_to_max96717(hw); + + cci_update_bits(priv->regmap, MAX96717_REG6, RCLKEN, 0, NULL); +} + +static const struct clk_ops max96717_clk_ops = { + .prepare = max96717_clk_prepare, + .unprepare = max96717_clk_unprepare, + .set_rate = max96717_clk_set_rate, + .recalc_rate = max96717_clk_recalc_rate, + .round_rate = max96717_clk_round_rate, +}; + +static int max96717_register_clkout(struct max96717_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct clk_init_data init = { .ops = &max96717_clk_ops }; + int ret; + + init.name = kasprintf(GFP_KERNEL, "max96717.%s.clk_out", + dev_name(dev)); + if (!init.name) + return -ENOMEM; + + /* RCLKSEL Reference PLL output */ + ret = cci_update_bits(priv->regmap, MAX96717_REG3, MAX96717_RCLKSEL, + MAX96717_RCLKSEL, NULL); + /* MFP4 fastest slew rate */ + cci_update_bits(priv->regmap, PIO_SLEW_1, BIT(5) | BIT(4), 0, &ret); + if (ret) + goto free_init_name; + + priv->clk_hw.init = &init; + + /* Initialize to 24 MHz */ + ret = max96717_clk_set_rate(&priv->clk_hw, + MAX96717_DEFAULT_CLKOUT_RATE, 0); + if (ret < 0) + goto free_init_name; + + ret = devm_clk_hw_register(dev, &priv->clk_hw); + kfree(init.name); + if (ret) + return dev_err_probe(dev, ret, "Cannot register clock HW\n"); + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &priv->clk_hw); + if (ret) + return dev_err_probe(dev, ret, + "Cannot add OF clock provider\n"); + + return 0; + +free_init_name: + kfree(init.name); + return ret; +} + +static int max96717_init_csi_lanes(struct max96717_priv *priv) +{ + struct v4l2_mbus_config_mipi_csi2 *mipi = &priv->mipi_csi2; + unsigned long lanes_used = 0; + unsigned int nlanes, lane, val = 0; + int ret; + + nlanes = mipi->num_data_lanes; + + ret = cci_update_bits(priv->regmap, MAX96717_MIPI_RX1, + MAX96717_MIPI_LANES_CNT, + FIELD_PREP(MAX96717_MIPI_LANES_CNT, + nlanes - 1), NULL); + + /* lanes polarity */ + for (lane = 0; lane < nlanes + 1; lane++) { + if (!mipi->lane_polarities[lane]) + continue; + /* Clock lane */ + if (lane == 0) + val |= BIT(2); + else if (lane < 3) + val |= BIT(lane - 1); + else + val |= BIT(lane); + } + + cci_update_bits(priv->regmap, MAX96717_MIPI_RX5, + MAX96717_PHY2_LANES_POL, + FIELD_PREP(MAX96717_PHY2_LANES_POL, val), &ret); + + cci_update_bits(priv->regmap, MAX96717_MIPI_RX4, + MAX96717_PHY1_LANES_POL, + FIELD_PREP(MAX96717_PHY1_LANES_POL, + val >> 3), &ret); + /* lanes mapping */ + for (lane = 0, val = 0; lane < nlanes; lane++) { + val |= (mipi->data_lanes[lane] - 1) << (lane * 2); + lanes_used |= BIT(mipi->data_lanes[lane] - 1); + } + + /* + * Unused lanes need to be mapped as well to not have + * the same lanes mapped twice. + */ + for (; lane < 4; lane++) { + unsigned int idx = find_first_zero_bit(&lanes_used, 4); + + val |= idx << (lane * 2); + lanes_used |= BIT(idx); + } + + cci_update_bits(priv->regmap, MAX96717_MIPI_RX3, + MAX96717_PHY1_LANES_MAP, + FIELD_PREP(MAX96717_PHY1_LANES_MAP, val), &ret); + + return cci_update_bits(priv->regmap, MAX96717_MIPI_RX2, + MAX96717_PHY2_LANES_MAP, + FIELD_PREP(MAX96717_PHY2_LANES_MAP, val >> 4), + &ret); +} + +static int max96717_hw_init(struct max96717_priv *priv) +{ + struct device *dev = &priv->client->dev; + u64 dev_id, val; + int ret; + + ret = cci_read(priv->regmap, MAX96717_DEV_ID, &dev_id, NULL); + if (ret) + return dev_err_probe(dev, ret, + "Fail to read the device id\n"); + + if (dev_id != MAX96717_DEVICE_ID && dev_id != MAX96717F_DEVICE_ID) + return dev_err_probe(dev, -EOPNOTSUPP, + "Unsupported device id got %x\n", (u8)dev_id); + + ret = cci_read(priv->regmap, MAX96717_DEV_REV, &val, NULL); + if (ret) + return dev_err_probe(dev, ret, + "Fail to read device revision"); + + dev_dbg(dev, "Found %x (rev %lx)\n", (u8)dev_id, + (u8)val & MAX96717_DEV_REV_MASK); + + ret = cci_read(priv->regmap, MAX96717_MIPI_RX_EXT11, &val, NULL); + if (ret) + return dev_err_probe(dev, ret, + "Fail to read mipi rx extension"); + + if (!(val & MAX96717_TUN_MODE)) + return dev_err_probe(dev, -EOPNOTSUPP, + "Only supporting tunnel mode"); + + return max96717_init_csi_lanes(priv); +} + +static int max96717_parse_dt(struct max96717_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct fwnode_handle *ep_fwnode; + unsigned char num_data_lanes; + int ret; + + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + MAX96717_PAD_SINK, 0, 0); + if (!ep_fwnode) + return dev_err_probe(dev, -ENOENT, "no endpoint found\n"); + + ret = v4l2_fwnode_endpoint_parse(ep_fwnode, &vep); + + fwnode_handle_put(ep_fwnode); + + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to parse sink endpoint"); + + num_data_lanes = vep.bus.mipi_csi2.num_data_lanes; + if (num_data_lanes < 1 || num_data_lanes > 4) + return dev_err_probe(dev, -EINVAL, + "Invalid data lanes must be 1 to 4\n"); + + memcpy(&priv->mipi_csi2, &vep.bus.mipi_csi2, sizeof(priv->mipi_csi2)); + + return 0; +} + +static int max96717_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct max96717_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->client = client; + priv->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + return dev_err_probe(dev, ret, "Failed to init regmap\n"); + } + + ret = max96717_parse_dt(priv); + if (ret) + return dev_err_probe(dev, ret, "Failed to parse the dt\n"); + + ret = max96717_hw_init(priv); + if (ret) + return dev_err_probe(dev, ret, + "Failed to initialize the hardware\n"); + + ret = max96717_gpiochip_probe(priv); + if (ret) + return dev_err_probe(&client->dev, ret, + "Failed to init gpiochip\n"); + + ret = max96717_register_clkout(priv); + if (ret) + return dev_err_probe(dev, ret, "Failed to register clkout\n"); + + ret = max96717_subdev_init(priv); + if (ret) + return dev_err_probe(dev, ret, + "Failed to initialize v4l2 subdev\n"); + + ret = max96717_i2c_mux_init(priv); + if (ret) { + dev_err_probe(dev, ret, "failed to add remote i2c adapter\n"); + max96717_subdev_uninit(priv); + } + + return ret; +} + +static void max96717_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct max96717_priv *priv = sd_to_max96717(sd); + + max96717_subdev_uninit(priv); + i2c_mux_del_adapters(priv->mux); +} + +static const struct of_device_id max96717_of_ids[] = { + { .compatible = "maxim,max96717f" }, + { } +}; +MODULE_DEVICE_TABLE(of, max96717_of_ids); + +static struct i2c_driver max96717_i2c_driver = { + .driver = { + .name = "max96717", + .of_match_table = max96717_of_ids, + }, + .probe = max96717_probe, + .remove = max96717_remove, +}; + +module_i2c_driver(max96717_i2c_driver); + +MODULE_DESCRIPTION("Maxim GMSL2 MAX96717 Serializer Driver"); +MODULE_AUTHOR("Julien Massot "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 37a638e9bf998581655d012a36ee36ed4b4bfc94 Mon Sep 17 00:00:00 2001 From: Julien Massot Date: Tue, 30 Apr 2024 15:19:30 +0200 Subject: media: i2c: add MAX96714 driver This driver handles the MAX96714 deserializer in tunnel mode. The CSI output will replicate all the CSI traffic forwarded by the remote serializer. The MAX96714 driver can handle MAX96714 and MAX96714F variants with the same "maxim,max96714f" compatible. Signed-off-by: Julien Massot [Sakari Ailus: Adapt to changed i2c_mux_add_adapter arguments.] Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- MAINTAINERS | 7 + drivers/media/i2c/Kconfig | 17 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/max96714.c | 1024 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1049 insertions(+) create mode 100644 drivers/media/i2c/max96714.c diff --git a/MAINTAINERS b/MAINTAINERS index 5e9b1cb24bee..e7569220bffc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13514,6 +13514,13 @@ S: Maintained F: Documentation/devicetree/bindings/media/i2c/maxim,max96712.yaml F: drivers/staging/media/max96712/max96712.c +MAX96714 GMSL2 DESERIALIZER DRIVER +M: Julien Massot +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/maxim,max96714.yaml +F: drivers/media/i2c/max96714.c + MAX96717 GMSL2 SERIALIZER DRIVER M: Julien Massot L: linux-media@vger.kernel.org diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 8f0f92a7e0f0..5498128773c7 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1586,6 +1586,23 @@ config VIDEO_DS90UB960 Device driver for the Texas Instruments DS90UB960 FPD-Link III Deserializer and DS90UB9702 FPD-Link IV Deserializer. +config VIDEO_MAX96714 + tristate "Maxim MAX96714 GMSL2 deserializer" + depends on OF && I2C && VIDEO_DEV + select I2C_MUX + select MEDIA_CONTROLLER + select GPIOLIB + select V4L2_CCI_I2C + select V4L2_FWNODE + select VIDEO_V4L2_SUBDEV_API + help + Device driver for the Maxim MAX96714 GMSL2 Deserializer. + MAX96714 deserializers convert a GMSL2 input to MIPI CSI-2 + output. + + To compile this driver as a module, choose M here: the + module will be called max96714. + config VIDEO_MAX96717 tristate "Maxim MAX96717 GMSL2 Serializer support" depends on OF && I2C && VIDEO_DEV && COMMON_CLK diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index e0e4fcdae12a..d1403e3273ca 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_VIDEO_LM3646) += lm3646.o obj-$(CONFIG_VIDEO_M52790) += m52790.o obj-$(CONFIG_VIDEO_MAX9271_LIB) += max9271.o obj-$(CONFIG_VIDEO_MAX9286) += max9286.o +obj-$(CONFIG_VIDEO_MAX96714) += max96714.o obj-$(CONFIG_VIDEO_MAX96717) += max96717.o obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o diff --git a/drivers/media/i2c/max96714.c b/drivers/media/i2c/max96714.c new file mode 100644 index 000000000000..c97de66631e0 --- /dev/null +++ b/drivers/media/i2c/max96714.c @@ -0,0 +1,1024 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim GMSL2 Deserializer Driver + * + * Copyright (C) 2024 Collabora Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MAX96714_DEVICE_ID 0xc9 +#define MAX96714F_DEVICE_ID 0xca +#define MAX96714_NPORTS 2 +#define MAX96714_PAD_SINK 0 +#define MAX96714_PAD_SOURCE 1 + +/* DEV */ +#define MAX96714_REG13 CCI_REG8(0x0d) +#define MAX96714_DEV_REV CCI_REG8(0x0e) +#define MAX96714_DEV_REV_MASK GENMASK(3, 0) +#define MAX96714_LINK_LOCK CCI_REG8(0x13) +#define MAX96714_LINK_LOCK_BIT BIT(3) +#define MAX96714_IO_CHK0 CCI_REG8(0x38) +#define MAX96714_PATTERN_CLK_FREQ GENMASK(1, 0) +/* VID_RX */ +#define MAX96714_VIDEO_RX8 CCI_REG8(0x11a) +#define MAX96714_VID_LOCK BIT(6) + +/* VRX_PATGEN_0 */ +#define MAX96714_PATGEN_0 CCI_REG8(0x240) +#define MAX96714_PATGEN_1 CCI_REG8(0x241) +#define MAX96714_PATGEN_MODE GENMASK(5, 4) +#define MAX96714_PATGEN_VS_DLY CCI_REG24(0x242) +#define MAX96714_PATGEN_VS_HIGH CCI_REG24(0x245) +#define MAX96714_PATGEN_VS_LOW CCI_REG24(0x248) +#define MAX96714_PATGEN_V2H CCI_REG24(0x24b) +#define MAX96714_PATGEN_HS_HIGH CCI_REG16(0x24e) +#define MAX96714_PATGEN_HS_LOW CCI_REG16(0x250) +#define MAX96714_PATGEN_HS_CNT CCI_REG16(0x252) +#define MAX96714_PATGEN_V2D CCI_REG24(0x254) +#define MAX96714_PATGEN_DE_HIGH CCI_REG16(0x257) +#define MAX96714_PATGEN_DE_LOW CCI_REG16(0x259) +#define MAX96714_PATGEN_DE_CNT CCI_REG16(0x25B) +#define MAX96714_PATGEN_GRAD_INC CCI_REG8(0x25d) +#define MAX96714_PATGEN_CHKB_COLOR_A CCI_REG24(0x25E) +#define MAX96714_PATGEN_CHKB_COLOR_B CCI_REG24(0x261) +#define MAX96714_PATGEN_CHKB_RPT_CNT_A CCI_REG8(0x264) +#define MAX96714_PATGEN_CHKB_RPT_CNT_B CCI_REG8(0x265) +#define MAX96714_PATGEN_CHKB_ALT CCI_REG8(0x266) +/* BACKTOP */ +#define MAX96714_BACKTOP25 CCI_REG8(0x320) +#define CSI_DPLL_FREQ_MASK GENMASK(4, 0) + +/* MIPI_PHY */ +#define MAX96714_MIPI_PHY0 CCI_REG8(0x330) +#define MAX96714_FORCE_CSI_OUT BIT(7) +#define MAX96714_MIPI_STDBY_N CCI_REG8(0x332) +#define MAX96714_MIPI_STDBY_MASK GENMASK(5, 4) +#define MAX96714_MIPI_LANE_MAP CCI_REG8(0x333) +#define MAX96714_MIPI_POLARITY CCI_REG8(0x335) +#define MAX96714_MIPI_POLARITY_MASK GENMASK(5, 0) + +/* MIPI_TX */ +#define MAX96714_MIPI_LANE_CNT CCI_REG8(0x44a) +#define MAX96714_CSI2_LANE_CNT_MASK GENMASK(7, 6) +#define MAX96714_MIPI_TX52 CCI_REG8(0x474) +#define MAX96714_TUN_EN BIT(0) + +#define MHZ(v) ((u32)((v) * 1000000U)) + +enum max96714_vpg_mode { + MAX96714_VPG_DISABLED = 0, + MAX96714_VPG_CHECKERBOARD = 1, + MAX96714_VPG_GRADIENT = 2, +}; + +struct max96714_rxport { + struct { + struct v4l2_subdev *sd; + u16 pad; + struct fwnode_handle *ep_fwnode; + } source; + struct regulator *poc; +}; + +struct max96714_txport { + struct v4l2_fwnode_endpoint vep; +}; + +struct max96714_priv { + struct i2c_client *client; + struct regmap *regmap; + struct gpio_desc *pd_gpio; + struct max96714_rxport rxport; + struct i2c_mux_core *mux; + u64 enabled_source_streams; + struct v4l2_subdev sd; + struct media_pad pads[MAX96714_NPORTS]; + struct v4l2_mbus_config_mipi_csi2 mipi_csi2; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_async_notifier notifier; + s64 tx_link_freq; + enum max96714_vpg_mode pattern; +}; + +static inline struct max96714_priv *sd_to_max96714(struct v4l2_subdev *sd) +{ + return container_of(sd, struct max96714_priv, sd); +} + +static int max96714_enable_tx_port(struct max96714_priv *priv) +{ + return cci_update_bits(priv->regmap, MAX96714_MIPI_STDBY_N, + MAX96714_MIPI_STDBY_MASK, + MAX96714_MIPI_STDBY_MASK, NULL); +} + +static int max96714_disable_tx_port(struct max96714_priv *priv) +{ + return cci_update_bits(priv->regmap, MAX96714_MIPI_STDBY_N, + MAX96714_MIPI_STDBY_MASK, 0, NULL); +} + +static bool max96714_tx_port_enabled(struct max96714_priv *priv) +{ + u64 val; + + cci_read(priv->regmap, MAX96714_MIPI_STDBY_N, &val, NULL); + + return val & MAX96714_MIPI_STDBY_MASK; +} + +static int max96714_apply_patgen_timing(struct max96714_priv *priv, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_state_get_format(state, MAX96714_PAD_SOURCE); + const u32 h_active = fmt->width; + const u32 h_fp = 88; + const u32 h_sw = 44; + const u32 h_bp = 148; + u32 h_tot; + const u32 v_active = fmt->height; + const u32 v_fp = 4; + const u32 v_sw = 5; + const u32 v_bp = 36; + u32 v_tot; + int ret = 0; + + h_tot = h_active + h_fp + h_sw + h_bp; + v_tot = v_active + v_fp + v_sw + v_bp; + + /* 75 Mhz pixel clock */ + cci_update_bits(priv->regmap, MAX96714_IO_CHK0, + MAX96714_PATTERN_CLK_FREQ, 1, &ret); + + dev_info(&priv->client->dev, "height: %d width: %d\n", fmt->height, + fmt->width); + + cci_write(priv->regmap, MAX96714_PATGEN_VS_DLY, 0, &ret); + cci_write(priv->regmap, MAX96714_PATGEN_VS_HIGH, v_sw * h_tot, &ret); + cci_write(priv->regmap, MAX96714_PATGEN_VS_LOW, + (v_active + v_fp + v_bp) * h_tot, &ret); + cci_write(priv->regmap, MAX96714_PATGEN_HS_HIGH, h_sw, &ret); + cci_write(priv->regmap, MAX96714_PATGEN_HS_LOW, h_active + h_fp + h_bp, + &ret); + cci_write(priv->regmap, MAX96714_PATGEN_V2D, + h_tot * (v_sw + v_bp) + (h_sw + h_bp), &ret); + cci_write(priv->regmap, MAX96714_PATGEN_HS_CNT, v_tot, &ret); + cci_write(priv->regmap, MAX96714_PATGEN_DE_HIGH, h_active, &ret); + cci_write(priv->regmap, MAX96714_PATGEN_DE_LOW, h_fp + h_sw + h_bp, + &ret); + cci_write(priv->regmap, MAX96714_PATGEN_DE_CNT, v_active, &ret); + /* B G R */ + cci_write(priv->regmap, MAX96714_PATGEN_CHKB_COLOR_A, 0xfecc00, &ret); + /* B G R */ + cci_write(priv->regmap, MAX96714_PATGEN_CHKB_COLOR_B, 0x006aa7, &ret); + cci_write(priv->regmap, MAX96714_PATGEN_CHKB_RPT_CNT_A, 0x3c, &ret); + cci_write(priv->regmap, MAX96714_PATGEN_CHKB_RPT_CNT_B, 0x3c, &ret); + cci_write(priv->regmap, MAX96714_PATGEN_CHKB_ALT, 0x3c, &ret); + cci_write(priv->regmap, MAX96714_PATGEN_GRAD_INC, 0x10, &ret); + + return ret; +} + +static int max96714_apply_patgen(struct max96714_priv *priv, + struct v4l2_subdev_state *state) +{ + unsigned int val; + int ret = 0; + + if (priv->pattern) + ret = max96714_apply_patgen_timing(priv, state); + + cci_write(priv->regmap, MAX96714_PATGEN_0, priv->pattern ? 0xfb : 0, + &ret); + + val = FIELD_PREP(MAX96714_PATGEN_MODE, priv->pattern); + cci_update_bits(priv->regmap, MAX96714_PATGEN_1, MAX96714_PATGEN_MODE, + val, &ret); + return ret; +} + +static int max96714_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct max96714_priv *priv = + container_of(ctrl->handler, struct max96714_priv, ctrl_handler); + int ret; + + switch (ctrl->id) { + case V4L2_CID_TEST_PATTERN: + if (priv->enabled_source_streams) + return -EBUSY; + priv->pattern = ctrl->val; + break; + default: + return -EINVAL; + } + + ret = cci_update_bits(priv->regmap, MAX96714_MIPI_PHY0, + MAX96714_FORCE_CSI_OUT, + priv->pattern ? MAX96714_FORCE_CSI_OUT : 0, NULL); + + /* Pattern generator doesn't work with tunnel mode */ + return cci_update_bits(priv->regmap, MAX96714_MIPI_TX52, + MAX96714_TUN_EN, + priv->pattern ? 0 : MAX96714_TUN_EN, &ret); +} + +static const char * const max96714_test_pattern[] = { + "Disabled", + "Checkerboard", + "Gradient" +}; + +static const struct v4l2_ctrl_ops max96714_ctrl_ops = { + .s_ctrl = max96714_s_ctrl, +}; + +static int max96714_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 source_pad, u64 streams_mask) +{ + struct max96714_priv *priv = sd_to_max96714(sd); + u64 sink_streams; + int ret; + + if (!priv->enabled_source_streams) + max96714_enable_tx_port(priv); + + ret = max96714_apply_patgen(priv, state); + if (ret) + goto err; + + if (!priv->pattern) { + if (!priv->rxport.source.sd) { + ret = -ENODEV; + goto err; + } + + sink_streams = + v4l2_subdev_state_xlate_streams(state, + MAX96714_PAD_SOURCE, + MAX96714_PAD_SINK, + &streams_mask); + + ret = v4l2_subdev_enable_streams(priv->rxport.source.sd, + priv->rxport.source.pad, + sink_streams); + if (ret) + goto err; + } + + priv->enabled_source_streams |= streams_mask; + + return 0; + +err: + if (!priv->enabled_source_streams) + max96714_disable_tx_port(priv); + + return ret; +} + +static int max96714_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 source_pad, u64 streams_mask) +{ + struct max96714_priv *priv = sd_to_max96714(sd); + u64 sink_streams; + + if (!priv->pattern) { + int ret; + + sink_streams = + v4l2_subdev_state_xlate_streams(state, + MAX96714_PAD_SOURCE, + MAX96714_PAD_SINK, + &streams_mask); + + ret = v4l2_subdev_disable_streams(priv->rxport.source.sd, + priv->rxport.source.pad, + sink_streams); + if (ret) + return ret; + } + + priv->enabled_source_streams &= ~streams_mask; + + if (!priv->enabled_source_streams) + max96714_disable_tx_port(priv); + + return 0; +} + +static int max96714_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *format) +{ + struct max96714_priv *priv = sd_to_max96714(sd); + struct v4l2_mbus_framefmt *fmt; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && + priv->enabled_source_streams) + return -EBUSY; + + /* No transcoding, source and sink formats must match. */ + if (format->pad == MAX96714_PAD_SOURCE) + return v4l2_subdev_get_fmt(sd, state, format); + + fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, + format->stream); + if (!fmt) + return -EINVAL; + + *fmt = format->format; + + return 0; +} + +static int _max96714_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + static const struct v4l2_mbus_framefmt format = { + .width = 1280, + .height = 1080, + .code = MEDIA_BUS_FMT_Y8_1X8, + .field = V4L2_FIELD_NONE, + }; + int ret; + + /* + * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until + * frame desc is made dynamically allocated. + */ + if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX) + return -EINVAL; + + ret = v4l2_subdev_routing_validate(sd, routing, + V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); + if (ret) + return ret; + + return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); +} + +static int max96714_set_routing(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which, + struct v4l2_subdev_krouting *routing) +{ + struct max96714_priv *priv = sd_to_max96714(sd); + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->enabled_source_streams) + return -EBUSY; + + return _max96714_set_routing(sd, state, which, routing); +} + +static int max96714_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_route routes[] = { + { + .sink_pad = MAX96714_PAD_SINK, + .sink_stream = 0, + .source_pad = MAX96714_PAD_SOURCE, + .source_stream = 0, + .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, + } + }; + struct v4l2_subdev_krouting routing = { + .num_routes = ARRAY_SIZE(routes), + .routes = routes, + }; + + return _max96714_set_routing(sd, state, V4L2_SUBDEV_FORMAT_ACTIVE, + &routing); +} + +static const struct v4l2_subdev_pad_ops max96714_pad_ops = { + .enable_streams = max96714_enable_streams, + .disable_streams = max96714_disable_streams, + + .set_routing = max96714_set_routing, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = max96714_set_fmt, +}; + +static bool max96714_link_locked(struct max96714_priv *priv) +{ + u64 val = 0; + + cci_read(priv->regmap, MAX96714_LINK_LOCK, &val, NULL); + + return val & MAX96714_LINK_LOCK_BIT; +} + +static void max96714_link_status(struct max96714_priv *priv) +{ + struct device *dev = &priv->client->dev; + + dev_info(dev, "Link locked:%d\n", max96714_link_locked(priv)); +} + +static bool max96714_pipe_locked(struct max96714_priv *priv) +{ + u64 val; + + cci_read(priv->regmap, MAX96714_VIDEO_RX8, &val, NULL); + + return val & MAX96714_VID_LOCK; +} + +static void max96714_pipe_status(struct max96714_priv *priv) +{ + struct device *dev = &priv->client->dev; + + dev_info(dev, "Pipe vidlock:%d\n", max96714_pipe_locked(priv)); +} + +static void max96714_csi_status(struct max96714_priv *priv) +{ + struct device *dev = &priv->client->dev; + u64 freq = 0; + + cci_read(priv->regmap, MAX96714_BACKTOP25, &freq, NULL); + freq = FIELD_GET(CSI_DPLL_FREQ_MASK, freq); + + dev_info(dev, "CSI controller DPLL freq:%u00MHz CSIPHY enabled:%d\n", + (u8)freq, max96714_tx_port_enabled(priv)); +} + +static int max96714_log_status(struct v4l2_subdev *sd) +{ + struct max96714_priv *priv = sd_to_max96714(sd); + struct device *dev = &priv->client->dev; + + dev_info(dev, "Deserializer: max96714\n"); + + max96714_link_status(priv); + max96714_pipe_status(priv); + max96714_csi_status(priv); + + return 0; +} + +static const struct v4l2_subdev_core_ops max96714_subdev_core_ops = { + .log_status = max96714_log_status, +}; + +static const struct v4l2_subdev_video_ops max96714_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_internal_ops max96714_internal_ops = { + .init_state = max96714_init_state, +}; + +static const struct v4l2_subdev_ops max96714_subdev_ops = { + .video = &max96714_video_ops, + .core = &max96714_subdev_core_ops, + .pad = &max96714_pad_ops, +}; + +static const struct media_entity_operations max96714_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int max96714_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asd) +{ + struct max96714_priv *priv = sd_to_max96714(notifier->sd); + struct device *dev = &priv->client->dev; + int ret; + + ret = media_entity_get_fwnode_pad(&subdev->entity, + priv->rxport.source.ep_fwnode, + MEDIA_PAD_FL_SOURCE); + if (ret < 0) { + dev_err(dev, "Failed to find pad for %s\n", subdev->name); + return ret; + } + + priv->rxport.source.sd = subdev; + priv->rxport.source.pad = ret; + + ret = media_create_pad_link(&priv->rxport.source.sd->entity, + priv->rxport.source.pad, &priv->sd.entity, + MAX96714_PAD_SINK, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) { + dev_err(dev, "Unable to link %s:%u -> %s:%u\n", + priv->rxport.source.sd->name, priv->rxport.source.pad, + priv->sd.name, MAX96714_PAD_SINK); + return ret; + } + + return 0; +} + +static const struct v4l2_async_notifier_operations max96714_notify_ops = { + .bound = max96714_notify_bound, +}; + +static int max96714_v4l2_notifier_register(struct max96714_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct max96714_rxport *rxport = &priv->rxport; + struct v4l2_async_connection *asd; + int ret; + + if (!rxport->source.ep_fwnode) + return 0; + + v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd); + + asd = v4l2_async_nf_add_fwnode(&priv->notifier, + rxport->source.ep_fwnode, + struct v4l2_async_connection); + if (IS_ERR(asd)) { + dev_err(dev, "Failed to add subdev: %pe", asd); + v4l2_async_nf_cleanup(&priv->notifier); + return PTR_ERR(asd); + } + + priv->notifier.ops = &max96714_notify_ops; + + ret = v4l2_async_nf_register(&priv->notifier); + if (ret) { + dev_err(dev, "Failed to register subdev_notifier"); + v4l2_async_nf_cleanup(&priv->notifier); + return ret; + } + + return 0; +} + +static int max96714_create_subdev(struct max96714_priv *priv) +{ + struct device *dev = &priv->client->dev; + int ret; + + v4l2_i2c_subdev_init(&priv->sd, priv->client, &max96714_subdev_ops); + priv->sd.internal_ops = &max96714_internal_ops; + + v4l2_ctrl_handler_init(&priv->ctrl_handler, 1); + priv->sd.ctrl_handler = &priv->ctrl_handler; + + v4l2_ctrl_new_int_menu(&priv->ctrl_handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, &priv->tx_link_freq); + v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler, + &max96714_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(max96714_test_pattern) - 1, + 0, 0, max96714_test_pattern); + if (priv->ctrl_handler.error) { + ret = priv->ctrl_handler.error; + goto err_free_ctrl; + } + + priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; + priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + priv->sd.entity.ops = &max96714_entity_ops; + + priv->pads[MAX96714_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + priv->pads[MAX96714_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&priv->sd.entity, + MAX96714_NPORTS, + priv->pads); + if (ret) + goto err_free_ctrl; + + priv->sd.state_lock = priv->sd.ctrl_handler->lock; + + ret = v4l2_subdev_init_finalize(&priv->sd); + if (ret) + goto err_entity_cleanup; + + ret = max96714_v4l2_notifier_register(priv); + if (ret) { + dev_err(dev, "v4l2 subdev notifier register failed: %d\n", ret); + goto err_subdev_cleanup; + } + + ret = v4l2_async_register_subdev(&priv->sd); + if (ret) { + dev_err(dev, "v4l2_async_register_subdev error: %d\n", ret); + goto err_unreg_notif; + } + + return 0; + +err_unreg_notif: + v4l2_async_nf_unregister(&priv->notifier); + v4l2_async_nf_cleanup(&priv->notifier); +err_subdev_cleanup: + v4l2_subdev_cleanup(&priv->sd); +err_entity_cleanup: + media_entity_cleanup(&priv->sd.entity); +err_free_ctrl: + v4l2_ctrl_handler_free(&priv->ctrl_handler); + + return ret; +}; + +static void max96714_destroy_subdev(struct max96714_priv *priv) +{ + v4l2_async_nf_unregister(&priv->notifier); + v4l2_async_nf_cleanup(&priv->notifier); + v4l2_async_unregister_subdev(&priv->sd); + + v4l2_subdev_cleanup(&priv->sd); + + media_entity_cleanup(&priv->sd.entity); + v4l2_ctrl_handler_free(&priv->ctrl_handler); +} + +static int max96714_i2c_mux_select(struct i2c_mux_core *mux, u32 chan) +{ + return 0; +} + +static int max96714_i2c_mux_init(struct max96714_priv *priv) +{ + priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev, + 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE, + max96714_i2c_mux_select, NULL); + if (!priv->mux) + return -ENOMEM; + + return i2c_mux_add_adapter(priv->mux, 0, 0); +} + +static int max96714_init_tx_port(struct max96714_priv *priv) +{ + struct v4l2_mbus_config_mipi_csi2 *mipi; + unsigned long lanes_used = 0; + unsigned int val, lane; + int ret; + + ret = max96714_disable_tx_port(priv); + + mipi = &priv->mipi_csi2; + val = div_u64(priv->tx_link_freq * 2, MHZ(100)); + + cci_update_bits(priv->regmap, MAX96714_BACKTOP25, + CSI_DPLL_FREQ_MASK, val, &ret); + + val = FIELD_PREP(MAX96714_CSI2_LANE_CNT_MASK, mipi->num_data_lanes - 1); + cci_update_bits(priv->regmap, MAX96714_MIPI_LANE_CNT, + MAX96714_CSI2_LANE_CNT_MASK, val, &ret); + + /* lanes polarity */ + val = 0; + for (lane = 0; lane < mipi->num_data_lanes + 1; lane++) { + if (!mipi->lane_polarities[lane]) + continue; + if (lane == 0) + /* clock lane */ + val |= BIT(5); + else if (lane < 3) + /* Lane D0 and D1 */ + val |= BIT(lane - 1); + else + /* D2 and D3 */ + val |= BIT(lane); + } + + cci_update_bits(priv->regmap, MAX96714_MIPI_POLARITY, + MAX96714_MIPI_POLARITY_MASK, val, &ret); + + /* lanes mapping */ + val = 0; + for (lane = 0; lane < mipi->num_data_lanes; lane++) { + val |= (mipi->data_lanes[lane] - 1) << (lane * 2); + lanes_used |= BIT(mipi->data_lanes[lane] - 1); + } + + /* + * Unused lanes need to be mapped as well to not have + * the same lanes mapped twice. + */ + for (; lane < 4; lane++) { + unsigned int idx = find_first_zero_bit(&lanes_used, 4); + + val |= idx << (lane * 2); + lanes_used |= BIT(idx); + } + + return cci_write(priv->regmap, MAX96714_MIPI_LANE_MAP, val, &ret); +} + +static int max96714_rxport_enable_poc(struct max96714_priv *priv) +{ + struct max96714_rxport *rxport = &priv->rxport; + + if (!rxport->poc) + return 0; + + return regulator_enable(rxport->poc); +} + +static int max96714_rxport_disable_poc(struct max96714_priv *priv) +{ + struct max96714_rxport *rxport = &priv->rxport; + + if (!rxport->poc) + return 0; + + return regulator_disable(rxport->poc); +} + +static int max96714_parse_dt_txport(struct max96714_priv *priv) +{ + struct device *dev = &priv->client->dev; + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct fwnode_handle *ep_fwnode; + u32 num_data_lanes; + int ret; + + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + MAX96714_PAD_SOURCE, 0, 0); + if (!ep_fwnode) + return -EINVAL; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep_fwnode, &vep); + fwnode_handle_put(ep_fwnode); + if (ret) { + dev_err(dev, "tx: failed to parse endpoint data\n"); + return -EINVAL; + } + + if (vep.nr_of_link_frequencies != 1) { + ret = -EINVAL; + goto err_free_vep; + } + + priv->tx_link_freq = vep.link_frequencies[0]; + /* Min 50MHz, Max 1250MHz, 50MHz step */ + if (priv->tx_link_freq < MHZ(50) || priv->tx_link_freq > MHZ(1250) || + (u32)priv->tx_link_freq % MHZ(50)) { + dev_err(dev, "tx: invalid link frequency\n"); + ret = -EINVAL; + goto err_free_vep; + } + + num_data_lanes = vep.bus.mipi_csi2.num_data_lanes; + if (num_data_lanes < 1 || num_data_lanes > 4) { + dev_err(dev, + "tx: invalid number of data lanes must be 1 to 4\n"); + ret = -EINVAL; + goto err_free_vep; + } + + memcpy(&priv->mipi_csi2, &vep.bus.mipi_csi2, sizeof(priv->mipi_csi2)); + +err_free_vep: + v4l2_fwnode_endpoint_free(&vep); + + return ret; +} + +static int max96714_parse_dt_rxport(struct max96714_priv *priv) +{ + static const char *poc_name = "port0-poc"; + struct max96714_rxport *rxport = &priv->rxport; + struct device *dev = &priv->client->dev; + struct fwnode_handle *ep_fwnode; + int ret; + + ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + MAX96714_PAD_SINK, 0, 0); + if (!ep_fwnode) + return -ENOENT; + + rxport->source.ep_fwnode = fwnode_graph_get_remote_endpoint(ep_fwnode); + fwnode_handle_put(ep_fwnode); + + if (!rxport->source.ep_fwnode) { + dev_err(dev, "rx: no remote endpoint\n"); + return -EINVAL; + } + + rxport->poc = devm_regulator_get_optional(dev, poc_name); + if (IS_ERR(rxport->poc)) { + ret = PTR_ERR(rxport->poc); + if (ret == -ENODEV) { + rxport->poc = NULL; + } else { + dev_err(dev, "rx: failed to get POC supply: %d\n", ret); + goto err_put_source_ep_fwnode; + } + } + + return 0; + +err_put_source_ep_fwnode: + fwnode_handle_put(rxport->source.ep_fwnode); + return ret; +} + +static int max96714_parse_dt(struct max96714_priv *priv) +{ + int ret; + + ret = max96714_parse_dt_txport(priv); + if (ret) + return ret; + + ret = max96714_parse_dt_rxport(priv); + /* + * The deserializer can create a test pattern even if the + * rx port is not connected to a serializer. + */ + if (ret && ret == -ENOENT) + ret = 0; + + return ret; +} + +static int max96714_enable_core_hw(struct max96714_priv *priv) +{ + struct device *dev = &priv->client->dev; + u64 val; + int ret; + + if (priv->pd_gpio) { + /* wait min 2 ms for reset to complete */ + gpiod_set_value_cansleep(priv->pd_gpio, 1); + fsleep(2000); + gpiod_set_value_cansleep(priv->pd_gpio, 0); + /* wait min 2 ms for power up to finish */ + fsleep(2000); + } + + ret = cci_read(priv->regmap, MAX96714_REG13, &val, NULL); + if (ret) { + dev_err_probe(dev, ret, "Cannot read first register, abort\n"); + goto err_pd_gpio; + } + + if (val != MAX96714_DEVICE_ID && val != MAX96714F_DEVICE_ID) { + dev_err(dev, "Unsupported device id expected %x got %x\n", + MAX96714F_DEVICE_ID, (u8)val); + ret = -EOPNOTSUPP; + goto err_pd_gpio; + } + + ret = cci_read(priv->regmap, MAX96714_DEV_REV, &val, NULL); + if (ret) + goto err_pd_gpio; + + dev_dbg(dev, "Found %x (rev %lx)\n", MAX96714F_DEVICE_ID, + (u8)val & MAX96714_DEV_REV_MASK); + + ret = cci_read(priv->regmap, MAX96714_MIPI_TX52, &val, NULL); + if (ret) + goto err_pd_gpio; + + if (!(val & MAX96714_TUN_EN)) { + dev_err(dev, "Only supporting tunnel mode"); + ret = -EOPNOTSUPP; + goto err_pd_gpio; + } + + return 0; + +err_pd_gpio: + gpiod_set_value_cansleep(priv->pd_gpio, 1); + return ret; +} + +static void max96714_disable_core_hw(struct max96714_priv *priv) +{ + gpiod_set_value_cansleep(priv->pd_gpio, 1); +} + +static int max96714_get_hw_resources(struct max96714_priv *priv) +{ + struct device *dev = &priv->client->dev; + + priv->regmap = devm_cci_regmap_init_i2c(priv->client, 16); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->pd_gpio = + devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); + if (IS_ERR(priv->pd_gpio)) + return dev_err_probe(dev, PTR_ERR(priv->pd_gpio), + "Cannot get powerdown GPIO\n"); + return 0; +} + +static int max96714_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct max96714_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->client = client; + + ret = max96714_get_hw_resources(priv); + if (ret) + return ret; + + ret = max96714_enable_core_hw(priv); + if (ret) + return ret; + + ret = max96714_parse_dt(priv); + if (ret) + goto err_disable_core_hw; + + max96714_init_tx_port(priv); + + ret = max96714_rxport_enable_poc(priv); + if (ret) + goto err_free_ports; + + ret = max96714_i2c_mux_init(priv); + if (ret) + goto err_disable_poc; + + ret = max96714_create_subdev(priv); + if (ret) + goto err_del_mux; + + return 0; + +err_del_mux: + i2c_mux_del_adapters(priv->mux); +err_disable_poc: + max96714_rxport_disable_poc(priv); +err_free_ports: + fwnode_handle_put(priv->rxport.source.ep_fwnode); +err_disable_core_hw: + max96714_disable_core_hw(priv); + + return ret; +} + +static void max96714_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct max96714_priv *priv = sd_to_max96714(sd); + + max96714_destroy_subdev(priv); + i2c_mux_del_adapters(priv->mux); + max96714_rxport_disable_poc(priv); + fwnode_handle_put(priv->rxport.source.ep_fwnode); + max96714_disable_core_hw(priv); + gpiod_set_value_cansleep(priv->pd_gpio, 1); +} + +static const struct of_device_id max96714_of_ids[] = { + { .compatible = "maxim,max96714f" }, + { } +}; +MODULE_DEVICE_TABLE(of, max96714_of_ids); + +static struct i2c_driver max96714_i2c_driver = { + .driver = { + .name = "max96714", + .of_match_table = max96714_of_ids, + }, + .probe = max96714_probe, + .remove = max96714_remove, +}; + +module_i2c_driver(max96714_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Maxim Integrated GMSL2 Deserializers Driver"); +MODULE_AUTHOR("Julien Massot "); -- cgit v1.2.3 From 64a9e1fb1d6ff67d52d546732e11aea1e43b01e8 Mon Sep 17 00:00:00 2001 From: Julien Massot Date: Tue, 30 Apr 2024 15:19:31 +0200 Subject: drivers: media: max96717: stop the csi receiver before the source Stopping the CSI source before stopping the serializer CSI port may make the serializer not respond. Then all the next writes to the device will fail. max96717 1-0040: Error writing reg 0x0308: -121 max96717 1-0040: Error writing reg 0x0006: -121 Fix that by stopping the CSI receiver first and then CSI source. Seen on max96717f revision 4. Signed-off-by: Julien Massot Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/max96717.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/media/i2c/max96717.c b/drivers/media/i2c/max96717.c index 62df822a193f..949306485873 100644 --- a/drivers/media/i2c/max96717.c +++ b/drivers/media/i2c/max96717.c @@ -384,24 +384,23 @@ static int max96717_disable_streams(struct v4l2_subdev *sd, { struct max96717_priv *priv = sd_to_max96717(sd); u64 sink_streams; - int ret; + + /* + * Stop the CSI receiver first then the source, + * otherwise the device may become unresponsive + * while holding the I2C bus low. + */ + priv->enabled_source_streams &= ~streams_mask; + if (!priv->enabled_source_streams) + max96717_start_csi(priv, false); sink_streams = v4l2_subdev_state_xlate_streams(state, MAX96717_PAD_SOURCE, MAX96717_PAD_SINK, &streams_mask); - ret = v4l2_subdev_disable_streams(priv->source_sd, priv->source_sd_pad, - sink_streams); - if (ret) - return ret; - - priv->enabled_source_streams &= ~streams_mask; - - if (!priv->enabled_source_streams) - max96717_start_csi(priv, false); - - return 0; + return v4l2_subdev_disable_streams(priv->source_sd, priv->source_sd_pad, + sink_streams); } static const struct v4l2_subdev_pad_ops max96717_pad_ops = { -- cgit v1.2.3 From cd4a34e02b9064c0e2b55ac9b6ee47ae422d5c73 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 1 Jun 2024 19:39:37 +0200 Subject: media: ov5693: Drop privacy-LED GPIO control Drop privacy-LED GPIO control, after the privacy-LED GPIO control was added to the ov5693 driver it was decided to model privacy-LEDs as LED class devices and have them be controlled by the v4l2-core. So this is dead code since on devices with privacy LEDs the led is not a GPIO on the ov5693 fwnode, but rather is a LED class devices controlled by the v4l2-core. Signed-off-by: Hans de Goede Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ov5693.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c index 8deb28b55983..46b9ce111676 100644 --- a/drivers/media/i2c/ov5693.c +++ b/drivers/media/i2c/ov5693.c @@ -141,7 +141,6 @@ struct ov5693_device { struct gpio_desc *reset; struct gpio_desc *powerdown; - struct gpio_desc *privacy_led; struct regulator_bulk_data supplies[OV5693_NUM_SUPPLIES]; struct clk *xvclk; @@ -657,7 +656,6 @@ static int ov5693_sensor_init(struct ov5693_device *ov5693) static void ov5693_sensor_powerdown(struct ov5693_device *ov5693) { - gpiod_set_value_cansleep(ov5693->privacy_led, 0); gpiod_set_value_cansleep(ov5693->reset, 1); gpiod_set_value_cansleep(ov5693->powerdown, 1); @@ -687,7 +685,6 @@ static int ov5693_sensor_powerup(struct ov5693_device *ov5693) gpiod_set_value_cansleep(ov5693->powerdown, 0); gpiod_set_value_cansleep(ov5693->reset, 0); - gpiod_set_value_cansleep(ov5693->privacy_led, 1); usleep_range(5000, 7500); @@ -1201,13 +1198,6 @@ static int ov5693_configure_gpios(struct ov5693_device *ov5693) return PTR_ERR(ov5693->powerdown); } - ov5693->privacy_led = devm_gpiod_get_optional(ov5693->dev, "privacy-led", - GPIOD_OUT_LOW); - if (IS_ERR(ov5693->privacy_led)) { - dev_err(ov5693->dev, "Error fetching privacy-led GPIO\n"); - return PTR_ERR(ov5693->privacy_led); - } - return 0; } -- cgit v1.2.3 From aad113c02bcf166d9d10ba3fcd6520a606607410 Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:24:40 +0200 Subject: media: av7110: remove old documentation files The documentation has been update and is now at: linux/Documentation/userspace-api/media/dvb/legacy_dvb_osd.rst linux/Documentation/userspace-api/media/dvb/legacy_dvb_audio.rst linux/Documentation/userspace-api/media/dvb/legacy_dvb_video.rst The .rst files of the old documentation aren't necessary any more, remove them. Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/TODO | 3 - .../av7110/audio-bilingual-channel-select.rst | 58 ----- .../staging/media/av7110/audio-channel-select.rst | 57 ----- .../staging/media/av7110/audio-clear-buffer.rst | 48 ---- drivers/staging/media/av7110/audio-continue.rst | 48 ---- drivers/staging/media/av7110/audio-fclose.rst | 51 ----- drivers/staging/media/av7110/audio-fopen.rst | 103 --------- drivers/staging/media/av7110/audio-fwrite.rst | 79 ------- .../media/av7110/audio-get-capabilities.rst | 54 ----- drivers/staging/media/av7110/audio-get-status.rst | 54 ----- drivers/staging/media/av7110/audio-pause.rst | 49 ---- drivers/staging/media/av7110/audio-play.rst | 48 ---- .../staging/media/av7110/audio-select-source.rst | 56 ----- drivers/staging/media/av7110/audio-set-av-sync.rst | 58 ----- .../staging/media/av7110/audio-set-bypass-mode.rst | 62 ------ drivers/staging/media/av7110/audio-set-id.rst | 59 ----- drivers/staging/media/av7110/audio-set-mixer.rst | 53 ----- drivers/staging/media/av7110/audio-set-mute.rst | 62 ------ .../staging/media/av7110/audio-set-streamtype.rst | 66 ------ drivers/staging/media/av7110/audio-stop.rst | 48 ---- drivers/staging/media/av7110/audio.rst | 27 --- drivers/staging/media/av7110/audio_data_types.rst | 116 ---------- .../staging/media/av7110/audio_function_calls.rst | 30 --- .../staging/media/av7110/video-clear-buffer.rst | 54 ----- drivers/staging/media/av7110/video-command.rst | 96 -------- drivers/staging/media/av7110/video-continue.rst | 57 ----- .../staging/media/av7110/video-fast-forward.rst | 72 ------ drivers/staging/media/av7110/video-fclose.rst | 51 ----- drivers/staging/media/av7110/video-fopen.rst | 111 --------- drivers/staging/media/av7110/video-freeze.rst | 61 ----- drivers/staging/media/av7110/video-fwrite.rst | 79 ------- .../media/av7110/video-get-capabilities.rst | 61 ----- drivers/staging/media/av7110/video-get-event.rst | 105 --------- .../staging/media/av7110/video-get-frame-count.rst | 65 ------ drivers/staging/media/av7110/video-get-pts.rst | 69 ------ drivers/staging/media/av7110/video-get-size.rst | 69 ------ drivers/staging/media/av7110/video-get-status.rst | 72 ------ drivers/staging/media/av7110/video-play.rst | 57 ----- .../staging/media/av7110/video-select-source.rst | 76 ------- drivers/staging/media/av7110/video-set-blank.rst | 64 ------ .../media/av7110/video-set-display-format.rst | 60 ----- drivers/staging/media/av7110/video-set-format.rst | 82 ------- .../staging/media/av7110/video-set-streamtype.rst | 61 ----- drivers/staging/media/av7110/video-slowmotion.rst | 72 ------ .../staging/media/av7110/video-stillpicture.rst | 61 ----- drivers/staging/media/av7110/video-stop.rst | 74 ------ drivers/staging/media/av7110/video-try-command.rst | 66 ------ drivers/staging/media/av7110/video.rst | 36 --- .../staging/media/av7110/video_function_calls.rst | 35 --- drivers/staging/media/av7110/video_types.rst | 248 --------------------- 50 files changed, 3303 deletions(-) delete mode 100644 drivers/staging/media/av7110/TODO delete mode 100644 drivers/staging/media/av7110/audio-bilingual-channel-select.rst delete mode 100644 drivers/staging/media/av7110/audio-channel-select.rst delete mode 100644 drivers/staging/media/av7110/audio-clear-buffer.rst delete mode 100644 drivers/staging/media/av7110/audio-continue.rst delete mode 100644 drivers/staging/media/av7110/audio-fclose.rst delete mode 100644 drivers/staging/media/av7110/audio-fopen.rst delete mode 100644 drivers/staging/media/av7110/audio-fwrite.rst delete mode 100644 drivers/staging/media/av7110/audio-get-capabilities.rst delete mode 100644 drivers/staging/media/av7110/audio-get-status.rst delete mode 100644 drivers/staging/media/av7110/audio-pause.rst delete mode 100644 drivers/staging/media/av7110/audio-play.rst delete mode 100644 drivers/staging/media/av7110/audio-select-source.rst delete mode 100644 drivers/staging/media/av7110/audio-set-av-sync.rst delete mode 100644 drivers/staging/media/av7110/audio-set-bypass-mode.rst delete mode 100644 drivers/staging/media/av7110/audio-set-id.rst delete mode 100644 drivers/staging/media/av7110/audio-set-mixer.rst delete mode 100644 drivers/staging/media/av7110/audio-set-mute.rst delete mode 100644 drivers/staging/media/av7110/audio-set-streamtype.rst delete mode 100644 drivers/staging/media/av7110/audio-stop.rst delete mode 100644 drivers/staging/media/av7110/audio.rst delete mode 100644 drivers/staging/media/av7110/audio_data_types.rst delete mode 100644 drivers/staging/media/av7110/audio_function_calls.rst delete mode 100644 drivers/staging/media/av7110/video-clear-buffer.rst delete mode 100644 drivers/staging/media/av7110/video-command.rst delete mode 100644 drivers/staging/media/av7110/video-continue.rst delete mode 100644 drivers/staging/media/av7110/video-fast-forward.rst delete mode 100644 drivers/staging/media/av7110/video-fclose.rst delete mode 100644 drivers/staging/media/av7110/video-fopen.rst delete mode 100644 drivers/staging/media/av7110/video-freeze.rst delete mode 100644 drivers/staging/media/av7110/video-fwrite.rst delete mode 100644 drivers/staging/media/av7110/video-get-capabilities.rst delete mode 100644 drivers/staging/media/av7110/video-get-event.rst delete mode 100644 drivers/staging/media/av7110/video-get-frame-count.rst delete mode 100644 drivers/staging/media/av7110/video-get-pts.rst delete mode 100644 drivers/staging/media/av7110/video-get-size.rst delete mode 100644 drivers/staging/media/av7110/video-get-status.rst delete mode 100644 drivers/staging/media/av7110/video-play.rst delete mode 100644 drivers/staging/media/av7110/video-select-source.rst delete mode 100644 drivers/staging/media/av7110/video-set-blank.rst delete mode 100644 drivers/staging/media/av7110/video-set-display-format.rst delete mode 100644 drivers/staging/media/av7110/video-set-format.rst delete mode 100644 drivers/staging/media/av7110/video-set-streamtype.rst delete mode 100644 drivers/staging/media/av7110/video-slowmotion.rst delete mode 100644 drivers/staging/media/av7110/video-stillpicture.rst delete mode 100644 drivers/staging/media/av7110/video-stop.rst delete mode 100644 drivers/staging/media/av7110/video-try-command.rst delete mode 100644 drivers/staging/media/av7110/video.rst delete mode 100644 drivers/staging/media/av7110/video_function_calls.rst delete mode 100644 drivers/staging/media/av7110/video_types.rst diff --git a/drivers/staging/media/av7110/TODO b/drivers/staging/media/av7110/TODO deleted file mode 100644 index 60062d8441b3..000000000000 --- a/drivers/staging/media/av7110/TODO +++ /dev/null @@ -1,3 +0,0 @@ -- This driver is too old and relies on a different API. - Drop it from Kernel on a couple of versions. -- Cleanup patches for the drivers here won't be accepted. diff --git a/drivers/staging/media/av7110/audio-bilingual-channel-select.rst b/drivers/staging/media/av7110/audio-bilingual-channel-select.rst deleted file mode 100644 index 33b5363317f1..000000000000 --- a/drivers/staging/media/av7110/audio-bilingual-channel-select.rst +++ /dev/null @@ -1,58 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_BILINGUAL_CHANNEL_SELECT: - -============================== -AUDIO_BILINGUAL_CHANNEL_SELECT -============================== - -Name ----- - -AUDIO_BILINGUAL_CHANNEL_SELECT - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_BILINGUAL_CHANNEL_SELECT - -``int ioctl(int fd, AUDIO_BILINGUAL_CHANNEL_SELECT, struct audio_channel_select *select)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - audio_channel_select_t ch - - - Select the output format of the audio (mono left/right, stereo). - -Description ------------ - -This ioctl is obsolete. Do not use in new drivers. It has been replaced -by the V4L2 ``V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK`` control -for MPEG decoders controlled through V4L2. - -This ioctl call asks the Audio Device to select the requested channel -for bilingual streams if possible. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-channel-select.rst b/drivers/staging/media/av7110/audio-channel-select.rst deleted file mode 100644 index 74093df92a68..000000000000 --- a/drivers/staging/media/av7110/audio-channel-select.rst +++ /dev/null @@ -1,57 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_CHANNEL_SELECT: - -==================== -AUDIO_CHANNEL_SELECT -==================== - -Name ----- - -AUDIO_CHANNEL_SELECT - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_CHANNEL_SELECT - -``int ioctl(int fd, AUDIO_CHANNEL_SELECT, struct audio_channel_select *select)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - audio_channel_select_t ch - - - Select the output format of the audio (mono left/right, stereo). - -Description ------------ - -This ioctl is for Digital TV devices only. To control a V4L2 decoder use the -V4L2 ``V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK`` control instead. - -This ioctl call asks the Audio Device to select the requested channel if -possible. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-clear-buffer.rst b/drivers/staging/media/av7110/audio-clear-buffer.rst deleted file mode 100644 index a0ebb0278260..000000000000 --- a/drivers/staging/media/av7110/audio-clear-buffer.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_CLEAR_BUFFER: - -================== -AUDIO_CLEAR_BUFFER -================== - -Name ----- - -AUDIO_CLEAR_BUFFER - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_CLEAR_BUFFER - -``int ioctl(int fd, AUDIO_CLEAR_BUFFER)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - -Description ------------ - -This ioctl call asks the Audio Device to clear all software and hardware -buffers of the audio decoder device. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-continue.rst b/drivers/staging/media/av7110/audio-continue.rst deleted file mode 100644 index a2e9850f37f2..000000000000 --- a/drivers/staging/media/av7110/audio-continue.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_CONTINUE: - -============== -AUDIO_CONTINUE -============== - -Name ----- - -AUDIO_CONTINUE - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_CONTINUE - -``int ioctl(int fd, AUDIO_CONTINUE)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - -Description ------------ - -This ioctl restarts the decoding and playing process previously paused -with AUDIO_PAUSE command. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-fclose.rst b/drivers/staging/media/av7110/audio-fclose.rst deleted file mode 100644 index 77857d578e83..000000000000 --- a/drivers/staging/media/av7110/audio-fclose.rst +++ /dev/null @@ -1,51 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _audio_fclose: - -======================== -Digital TV audio close() -======================== - -Name ----- - -Digital TV audio close() - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:function:: int close(int fd) - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - -Description ------------ - -This system call closes a previously opened audio device. - -Return Value ------------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``EBADF`` - - - fd is not a valid open file descriptor. diff --git a/drivers/staging/media/av7110/audio-fopen.rst b/drivers/staging/media/av7110/audio-fopen.rst deleted file mode 100644 index 774daaab3bad..000000000000 --- a/drivers/staging/media/av7110/audio-fopen.rst +++ /dev/null @@ -1,103 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _audio_fopen: - -======================= -Digital TV audio open() -======================= - -Name ----- - -Digital TV audio open() - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:function:: int open(const char *deviceName, int flags) - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - const char \*deviceName - - - Name of specific audio device. - - - .. row 2 - - - int flags - - - A bit-wise OR of the following flags: - - - .. row 3 - - - - - O_RDONLY read-only access - - - .. row 4 - - - - - O_RDWR read/write access - - - .. row 5 - - - - - O_NONBLOCK open in non-blocking mode - - - .. row 6 - - - - - (blocking mode is the default) - -Description ------------ - -This system call opens a named audio device (e.g. -/dev/dvb/adapter0/audio0) for subsequent use. When an open() call has -succeeded, the device will be ready for use. The significance of -blocking or non-blocking mode is described in the documentation for -functions where there is a difference. It does not affect the semantics -of the open() call itself. A device opened in blocking mode can later be -put into non-blocking mode (and vice versa) using the F_SETFL command -of the fcntl system call. This is a standard system call, documented in -the Linux manual page for fcntl. Only one user can open the Audio Device -in O_RDWR mode. All other attempts to open the device in this mode will -fail, and an error code will be returned. If the Audio Device is opened -in O_RDONLY mode, the only ioctl call that can be used is -AUDIO_GET_STATUS. All other call will return with an error code. - -Return Value ------------- - -.. tabularcolumns:: |p{2.5cm}|p{15.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``ENODEV`` - - - Device driver not loaded/available. - - - .. row 2 - - - ``EBUSY`` - - - Device or resource busy. - - - .. row 3 - - - ``EINVAL`` - - - Invalid argument. diff --git a/drivers/staging/media/av7110/audio-fwrite.rst b/drivers/staging/media/av7110/audio-fwrite.rst deleted file mode 100644 index 7b096ac2b6c4..000000000000 --- a/drivers/staging/media/av7110/audio-fwrite.rst +++ /dev/null @@ -1,79 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _audio_fwrite: - -========================= -Digital TV audio write() -========================= - -Name ----- - -Digital TV audio write() - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:function:: size_t write(int fd, const void *buf, size_t count) - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - void \*buf - - - Pointer to the buffer containing the PES data. - - - .. row 3 - - - size_t count - - - Size of buf. - -Description ------------ - -This system call can only be used if AUDIO_SOURCE_MEMORY is selected -in the ioctl call AUDIO_SELECT_SOURCE. The data provided shall be in -PES format. If O_NONBLOCK is not specified the function will block -until buffer space is available. The amount of data to be transferred is -implied by count. - -Return Value ------------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``EPERM`` - - - Mode AUDIO_SOURCE_MEMORY not selected. - - - .. row 2 - - - ``ENOMEM`` - - - Attempted to write more data than the internal buffer can hold. - - - .. row 3 - - - ``EBADF`` - - - fd is not a valid open file descriptor. diff --git a/drivers/staging/media/av7110/audio-get-capabilities.rst b/drivers/staging/media/av7110/audio-get-capabilities.rst deleted file mode 100644 index 6d9eb71dad17..000000000000 --- a/drivers/staging/media/av7110/audio-get-capabilities.rst +++ /dev/null @@ -1,54 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_GET_CAPABILITIES: - -====================== -AUDIO_GET_CAPABILITIES -====================== - -Name ----- - -AUDIO_GET_CAPABILITIES - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_GET_CAPABILITIES - -``int ioctl(int fd, AUDIO_GET_CAPABILITIES, unsigned int *cap)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - unsigned int \*cap - - - Returns a bit array of supported sound formats. - -Description ------------ - -This ioctl call asks the Audio Device to tell us about the decoding -capabilities of the audio hardware. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-get-status.rst b/drivers/staging/media/av7110/audio-get-status.rst deleted file mode 100644 index 7ae8db2e65e9..000000000000 --- a/drivers/staging/media/av7110/audio-get-status.rst +++ /dev/null @@ -1,54 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_GET_STATUS: - -================ -AUDIO_GET_STATUS -================ - -Name ----- - -AUDIO_GET_STATUS - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_GET_STATUS - -``int ioctl(int fd, AUDIO_GET_STATUS, struct audio_status *status)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - struct audio_status \*status - - - Returns the current state of Audio Device. - -Description ------------ - -This ioctl call asks the Audio Device to return the current state of the -Audio Device. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-pause.rst b/drivers/staging/media/av7110/audio-pause.rst deleted file mode 100644 index d37d1ddce4df..000000000000 --- a/drivers/staging/media/av7110/audio-pause.rst +++ /dev/null @@ -1,49 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_PAUSE: - -=========== -AUDIO_PAUSE -=========== - -Name ----- - -AUDIO_PAUSE - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_PAUSE - -``int ioctl(int fd, AUDIO_PAUSE)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - -Description ------------ - -This ioctl call suspends the audio stream being played. Decoding and -playing are paused. It is then possible to restart again decoding and -playing process of the audio stream using AUDIO_CONTINUE command. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-play.rst b/drivers/staging/media/av7110/audio-play.rst deleted file mode 100644 index e591930b6ca7..000000000000 --- a/drivers/staging/media/av7110/audio-play.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_PLAY: - -========== -AUDIO_PLAY -========== - -Name ----- - -AUDIO_PLAY - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_PLAY - -``int ioctl(int fd, AUDIO_PLAY)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - -Description ------------ - -This ioctl call asks the Audio Device to start playing an audio stream -from the selected source. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-select-source.rst b/drivers/staging/media/av7110/audio-select-source.rst deleted file mode 100644 index 6a0c0f365eb1..000000000000 --- a/drivers/staging/media/av7110/audio-select-source.rst +++ /dev/null @@ -1,56 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_SELECT_SOURCE: - -=================== -AUDIO_SELECT_SOURCE -=================== - -Name ----- - -AUDIO_SELECT_SOURCE - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_SELECT_SOURCE - -``int ioctl(int fd, AUDIO_SELECT_SOURCE, struct audio_stream_source *source)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - audio_stream_source_t source - - - Indicates the source that shall be used for the Audio stream. - -Description ------------ - -This ioctl call informs the audio device which source shall be used for -the input data. The possible sources are demux or memory. If -AUDIO_SOURCE_MEMORY is selected, the data is fed to the Audio Device -through the write command. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-set-av-sync.rst b/drivers/staging/media/av7110/audio-set-av-sync.rst deleted file mode 100644 index 85a8016bf025..000000000000 --- a/drivers/staging/media/av7110/audio-set-av-sync.rst +++ /dev/null @@ -1,58 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_SET_AV_SYNC: - -================= -AUDIO_SET_AV_SYNC -================= - -Name ----- - -AUDIO_SET_AV_SYNC - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_SET_AV_SYNC - -``int ioctl(int fd, AUDIO_SET_AV_SYNC, boolean state)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - boolean state - - - Tells the Digital TV subsystem if A/V synchronization shall be ON or OFF. - - TRUE: AV-sync ON - - FALSE: AV-sync OFF - -Description ------------ - -This ioctl call asks the Audio Device to turn ON or OFF A/V -synchronization. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-set-bypass-mode.rst b/drivers/staging/media/av7110/audio-set-bypass-mode.rst deleted file mode 100644 index 80d551a2053a..000000000000 --- a/drivers/staging/media/av7110/audio-set-bypass-mode.rst +++ /dev/null @@ -1,62 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_SET_BYPASS_MODE: - -===================== -AUDIO_SET_BYPASS_MODE -===================== - -Name ----- - -AUDIO_SET_BYPASS_MODE - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_SET_BYPASS_MODE - -``int ioctl(int fd, AUDIO_SET_BYPASS_MODE, boolean mode)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - boolean mode - - - Enables or disables the decoding of the current Audio stream in - the Digital TV subsystem. - - TRUE: Bypass is disabled - - FALSE: Bypass is enabled - -Description ------------ - -This ioctl call asks the Audio Device to bypass the Audio decoder and -forward the stream without decoding. This mode shall be used if streams -that can't be handled by the Digital TV system shall be decoded. Dolby -DigitalTM streams are automatically forwarded by the Digital TV subsystem if -the hardware can handle it. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-set-id.rst b/drivers/staging/media/av7110/audio-set-id.rst deleted file mode 100644 index 39ad846d412d..000000000000 --- a/drivers/staging/media/av7110/audio-set-id.rst +++ /dev/null @@ -1,59 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_SET_ID: - -============ -AUDIO_SET_ID -============ - -Name ----- - -AUDIO_SET_ID - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_SET_ID - -``int ioctl(int fd, AUDIO_SET_ID, int id)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - int id - - - audio sub-stream id - -Description ------------ - -This ioctl selects which sub-stream is to be decoded if a program or -system stream is sent to the video device. If no audio stream type is -set the id has to be in [0xC0,0xDF] for MPEG sound, in [0x80,0x87] for -AC3 and in [0xA0,0xA7] for LPCM. More specifications may follow for -other stream types. If the stream type is set the id just specifies the -substream id of the audio stream and only the first 5 bits are -recognized. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-set-mixer.rst b/drivers/staging/media/av7110/audio-set-mixer.rst deleted file mode 100644 index 45dbdf4801e0..000000000000 --- a/drivers/staging/media/av7110/audio-set-mixer.rst +++ /dev/null @@ -1,53 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_SET_MIXER: - -=============== -AUDIO_SET_MIXER -=============== - -Name ----- - -AUDIO_SET_MIXER - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_SET_MIXER - -``int ioctl(int fd, AUDIO_SET_MIXER, struct audio_mixer *mix)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - audio_mixer_t \*mix - - - mixer settings. - -Description ------------ - -This ioctl lets you adjust the mixer settings of the audio decoder. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-set-mute.rst b/drivers/staging/media/av7110/audio-set-mute.rst deleted file mode 100644 index 987751f92967..000000000000 --- a/drivers/staging/media/av7110/audio-set-mute.rst +++ /dev/null @@ -1,62 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_SET_MUTE: - -============== -AUDIO_SET_MUTE -============== - -Name ----- - -AUDIO_SET_MUTE - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_SET_MUTE - -``int ioctl(int fd, AUDIO_SET_MUTE, boolean state)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - boolean state - - - Indicates if audio device shall mute or not. - - TRUE: Audio Mute - - FALSE: Audio Un-mute - -Description ------------ - -This ioctl is for Digital TV devices only. To control a V4L2 decoder use the -V4L2 :ref:`VIDIOC_DECODER_CMD` with the -``V4L2_DEC_CMD_START_MUTE_AUDIO`` flag instead. - -This ioctl call asks the audio device to mute the stream that is -currently being played. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio-set-streamtype.rst b/drivers/staging/media/av7110/audio-set-streamtype.rst deleted file mode 100644 index 77d73c74882f..000000000000 --- a/drivers/staging/media/av7110/audio-set-streamtype.rst +++ /dev/null @@ -1,66 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_SET_STREAMTYPE: - -==================== -AUDIO_SET_STREAMTYPE -==================== - -Name ----- - -AUDIO_SET_STREAMTYPE - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_SET_STREAMTYPE - -``int ioctl(fd, AUDIO_SET_STREAMTYPE, int type)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - - - - int fd - - - File descriptor returned by a previous call to open(). - - - - - - int type - - - stream type - -Description ------------ - -This ioctl tells the driver which kind of audio stream to expect. This -is useful if the stream offers several audio sub-streams like LPCM and -AC3. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``EINVAL`` - - - type is not a valid or supported stream type. diff --git a/drivers/staging/media/av7110/audio-stop.rst b/drivers/staging/media/av7110/audio-stop.rst deleted file mode 100644 index d77f786fd797..000000000000 --- a/drivers/staging/media/av7110/audio-stop.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.audio - -.. _AUDIO_STOP: - -========== -AUDIO_STOP -========== - -Name ----- - -AUDIO_STOP - -.. attention:: This ioctl is deprecated - -Synopsis --------- - -.. c:macro:: AUDIO_STOP - -``int ioctl(int fd, AUDIO_STOP)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - -Description ------------ - -This ioctl call asks the Audio Device to stop playing the current -stream. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/audio.rst b/drivers/staging/media/av7110/audio.rst deleted file mode 100644 index aa753336b31f..000000000000 --- a/drivers/staging/media/av7110/audio.rst +++ /dev/null @@ -1,27 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _dvb_audio: - -####################### -Digital TV Audio Device -####################### - -The Digital TV audio device controls the MPEG2 audio decoder of the Digital -TV hardware. It can be accessed through ``/dev/dvb/adapter?/audio?``. Data -types and ioctl definitions can be accessed by including -``linux/dvb/audio.h`` in your application. - -Please note that some Digital TV cards don't have their own MPEG decoder, which -results in the omission of the audio and video device. - -These ioctls were also used by V4L2 to control MPEG decoders implemented -in V4L2. The use of these ioctls for that purpose has been made obsolete -and proper V4L2 ioctls or controls have been created to replace that -functionality. - - -.. toctree:: - :maxdepth: 1 - - audio_data_types - audio_function_calls diff --git a/drivers/staging/media/av7110/audio_data_types.rst b/drivers/staging/media/av7110/audio_data_types.rst deleted file mode 100644 index 4744529136a8..000000000000 --- a/drivers/staging/media/av7110/audio_data_types.rst +++ /dev/null @@ -1,116 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _audio_data_types: - -**************** -Audio Data Types -**************** - -This section describes the structures, data types and defines used when -talking to the audio device. - -.. c:type:: audio_stream_source - -The audio stream source is set through the AUDIO_SELECT_SOURCE call -and can take the following values, depending on whether we are replaying -from an internal (demux) or external (user write) source. - - -.. code-block:: c - - typedef enum { - AUDIO_SOURCE_DEMUX, - AUDIO_SOURCE_MEMORY - } audio_stream_source_t; - -AUDIO_SOURCE_DEMUX selects the demultiplexer (fed either by the -frontend or the DVR device) as the source of the video stream. If -AUDIO_SOURCE_MEMORY is selected the stream comes from the application -through the ``write()`` system call. - - -.. c:type:: audio_play_state - -The following values can be returned by the AUDIO_GET_STATUS call -representing the state of audio playback. - - -.. code-block:: c - - typedef enum { - AUDIO_STOPPED, - AUDIO_PLAYING, - AUDIO_PAUSED - } audio_play_state_t; - - -.. c:type:: audio_channel_select - -The audio channel selected via AUDIO_CHANNEL_SELECT is determined by -the following values. - - -.. code-block:: c - - typedef enum { - AUDIO_STEREO, - AUDIO_MONO_LEFT, - AUDIO_MONO_RIGHT, - AUDIO_MONO, - AUDIO_STEREO_SWAPPED - } audio_channel_select_t; - - -.. c:type:: audio_status - -The AUDIO_GET_STATUS call returns the following structure informing -about various states of the playback operation. - - -.. code-block:: c - - typedef struct audio_status { - boolean AV_sync_state; - boolean mute_state; - audio_play_state_t play_state; - audio_stream_source_t stream_source; - audio_channel_select_t channel_select; - boolean bypass_mode; - audio_mixer_t mixer_state; - } audio_status_t; - - -.. c:type:: audio_mixer - -The following structure is used by the AUDIO_SET_MIXER call to set the -audio volume. - - -.. code-block:: c - - typedef struct audio_mixer { - unsigned int volume_left; - unsigned int volume_right; - } audio_mixer_t; - - -.. _audio_encodings: - -audio encodings -=============== - -A call to AUDIO_GET_CAPABILITIES returns an unsigned integer with the -following bits set according to the hardwares capabilities. - - -.. code-block:: c - - #define AUDIO_CAP_DTS 1 - #define AUDIO_CAP_LPCM 2 - #define AUDIO_CAP_MP1 4 - #define AUDIO_CAP_MP2 8 - #define AUDIO_CAP_MP3 16 - #define AUDIO_CAP_AAC 32 - #define AUDIO_CAP_OGG 64 - #define AUDIO_CAP_SDDS 128 - #define AUDIO_CAP_AC3 256 diff --git a/drivers/staging/media/av7110/audio_function_calls.rst b/drivers/staging/media/av7110/audio_function_calls.rst deleted file mode 100644 index fa5ba9539caf..000000000000 --- a/drivers/staging/media/av7110/audio_function_calls.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _audio_function_calls: - -******************** -Audio Function Calls -******************** - -.. toctree:: - :maxdepth: 1 - - audio-fopen - audio-fclose - audio-fwrite - audio-stop - audio-play - audio-pause - audio-continue - audio-select-source - audio-set-mute - audio-set-av-sync - audio-set-bypass-mode - audio-channel-select - audio-bilingual-channel-select - audio-get-status - audio-get-capabilities - audio-clear-buffer - audio-set-id - audio-set-mixer - audio-set-streamtype diff --git a/drivers/staging/media/av7110/video-clear-buffer.rst b/drivers/staging/media/av7110/video-clear-buffer.rst deleted file mode 100644 index a7730559bbb2..000000000000 --- a/drivers/staging/media/av7110/video-clear-buffer.rst +++ /dev/null @@ -1,54 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_CLEAR_BUFFER: - -================== -VIDEO_CLEAR_BUFFER -================== - -Name ----- - -VIDEO_CLEAR_BUFFER - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_CLEAR_BUFFER - -``int ioctl(fd, VIDEO_CLEAR_BUFFER)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_CLEAR_BUFFER for this command. - -Description ------------ - -This ioctl call clears all video buffers in the driver and in the -decoder hardware. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-command.rst b/drivers/staging/media/av7110/video-command.rst deleted file mode 100644 index cae9445eb3af..000000000000 --- a/drivers/staging/media/av7110/video-command.rst +++ /dev/null @@ -1,96 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_COMMAND: - -============= -VIDEO_COMMAND -============= - -Name ----- - -VIDEO_COMMAND - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_COMMAND - -``int ioctl(int fd, VIDEO_COMMAND, struct video_command *cmd)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_COMMAND for this command. - - - .. row 3 - - - struct video_command \*cmd - - - Commands the decoder. - -Description ------------ - -This ioctl is obsolete. Do not use in new drivers. For V4L2 decoders -this ioctl has been replaced by the -:ref:`VIDIOC_DECODER_CMD` ioctl. - -This ioctl commands the decoder. The ``video_command`` struct is a -subset of the ``v4l2_decoder_cmd`` struct, so refer to the -:ref:`VIDIOC_DECODER_CMD` documentation for -more information. - -.. c:type:: video_command - -.. code-block:: c - - /* The structure must be zeroed before use by the application - This ensures it can be extended safely in the future. */ - struct video_command { - __u32 cmd; - __u32 flags; - union { - struct { - __u64 pts; - } stop; - - struct { - /* 0 or 1000 specifies normal speed, - 1 specifies forward single stepping, - -1 specifies backward single stepping, - >1: playback at speed/1000 of the normal speed, - <-1: reverse playback at (-speed/1000) of the normal speed. */ - __s32 speed; - __u32 format; - } play; - - struct { - __u32 data[16]; - } raw; - }; - }; - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-continue.rst b/drivers/staging/media/av7110/video-continue.rst deleted file mode 100644 index bc34bf3989e4..000000000000 --- a/drivers/staging/media/av7110/video-continue.rst +++ /dev/null @@ -1,57 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_CONTINUE: - -============== -VIDEO_CONTINUE -============== - -Name ----- - -VIDEO_CONTINUE - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_CONTINUE - -``int ioctl(fd, VIDEO_CONTINUE)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_CONTINUE for this command. - -Description ------------ - -This ioctl is for Digital TV devices only. To control a V4L2 decoder use the -V4L2 :ref:`VIDIOC_DECODER_CMD` instead. - -This ioctl call restarts decoding and playing processes of the video -stream which was played before a call to VIDEO_FREEZE was made. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-fast-forward.rst b/drivers/staging/media/av7110/video-fast-forward.rst deleted file mode 100644 index e71fa8d6965b..000000000000 --- a/drivers/staging/media/av7110/video-fast-forward.rst +++ /dev/null @@ -1,72 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_FAST_FORWARD: - -================== -VIDEO_FAST_FORWARD -================== - -Name ----- - -VIDEO_FAST_FORWARD - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_FAST_FORWARD - -``int ioctl(fd, VIDEO_FAST_FORWARD, int nFrames)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_FAST_FORWARD for this command. - - - .. row 3 - - - int nFrames - - - The number of frames to skip. - -Description ------------ - -This ioctl call asks the Video Device to skip decoding of N number of -I-frames. This call can only be used if VIDEO_SOURCE_MEMORY is -selected. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``EPERM`` - - - Mode VIDEO_SOURCE_MEMORY not selected. diff --git a/drivers/staging/media/av7110/video-fclose.rst b/drivers/staging/media/av7110/video-fclose.rst deleted file mode 100644 index 01d24d548439..000000000000 --- a/drivers/staging/media/av7110/video-fclose.rst +++ /dev/null @@ -1,51 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _video_fclose: - -================= -dvb video close() -================= - -Name ----- - -dvb video close() - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:function:: int close(int fd) - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - -Description ------------ - -This system call closes a previously opened video device. - -Return Value ------------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``EBADF`` - - - fd is not a valid open file descriptor. diff --git a/drivers/staging/media/av7110/video-fopen.rst b/drivers/staging/media/av7110/video-fopen.rst deleted file mode 100644 index 1371b083e4e8..000000000000 --- a/drivers/staging/media/av7110/video-fopen.rst +++ /dev/null @@ -1,111 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _video_fopen: - -================ -dvb video open() -================ - -Name ----- - -dvb video open() - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:function:: int open(const char *deviceName, int flags) - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - const char \*deviceName - - - Name of specific video device. - - - .. row 2 - - - int flags - - - A bit-wise OR of the following flags: - - - .. row 3 - - - - - O_RDONLY read-only access - - - .. row 4 - - - - - O_RDWR read/write access - - - .. row 5 - - - - - O_NONBLOCK open in non-blocking mode - - - .. row 6 - - - - - (blocking mode is the default) - -Description ------------ - -This system call opens a named video device (e.g. -/dev/dvb/adapter0/video0) for subsequent use. - -When an open() call has succeeded, the device will be ready for use. The -significance of blocking or non-blocking mode is described in the -documentation for functions where there is a difference. It does not -affect the semantics of the open() call itself. A device opened in -blocking mode can later be put into non-blocking mode (and vice versa) -using the F_SETFL command of the fcntl system call. This is a standard -system call, documented in the Linux manual page for fcntl. Only one -user can open the Video Device in O_RDWR mode. All other attempts to -open the device in this mode will fail, and an error-code will be -returned. If the Video Device is opened in O_RDONLY mode, the only -ioctl call that can be used is VIDEO_GET_STATUS. All other call will -return an error code. - -Return Value ------------- - -.. tabularcolumns:: |p{2.5cm}|p{15.0cm}| - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``ENODEV`` - - - Device driver not loaded/available. - - - .. row 2 - - - ``EINTERNAL`` - - - Internal error. - - - .. row 3 - - - ``EBUSY`` - - - Device or resource busy. - - - .. row 4 - - - ``EINVAL`` - - - Invalid argument. diff --git a/drivers/staging/media/av7110/video-freeze.rst b/drivers/staging/media/av7110/video-freeze.rst deleted file mode 100644 index 4321f257cb70..000000000000 --- a/drivers/staging/media/av7110/video-freeze.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_FREEZE: - -============ -VIDEO_FREEZE -============ - -Name ----- - -VIDEO_FREEZE - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_FREEZE - -``int ioctl(fd, VIDEO_FREEZE)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_FREEZE for this command. - -Description ------------ - -This ioctl is for Digital TV devices only. To control a V4L2 decoder use the -V4L2 :ref:`VIDIOC_DECODER_CMD` instead. - -This ioctl call suspends the live video stream being played. Decoding -and playing are frozen. It is then possible to restart the decoding and -playing process of the video stream using the VIDEO_CONTINUE command. -If VIDEO_SOURCE_MEMORY is selected in the ioctl call -VIDEO_SELECT_SOURCE, the Digital TV subsystem will not decode any more data -until the ioctl call VIDEO_CONTINUE or VIDEO_PLAY is performed. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-fwrite.rst b/drivers/staging/media/av7110/video-fwrite.rst deleted file mode 100644 index a07fd7d7a40e..000000000000 --- a/drivers/staging/media/av7110/video-fwrite.rst +++ /dev/null @@ -1,79 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _video_fwrite: - -================= -dvb video write() -================= - -Name ----- - -dvb video write() - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:function:: size_t write(int fd, const void *buf, size_t count) - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - void \*buf - - - Pointer to the buffer containing the PES data. - - - .. row 3 - - - size_t count - - - Size of buf. - -Description ------------ - -This system call can only be used if VIDEO_SOURCE_MEMORY is selected -in the ioctl call VIDEO_SELECT_SOURCE. The data provided shall be in -PES format, unless the capability allows other formats. If O_NONBLOCK -is not specified the function will block until buffer space is -available. The amount of data to be transferred is implied by count. - -Return Value ------------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``EPERM`` - - - Mode VIDEO_SOURCE_MEMORY not selected. - - - .. row 2 - - - ``ENOMEM`` - - - Attempted to write more data than the internal buffer can hold. - - - .. row 3 - - - ``EBADF`` - - - fd is not a valid open file descriptor. diff --git a/drivers/staging/media/av7110/video-get-capabilities.rst b/drivers/staging/media/av7110/video-get-capabilities.rst deleted file mode 100644 index 01e09f56656c..000000000000 --- a/drivers/staging/media/av7110/video-get-capabilities.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_GET_CAPABILITIES: - -====================== -VIDEO_GET_CAPABILITIES -====================== - -Name ----- - -VIDEO_GET_CAPABILITIES - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_GET_CAPABILITIES - -``int ioctl(fd, VIDEO_GET_CAPABILITIES, unsigned int *cap)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_GET_CAPABILITIES for this command. - - - .. row 3 - - - unsigned int \*cap - - - Pointer to a location where to store the capability information. - -Description ------------ - -This ioctl call asks the video device about its decoding capabilities. -On success it returns and integer which has bits set according to the -defines in section ??. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-get-event.rst b/drivers/staging/media/av7110/video-get-event.rst deleted file mode 100644 index 90382bc36cfe..000000000000 --- a/drivers/staging/media/av7110/video-get-event.rst +++ /dev/null @@ -1,105 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_GET_EVENT: - -=============== -VIDEO_GET_EVENT -=============== - -Name ----- - -VIDEO_GET_EVENT - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_GET_EVENT - -``int ioctl(fd, VIDEO_GET_EVENT, struct video_event *ev)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_GET_EVENT for this command. - - - .. row 3 - - - struct video_event \*ev - - - Points to the location where the event, if any, is to be stored. - -Description ------------ - -This ioctl is for Digital TV devices only. To get events from a V4L2 decoder -use the V4L2 :ref:`VIDIOC_DQEVENT` ioctl instead. - -This ioctl call returns an event of type video_event if available. If -an event is not available, the behavior depends on whether the device is -in blocking or non-blocking mode. In the latter case, the call fails -immediately with errno set to ``EWOULDBLOCK``. In the former case, the call -blocks until an event becomes available. The standard Linux poll() -and/or select() system calls can be used with the device file descriptor -to watch for new events. For select(), the file descriptor should be -included in the exceptfds argument, and for poll(), POLLPRI should be -specified as the wake-up condition. Read-only permissions are sufficient -for this ioctl call. - -.. c:type:: video_event - -.. code-block:: c - - struct video_event { - __s32 type; - #define VIDEO_EVENT_SIZE_CHANGED 1 - #define VIDEO_EVENT_FRAME_RATE_CHANGED 2 - #define VIDEO_EVENT_DECODER_STOPPED 3 - #define VIDEO_EVENT_VSYNC 4 - long timestamp; - union { - video_size_t size; - unsigned int frame_rate; /* in frames per 1000sec */ - unsigned char vsync_field; /* unknown/odd/even/progressive */ - } u; - }; - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``EWOULDBLOCK`` - - - There is no event pending, and the device is in non-blocking mode. - - - .. row 2 - - - ``EOVERFLOW`` - - - Overflow in event queue - one or more events were lost. diff --git a/drivers/staging/media/av7110/video-get-frame-count.rst b/drivers/staging/media/av7110/video-get-frame-count.rst deleted file mode 100644 index b48ac8c58a41..000000000000 --- a/drivers/staging/media/av7110/video-get-frame-count.rst +++ /dev/null @@ -1,65 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_GET_FRAME_COUNT: - -===================== -VIDEO_GET_FRAME_COUNT -===================== - -Name ----- - -VIDEO_GET_FRAME_COUNT - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_GET_FRAME_COUNT - -``int ioctl(int fd, VIDEO_GET_FRAME_COUNT, __u64 *pts)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_GET_FRAME_COUNT for this command. - - - .. row 3 - - - __u64 \*pts - - - Returns the number of frames displayed since the decoder was - started. - -Description ------------ - -This ioctl is obsolete. Do not use in new drivers. For V4L2 decoders -this ioctl has been replaced by the ``V4L2_CID_MPEG_VIDEO_DEC_FRAME`` -control. - -This ioctl call asks the Video Device to return the number of displayed -frames since the decoder was started. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-get-pts.rst b/drivers/staging/media/av7110/video-get-pts.rst deleted file mode 100644 index fedaff41be0b..000000000000 --- a/drivers/staging/media/av7110/video-get-pts.rst +++ /dev/null @@ -1,69 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_GET_PTS: - -============= -VIDEO_GET_PTS -============= - -Name ----- - -VIDEO_GET_PTS - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_GET_PTS - -``int ioctl(int fd, VIDEO_GET_PTS, __u64 *pts)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_GET_PTS for this command. - - - .. row 3 - - - __u64 \*pts - - - Returns the 33-bit timestamp as defined in ITU T-REC-H.222.0 / - ISO/IEC 13818-1. - - The PTS should belong to the currently played frame if possible, - but may also be a value close to it like the PTS of the last - decoded frame or the last PTS extracted by the PES parser. - -Description ------------ - -This ioctl is obsolete. Do not use in new drivers. For V4L2 decoders -this ioctl has been replaced by the ``V4L2_CID_MPEG_VIDEO_DEC_PTS`` -control. - -This ioctl call asks the Video Device to return the current PTS -timestamp. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-get-size.rst b/drivers/staging/media/av7110/video-get-size.rst deleted file mode 100644 index de34331c5bd1..000000000000 --- a/drivers/staging/media/av7110/video-get-size.rst +++ /dev/null @@ -1,69 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_GET_SIZE: - -============== -VIDEO_GET_SIZE -============== - -Name ----- - -VIDEO_GET_SIZE - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_GET_SIZE - -``int ioctl(int fd, VIDEO_GET_SIZE, video_size_t *size)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_GET_SIZE for this command. - - - .. row 3 - - - video_size_t \*size - - - Returns the size and aspect ratio. - -Description ------------ - -This ioctl returns the size and aspect ratio. - -.. c:type:: video_size_t - -.. code-block::c - - typedef struct { - int w; - int h; - video_format_t aspect_ratio; - } video_size_t; - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-get-status.rst b/drivers/staging/media/av7110/video-get-status.rst deleted file mode 100644 index 9b86fbf411d4..000000000000 --- a/drivers/staging/media/av7110/video-get-status.rst +++ /dev/null @@ -1,72 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_GET_STATUS: - -================ -VIDEO_GET_STATUS -================ - -Name ----- - -VIDEO_GET_STATUS - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_GET_STATUS - -``int ioctl(fd, VIDEO_GET_STATUS, struct video_status *status)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_GET_STATUS for this command. - - - .. row 3 - - - struct video_status \*status - - - Returns the current status of the Video Device. - -Description ------------ - -This ioctl call asks the Video Device to return the current status of -the device. - -.. c:type:: video_status - -.. code-block:: c - - struct video_status { - int video_blank; /* blank video on freeze? */ - video_play_state_t play_state; /* current state of playback */ - video_stream_source_t stream_source; /* current source (demux/memory) */ - video_format_t video_format; /* current aspect ratio of stream*/ - video_displayformat_t display_format;/* selected cropping mode */ - }; - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-play.rst b/drivers/staging/media/av7110/video-play.rst deleted file mode 100644 index 35ac8b98fdbf..000000000000 --- a/drivers/staging/media/av7110/video-play.rst +++ /dev/null @@ -1,57 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_PLAY: - -========== -VIDEO_PLAY -========== - -Name ----- - -VIDEO_PLAY - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_PLAY - -``int ioctl(fd, VIDEO_PLAY)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_PLAY for this command. - -Description ------------ - -This ioctl is for Digital TV devices only. To control a V4L2 decoder use the -V4L2 :ref:`VIDIOC_DECODER_CMD` instead. - -This ioctl call asks the Video Device to start playing a video stream -from the selected source. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-select-source.rst b/drivers/staging/media/av7110/video-select-source.rst deleted file mode 100644 index 929a20985d53..000000000000 --- a/drivers/staging/media/av7110/video-select-source.rst +++ /dev/null @@ -1,76 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_SELECT_SOURCE: - -=================== -VIDEO_SELECT_SOURCE -=================== - -Name ----- - -VIDEO_SELECT_SOURCE - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_SELECT_SOURCE - -``int ioctl(fd, VIDEO_SELECT_SOURCE, video_stream_source_t source)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_SELECT_SOURCE for this command. - - - .. row 3 - - - video_stream_source_t source - - - Indicates which source shall be used for the Video stream. - -Description ------------ - -This ioctl is for Digital TV devices only. This ioctl was also supported by the -V4L2 ivtv driver, but that has been replaced by the ivtv-specific -``IVTV_IOC_PASSTHROUGH_MODE`` ioctl. - -This ioctl call informs the video device which source shall be used for -the input data. The possible sources are demux or memory. If memory is -selected, the data is fed to the video device through the write command. - -.. c:type:: video_stream_source_t - -.. code-block:: c - - typedef enum { - VIDEO_SOURCE_DEMUX, /* Select the demux as the main source */ - VIDEO_SOURCE_MEMORY /* If this source is selected, the stream - comes from the user through the write - system call */ - } video_stream_source_t; - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-set-blank.rst b/drivers/staging/media/av7110/video-set-blank.rst deleted file mode 100644 index 70249a6ba125..000000000000 --- a/drivers/staging/media/av7110/video-set-blank.rst +++ /dev/null @@ -1,64 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_SET_BLANK: - -=============== -VIDEO_SET_BLANK -=============== - -Name ----- - -VIDEO_SET_BLANK - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_SET_BLANK - -``int ioctl(fd, VIDEO_SET_BLANK, boolean mode)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_SET_BLANK for this command. - - - .. row 3 - - - boolean mode - - - TRUE: Blank screen when stop. - - - .. row 4 - - - - - FALSE: Show last decoded frame. - -Description ------------ - -This ioctl call asks the Video Device to blank out the picture. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-set-display-format.rst b/drivers/staging/media/av7110/video-set-display-format.rst deleted file mode 100644 index 1de4f40ae732..000000000000 --- a/drivers/staging/media/av7110/video-set-display-format.rst +++ /dev/null @@ -1,60 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_SET_DISPLAY_FORMAT: - -======================== -VIDEO_SET_DISPLAY_FORMAT -======================== - -Name ----- - -VIDEO_SET_DISPLAY_FORMAT - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_SET_DISPLAY_FORMAT - -``int ioctl(fd, VIDEO_SET_DISPLAY_FORMAT)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_SET_DISPLAY_FORMAT for this command. - - - .. row 3 - - - video_display_format_t format - - - Selects the video format to be used. - -Description ------------ - -This ioctl call asks the Video Device to select the video format to be -applied by the MPEG chip on the video. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-set-format.rst b/drivers/staging/media/av7110/video-set-format.rst deleted file mode 100644 index bb64e37ae081..000000000000 --- a/drivers/staging/media/av7110/video-set-format.rst +++ /dev/null @@ -1,82 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_SET_FORMAT: - -================ -VIDEO_SET_FORMAT -================ - -Name ----- - -VIDEO_SET_FORMAT - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_SET_FORMAT - -``int ioctl(fd, VIDEO_SET_FORMAT, video_format_t format)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_SET_FORMAT for this command. - - - .. row 3 - - - video_format_t format - - - video format of TV as defined in section ??. - -Description ------------ - -This ioctl sets the screen format (aspect ratio) of the connected output -device (TV) so that the output of the decoder can be adjusted -accordingly. - -.. c:type:: video_format_t - -.. code-block:: c - - typedef enum { - VIDEO_FORMAT_4_3, /* Select 4:3 format */ - VIDEO_FORMAT_16_9, /* Select 16:9 format. */ - VIDEO_FORMAT_221_1 /* 2.21:1 */ - } video_format_t; - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``EINVAL`` - - - format is not a valid video format. diff --git a/drivers/staging/media/av7110/video-set-streamtype.rst b/drivers/staging/media/av7110/video-set-streamtype.rst deleted file mode 100644 index 1f31c048bdbc..000000000000 --- a/drivers/staging/media/av7110/video-set-streamtype.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_SET_STREAMTYPE: - -==================== -VIDEO_SET_STREAMTYPE -==================== - -Name ----- - -VIDEO_SET_STREAMTYPE - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_SET_STREAMTYPE - -``int ioctl(fd, VIDEO_SET_STREAMTYPE, int type)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_SET_STREAMTYPE for this command. - - - .. row 3 - - - int type - - - stream type - -Description ------------ - -This ioctl tells the driver which kind of stream to expect being written -to it. If this call is not used the default of video PES is used. Some -drivers might not support this call and always expect PES. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-slowmotion.rst b/drivers/staging/media/av7110/video-slowmotion.rst deleted file mode 100644 index 1478fcc30cb8..000000000000 --- a/drivers/staging/media/av7110/video-slowmotion.rst +++ /dev/null @@ -1,72 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_SLOWMOTION: - -================ -VIDEO_SLOWMOTION -================ - -Name ----- - -VIDEO_SLOWMOTION - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_SLOWMOTION - -``int ioctl(fd, VIDEO_SLOWMOTION, int nFrames)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_SLOWMOTION for this command. - - - .. row 3 - - - int nFrames - - - The number of times to repeat each frame. - -Description ------------ - -This ioctl call asks the video device to repeat decoding frames N number -of times. This call can only be used if VIDEO_SOURCE_MEMORY is -selected. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. - - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - ``EPERM`` - - - Mode VIDEO_SOURCE_MEMORY not selected. diff --git a/drivers/staging/media/av7110/video-stillpicture.rst b/drivers/staging/media/av7110/video-stillpicture.rst deleted file mode 100644 index d25384222a20..000000000000 --- a/drivers/staging/media/av7110/video-stillpicture.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_STILLPICTURE: - -================== -VIDEO_STILLPICTURE -================== - -Name ----- - -VIDEO_STILLPICTURE - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_STILLPICTURE - -``int ioctl(fd, VIDEO_STILLPICTURE, struct video_still_picture *sp)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_STILLPICTURE for this command. - - - .. row 3 - - - struct video_still_picture \*sp - - - Pointer to a location where an I-frame and size is stored. - -Description ------------ - -This ioctl call asks the Video Device to display a still picture -(I-frame). The input data shall contain an I-frame. If the pointer is -NULL, then the current displayed still picture is blanked. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-stop.rst b/drivers/staging/media/av7110/video-stop.rst deleted file mode 100644 index 96f61c5b48a2..000000000000 --- a/drivers/staging/media/av7110/video-stop.rst +++ /dev/null @@ -1,74 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_STOP: - -========== -VIDEO_STOP -========== - -Name ----- - -VIDEO_STOP - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_STOP - -``int ioctl(fd, VIDEO_STOP, boolean mode)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_STOP for this command. - - - .. row 3 - - - Boolean mode - - - Indicates how the screen shall be handled. - - - .. row 4 - - - - - TRUE: Blank screen when stop. - - - .. row 5 - - - - - FALSE: Show last decoded frame. - -Description ------------ - -This ioctl is for Digital TV devices only. To control a V4L2 decoder use the -V4L2 :ref:`VIDIOC_DECODER_CMD` instead. - -This ioctl call asks the Video Device to stop playing the current -stream. Depending on the input parameter, the screen can be blanked out -or displaying the last decoded frame. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video-try-command.rst b/drivers/staging/media/av7110/video-try-command.rst deleted file mode 100644 index 79bf3dfb8a32..000000000000 --- a/drivers/staging/media/av7110/video-try-command.rst +++ /dev/null @@ -1,66 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later -.. c:namespace:: DTV.video - -.. _VIDEO_TRY_COMMAND: - -================= -VIDEO_TRY_COMMAND -================= - -Name ----- - -VIDEO_TRY_COMMAND - -.. attention:: This ioctl is deprecated. - -Synopsis --------- - -.. c:macro:: VIDEO_TRY_COMMAND - -``int ioctl(int fd, VIDEO_TRY_COMMAND, struct video_command *cmd)`` - -Arguments ---------- - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - - - .. row 1 - - - int fd - - - File descriptor returned by a previous call to open(). - - - .. row 2 - - - int request - - - Equals VIDEO_TRY_COMMAND for this command. - - - .. row 3 - - - struct video_command \*cmd - - - Try a decoder command. - -Description ------------ - -This ioctl is obsolete. Do not use in new drivers. For V4L2 decoders -this ioctl has been replaced by the -:ref:`VIDIOC_TRY_DECODER_CMD ` ioctl. - -This ioctl tries a decoder command. The ``video_command`` struct is a -subset of the ``v4l2_decoder_cmd`` struct, so refer to the -:ref:`VIDIOC_TRY_DECODER_CMD ` documentation -for more information. - -Return Value ------------- - -On success 0 is returned, on error -1 and the ``errno`` variable is set -appropriately. The generic error codes are described at the -:ref:`Generic Error Codes ` chapter. diff --git a/drivers/staging/media/av7110/video.rst b/drivers/staging/media/av7110/video.rst deleted file mode 100644 index 808705b769a1..000000000000 --- a/drivers/staging/media/av7110/video.rst +++ /dev/null @@ -1,36 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _dvb_video: - -####################### -Digital TV Video Device -####################### - -The Digital TV video device controls the MPEG2 video decoder of the Digital -TV hardware. It can be accessed through **/dev/dvb/adapter0/video0**. Data -types and ioctl definitions can be accessed by including -**linux/dvb/video.h** in your application. - -Note that the Digital TV video device only controls decoding of the MPEG video -stream, not its presentation on the TV or computer screen. On PCs this -is typically handled by an associated video4linux device, e.g. -**/dev/video**, which allows scaling and defining output windows. - -Some Digital TV cards don't have their own MPEG decoder, which results in the -omission of the audio and video device as well as the video4linux -device. - -The ioctls that deal with SPUs (sub picture units) and navigation -packets are only supported on some MPEG decoders made for DVD playback. - -These ioctls were also used by V4L2 to control MPEG decoders implemented -in V4L2. The use of these ioctls for that purpose has been made obsolete -and proper V4L2 ioctls or controls have been created to replace that -functionality. - - -.. toctree:: - :maxdepth: 1 - - video_types - video_function_calls diff --git a/drivers/staging/media/av7110/video_function_calls.rst b/drivers/staging/media/av7110/video_function_calls.rst deleted file mode 100644 index 20a897be5dca..000000000000 --- a/drivers/staging/media/av7110/video_function_calls.rst +++ /dev/null @@ -1,35 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _video_function_calls: - -******************** -Video Function Calls -******************** - -.. toctree:: - :maxdepth: 1 - - video-fopen - video-fclose - video-fwrite - video-stop - video-play - video-freeze - video-continue - video-select-source - video-set-blank - video-get-status - video-get-frame-count - video-get-pts - video-get-event - video-command - video-try-command - video-get-size - video-set-display-format - video-stillpicture - video-fast-forward - video-slowmotion - video-get-capabilities - video-clear-buffer - video-set-streamtype - video-set-format diff --git a/drivers/staging/media/av7110/video_types.rst b/drivers/staging/media/av7110/video_types.rst deleted file mode 100644 index c4557d328b7a..000000000000 --- a/drivers/staging/media/av7110/video_types.rst +++ /dev/null @@ -1,248 +0,0 @@ -.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later - -.. _video_types: - -**************** -Video Data Types -**************** - - -.. _video-format-t: - -video_format_t -============== - -The ``video_format_t`` data type defined by - - -.. code-block:: c - - typedef enum { - VIDEO_FORMAT_4_3, /* Select 4:3 format */ - VIDEO_FORMAT_16_9, /* Select 16:9 format. */ - VIDEO_FORMAT_221_1 /* 2.21:1 */ - } video_format_t; - -is used in the VIDEO_SET_FORMAT function (??) to tell the driver which -aspect ratio the output hardware (e.g. TV) has. It is also used in the -data structures video_status (??) returned by VIDEO_GET_STATUS (??) -and video_event (??) returned by VIDEO_GET_EVENT (??) which report -about the display format of the current video stream. - - -.. _video-displayformat-t: - -video_displayformat_t -===================== - -In case the display format of the video stream and of the display -hardware differ the application has to specify how to handle the -cropping of the picture. This can be done using the -VIDEO_SET_DISPLAY_FORMAT call (??) which accepts - - -.. code-block:: c - - typedef enum { - VIDEO_PAN_SCAN, /* use pan and scan format */ - VIDEO_LETTER_BOX, /* use letterbox format */ - VIDEO_CENTER_CUT_OUT /* use center cut out format */ - } video_displayformat_t; - -as argument. - - -.. _video-stream-source-t: - -video_stream_source_t -===================== - -The video stream source is set through the VIDEO_SELECT_SOURCE call -and can take the following values, depending on whether we are replaying -from an internal (demuxer) or external (user write) source. - - -.. code-block:: c - - typedef enum { - VIDEO_SOURCE_DEMUX, /* Select the demux as the main source */ - VIDEO_SOURCE_MEMORY /* If this source is selected, the stream - comes from the user through the write - system call */ - } video_stream_source_t; - -VIDEO_SOURCE_DEMUX selects the demultiplexer (fed either by the -frontend or the DVR device) as the source of the video stream. If -VIDEO_SOURCE_MEMORY is selected the stream comes from the application -through the **write()** system call. - - -.. _video-play-state-t: - -video_play_state_t -================== - -The following values can be returned by the VIDEO_GET_STATUS call -representing the state of video playback. - - -.. code-block:: c - - typedef enum { - VIDEO_STOPPED, /* Video is stopped */ - VIDEO_PLAYING, /* Video is currently playing */ - VIDEO_FREEZED /* Video is freezed */ - } video_play_state_t; - - -.. c:type:: video_command - -struct video_command -==================== - -The structure must be zeroed before use by the application This ensures -it can be extended safely in the future. - - -.. code-block:: c - - struct video_command { - __u32 cmd; - __u32 flags; - union { - struct { - __u64 pts; - } stop; - - struct { - /* 0 or 1000 specifies normal speed, - 1 specifies forward single stepping, - -1 specifies backward single stepping, - >>1: playback at speed/1000 of the normal speed, - <-1: reverse playback at (-speed/1000) of the normal speed. */ - __s32 speed; - __u32 format; - } play; - - struct { - __u32 data[16]; - } raw; - }; - }; - - -.. _video-size-t: - -video_size_t -============ - - -.. code-block:: c - - typedef struct { - int w; - int h; - video_format_t aspect_ratio; - } video_size_t; - - -.. c:type:: video_event - -struct video_event -================== - -The following is the structure of a video event as it is returned by the -VIDEO_GET_EVENT call. - - -.. code-block:: c - - struct video_event { - __s32 type; - #define VIDEO_EVENT_SIZE_CHANGED 1 - #define VIDEO_EVENT_FRAME_RATE_CHANGED 2 - #define VIDEO_EVENT_DECODER_STOPPED 3 - #define VIDEO_EVENT_VSYNC 4 - long timestamp; - union { - video_size_t size; - unsigned int frame_rate; /* in frames per 1000sec */ - unsigned char vsync_field; /* unknown/odd/even/progressive */ - } u; - }; - - -.. c:type:: video_status - -struct video_status -=================== - -The VIDEO_GET_STATUS call returns the following structure informing -about various states of the playback operation. - - -.. code-block:: c - - struct video_status { - int video_blank; /* blank video on freeze? */ - video_play_state_t play_state; /* current state of playback */ - video_stream_source_t stream_source; /* current source (demux/memory) */ - video_format_t video_format; /* current aspect ratio of stream */ - video_displayformat_t display_format;/* selected cropping mode */ - }; - -If video_blank is set video will be blanked out if the channel is -changed or if playback is stopped. Otherwise, the last picture will be -displayed. play_state indicates if the video is currently frozen, -stopped, or being played back. The stream_source corresponds to the -selected source for the video stream. It can come either from the -demultiplexer or from memory. The video_format indicates the aspect -ratio (one of 4:3 or 16:9) of the currently played video stream. -Finally, display_format corresponds to the selected cropping mode in -case the source video format is not the same as the format of the output -device. - - -.. c:type:: video_still_picture - -struct video_still_picture -========================== - -An I-frame displayed via the VIDEO_STILLPICTURE call is passed on -within the following structure. - - -.. code-block:: c - - /* pointer to and size of a single iframe in memory */ - struct video_still_picture { - char *iFrame; /* pointer to a single iframe in memory */ - int32_t size; - }; - - -.. _video_caps: - -video capabilities -================== - -A call to VIDEO_GET_CAPABILITIES returns an unsigned integer with the -following bits set according to the hardwares capabilities. - - -.. code-block:: c - - /* bit definitions for capabilities: */ - /* can the hardware decode MPEG1 and/or MPEG2? */ - #define VIDEO_CAP_MPEG1 1 - #define VIDEO_CAP_MPEG2 2 - /* can you send a system and/or program stream to video device? - (you still have to open the video and the audio device but only - send the stream to the video device) */ - #define VIDEO_CAP_SYS 4 - #define VIDEO_CAP_PROG 8 - /* can the driver also handle SPU, NAVI and CSS encoded data? - (CSS API is not present yet) */ - #define VIDEO_CAP_SPU 16 - #define VIDEO_CAP_NAVI 32 - #define VIDEO_CAP_CSS 64 -- cgit v1.2.3 From 95ccc5b604415cbdef6bd228340088ac4ceb8055 Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:24:41 +0200 Subject: media: av7110: remove budget-patch driver This patch removes the budget-patch stand alone driver only. This patch does *not* remove the budget-patch support from the av7110 (= dvb-ttpci) driver. This patch does also *not* remove the support for the full-ts mod. The budget-patch is an obsolete, experimental hardware modification, which aimed to receive the whole transport stream of a transponder by bypassing the av7110. This modification never worked reliably enough to be used in a productive environment. "almost no packet loss" [budget-patch.c #522] is still a packet loss. The successor of the budget-patch is the full-ts mod, which is reliable. The full-ts mod circuit is found on the never rev. 2.2 and 2.3 DVB-S cards ex factory. The full-ts mod support is (only) in the av7110 (= dvb-ttpci) driver. The budget-patch driver is a stand alone driver, which turns the card into a budget card. It doesn't appear to be ever intended for the average user. "Card is required to have loaded firmware to tune properly. Firmware can be loaded by insertion and removal of standard av7110 driver prior to loading this driver." [Kconfig] There is support for the budget-patch in the av7110 (= dvb-ttpci) driver too, which isn't affected by this patch. The budget-patch is obsolete and replaced by the full-ts mod quiet a while now. Remove the budget-patch driver left over from development. Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/Kconfig | 22 - drivers/staging/media/av7110/Makefile | 2 - drivers/staging/media/av7110/budget-patch.c | 665 ---------------------------- 3 files changed, 689 deletions(-) delete mode 100644 drivers/staging/media/av7110/budget-patch.c diff --git a/drivers/staging/media/av7110/Kconfig b/drivers/staging/media/av7110/Kconfig index 9faf9d2d4001..0722df9e6a41 100644 --- a/drivers/staging/media/av7110/Kconfig +++ b/drivers/staging/media/av7110/Kconfig @@ -51,28 +51,6 @@ config DVB_AV7110_OSD All other people say N. -config DVB_BUDGET_PATCH - tristate "AV7110 cards with Budget Patch" - depends on DVB_BUDGET_CORE && I2C - depends on DVB_AV7110 - select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT - select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT - select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT - help - Support for Budget Patch (full TS) modification on - SAA7146+AV7110 based cards (DVB-S cards). This - driver doesn't use onboard MPEG2 decoder. The - card is driven in Budget-only mode. Card is - required to have loaded firmware to tune properly. - Firmware can be loaded by insertion and removal of - standard AV7110 driver prior to loading this - driver. - - Say Y if you own such a card and want to use it. - - To compile this driver as a module, choose M here: the - module will be called budget-patch. - if DVB_AV7110 # Frontend driver that it is used only by AV7110 driver diff --git a/drivers/staging/media/av7110/Makefile b/drivers/staging/media/av7110/Makefile index 307b267598ea..f4bbb535d988 100644 --- a/drivers/staging/media/av7110/Makefile +++ b/drivers/staging/media/av7110/Makefile @@ -10,8 +10,6 @@ ifdef CONFIG_DVB_AV7110_IR dvb-ttpci-objs += av7110_ir.o endif -obj-$(CONFIG_DVB_BUDGET_PATCH) += budget-patch.o - obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o obj-$(CONFIG_DVB_SP8870) += sp8870.o diff --git a/drivers/staging/media/av7110/budget-patch.c b/drivers/staging/media/av7110/budget-patch.c deleted file mode 100644 index d173c8ade6a7..000000000000 --- a/drivers/staging/media/av7110/budget-patch.c +++ /dev/null @@ -1,665 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * budget-patch.c: driver for Budget Patch, - * hardware modification of DVB-S cards enabling full TS - * - * Written by Emard - * - * Original idea by Roberto Deza - * - * Special thanks to Holger Waechtler, Michael Hunold, Marian Durkovic - * and Metzlerbros - * - * the project's page is at https://linuxtv.org - */ - -#include "av7110.h" -#include "av7110_hw.h" -#include "budget.h" -#include "stv0299.h" -#include "ves1x93.h" -#include "tda8083.h" - -#include "bsru6.h" - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - -#define budget_patch budget - -static struct saa7146_extension budget_extension; - -MAKE_BUDGET_INFO(ttbp, "TT-Budget/Patch DVB-S 1.x PCI", BUDGET_PATCH); -//MAKE_BUDGET_INFO(satel,"TT-Budget/Patch SATELCO PCI", BUDGET_TT_HW_DISEQC); - -static const struct pci_device_id pci_tbl[] = { - MAKE_EXTENSION_PCI(ttbp,0x13c2, 0x0000), -// MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013), - { - .vendor = 0, - } -}; - -/* those lines are for budget-patch to be tried -** on a true budget card and observe the -** behaviour of VSYNC generated by rps1. -** this code was shamelessly copy/pasted from budget.c -*/ -static void gpio_Set22K (struct budget *budget, int state) -{ - struct saa7146_dev *dev=budget->dev; - dprintk(2, "budget: %p\n", budget); - saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO)); -} - -/* Diseqc functions only for TT Budget card */ -/* taken from the Skyvision DVB driver by - Ralph Metzler */ - -static void DiseqcSendBit (struct budget *budget, int data) -{ - struct saa7146_dev *dev=budget->dev; - dprintk(2, "budget: %p\n", budget); - - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); - udelay(data ? 500 : 1000); - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); - udelay(data ? 1000 : 500); -} - -static void DiseqcSendByte (struct budget *budget, int data) -{ - int i, par=1, d; - - dprintk(2, "budget: %p\n", budget); - - for (i=7; i>=0; i--) { - d = (data>>i)&1; - par ^= d; - DiseqcSendBit(budget, d); - } - - DiseqcSendBit(budget, par); -} - -static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst) -{ - struct saa7146_dev *dev=budget->dev; - int i; - - dprintk(2, "budget: %p\n", budget); - - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); - mdelay(16); - - for (i=0; idvb->priv; - - switch (tone) { - case SEC_TONE_ON: - gpio_Set22K (budget, 1); - break; - - case SEC_TONE_OFF: - gpio_Set22K (budget, 0); - break; - - default: - return -EINVAL; - } - - return 0; -} - -static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) -{ - struct budget* budget = (struct budget*) fe->dvb->priv; - - SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0); - - return 0; -} - -static int budget_diseqc_send_burst(struct dvb_frontend *fe, - enum fe_sec_mini_cmd minicmd) -{ - struct budget* budget = (struct budget*) fe->dvb->priv; - - SendDiSEqCMsg (budget, 0, NULL, minicmd); - - return 0; -} - -static int budget_av7110_send_fw_cmd(struct budget_patch *budget, u16* buf, int length) -{ - int i; - - dprintk(2, "budget: %p\n", budget); - - for (i = 2; i < length; i++) - { - ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2*i, 2, (u32) buf[i], 0,0); - msleep(5); - } - if (length) - ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2, 2, (u32) buf[1], 0,0); - else - ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2, 2, 0, 0,0); - msleep(5); - ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND, 2, (u32) buf[0], 0,0); - msleep(5); - return 0; -} - -static void av7110_set22k(struct budget_patch *budget, int state) -{ - u16 buf[2] = {( COMTYPE_AUDIODAC << 8) | (state ? ON22K : OFF22K), 0}; - - dprintk(2, "budget: %p\n", budget); - budget_av7110_send_fw_cmd(budget, buf, 2); -} - -static int av7110_send_diseqc_msg(struct budget_patch *budget, int len, u8 *msg, int burst) -{ - int i; - u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) | SendDiSEqC), - 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - dprintk(2, "budget: %p\n", budget); - - if (len>10) - len=10; - - buf[1] = len+2; - buf[2] = len; - - if (burst != -1) - buf[3]=burst ? 0x01 : 0x00; - else - buf[3]=0xffff; - - for (i=0; idvb->priv; - - switch (tone) { - case SEC_TONE_ON: - av7110_set22k (budget, 1); - break; - - case SEC_TONE_OFF: - av7110_set22k (budget, 0); - break; - - default: - return -EINVAL; - } - - return 0; -} - -static int budget_patch_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) -{ - struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; - - av7110_send_diseqc_msg (budget, cmd->msg_len, cmd->msg, 0); - - return 0; -} - -static int budget_patch_diseqc_send_burst(struct dvb_frontend *fe, - enum fe_sec_mini_cmd minicmd) -{ - struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; - - av7110_send_diseqc_msg (budget, 0, NULL, minicmd); - - return 0; -} - -static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; - u8 pwr = 0; - u8 buf[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; - u32 div = (p->frequency + 479500) / 125; - - if (p->frequency > 2000000) - pwr = 3; - else if (p->frequency > 1800000) - pwr = 2; - else if (p->frequency > 1600000) - pwr = 1; - else if (p->frequency > 1200000) - pwr = 0; - else if (p->frequency >= 1100000) - pwr = 1; - else pwr = 2; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = ((div & 0x18000) >> 10) | 0x95; - buf[3] = (pwr << 6) | 0x30; - - // NOTE: since we're using a prescaler of 2, we set the - // divisor frequency to 62.5kHz and divide by 125 above - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static struct ves1x93_config alps_bsrv2_config = { - .demod_address = 0x08, - .xin = 90100000UL, - .invert_pwm = 0, -}; - -static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; - u32 div; - u8 data[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; - - div = p->frequency / 125; - data[0] = (div >> 8) & 0x7f; - data[1] = div & 0xff; - data[2] = 0x8e; - data[3] = 0x00; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static struct tda8083_config grundig_29504_451_config = { - .demod_address = 0x68, -}; - -static void frontend_init(struct budget_patch* budget) -{ - switch(budget->dev->pci->subsystem_device) { - case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X - case 0x1013: // SATELCO Multimedia PCI - - // try the ALPS BSRV2 first of all - budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; - budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_patch_diseqc_send_master_cmd; - budget->dvb_frontend->ops.diseqc_send_burst = budget_patch_diseqc_send_burst; - budget->dvb_frontend->ops.set_tone = budget_patch_set_tone; - break; - } - - // try the ALPS BSRU6 now - budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; - budget->dvb_frontend->tuner_priv = &budget->i2c_adap; - - budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; - budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; - budget->dvb_frontend->ops.set_tone = budget_set_tone; - break; - } - - // Try the grundig 29504-451 - budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap); - if (budget->dvb_frontend) { - budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; - budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; - budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; - budget->dvb_frontend->ops.set_tone = budget_set_tone; - break; - } - break; - } - - if (budget->dvb_frontend == NULL) { - printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", - budget->dev->pci->vendor, - budget->dev->pci->device, - budget->dev->pci->subsystem_vendor, - budget->dev->pci->subsystem_device); - } else { - if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend)) { - printk("budget-av: Frontend registration failed!\n"); - dvb_frontend_detach(budget->dvb_frontend); - budget->dvb_frontend = NULL; - } - } -} - -/* written by Emard */ -static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) -{ - struct budget_patch *budget; - int err; - int count = 0; - int detected = 0; - -#define PATCH_RESET 0 -#define RPS_IRQ 0 -#define HPS_SETUP 0 -#if PATCH_RESET - saa7146_write(dev, MC1, MASK_31); - msleep(40); -#endif -#if HPS_SETUP - // initialize registers. Better to have it like this - // than leaving something unconfigured - saa7146_write(dev, DD1_STREAM_B, 0); - // port B VSYNC at rising edge - saa7146_write(dev, DD1_INIT, 0x00000200); // have this in budget-core too! - saa7146_write(dev, BRS_CTRL, 0x00000000); // VBI - - // debi config - // saa7146_write(dev, DEBI_CONFIG, MASK_30|MASK_28|MASK_18); - - // zero all HPS registers - saa7146_write(dev, HPS_H_PRESCALE, 0); // r68 - saa7146_write(dev, HPS_H_SCALE, 0); // r6c - saa7146_write(dev, BCS_CTRL, 0); // r70 - saa7146_write(dev, HPS_V_SCALE, 0); // r60 - saa7146_write(dev, HPS_V_GAIN, 0); // r64 - saa7146_write(dev, CHROMA_KEY_RANGE, 0); // r74 - saa7146_write(dev, CLIP_FORMAT_CTRL, 0); // r78 - // Set HPS prescaler for port B input - saa7146_write(dev, HPS_CTRL, (1<<30) | (0<<29) | (1<<28) | (0<<12) ); - saa7146_write(dev, MC2, - 0 * (MASK_08 | MASK_24) | // BRS control - 0 * (MASK_09 | MASK_25) | // a - 0 * (MASK_10 | MASK_26) | // b - 1 * (MASK_06 | MASK_22) | // HPS_CTRL1 - 1 * (MASK_05 | MASK_21) | // HPS_CTRL2 - 0 * (MASK_01 | MASK_15) // DEBI - ); -#endif - // Disable RPS1 and RPS0 - saa7146_write(dev, MC1, ( MASK_29 | MASK_28)); - // RPS1 timeout disable - saa7146_write(dev, RPS_TOV1, 0); - - // code for autodetection - // will wait for VBI_B event (vertical blank at port B) - // and will reset GPIO3 after VBI_B is detected. - // (GPIO3 should be raised high by CPU to - // test if GPIO3 will generate vertical blank signal - // in budget patch GPIO3 is connected to VSYNC_B - count = 0; -#if 0 - WRITE_RPS1(CMD_UPLOAD | - MASK_10 | MASK_09 | MASK_08 | MASK_06 | MASK_05 | MASK_04 | MASK_03 | MASK_02 ); -#endif - WRITE_RPS1(CMD_PAUSE | EVT_VBI_B); - WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); - WRITE_RPS1(GPIO3_MSK); - WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); -#if RPS_IRQ - // issue RPS1 interrupt to increment counter - WRITE_RPS1(CMD_INTERRUPT); - // at least a NOP is neede between two interrupts - WRITE_RPS1(CMD_NOP); - // interrupt again - WRITE_RPS1(CMD_INTERRUPT); -#endif - WRITE_RPS1(CMD_STOP); - -#if RPS_IRQ - // set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) - // use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled - // use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called - saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); - // set event counter 1 threshold to maximum allowed value (rEC p55) - saa7146_write(dev, ECT1R, 0x3fff ); -#endif - // Fix VSYNC level - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); - // Set RPS1 Address register to point to RPS code (r108 p42) - saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); - // Enable RPS1, (rFC p33) - saa7146_write(dev, MC1, (MASK_13 | MASK_29 )); - - - mdelay(50); - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); - mdelay(150); - - - if( (saa7146_read(dev, GPIO_CTRL) & 0x10000000) == 0) - detected = 1; - -#if RPS_IRQ - printk("Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff ); -#endif - // Disable RPS1 - saa7146_write(dev, MC1, ( MASK_29 )); - - if(detected == 0) - printk("budget-patch not detected or saa7146 in non-default state.\n" - "try enabling resetting of 7146 with MASK_31 in MC1 register\n"); - - else - printk("BUDGET-PATCH DETECTED.\n"); - - -/* OLD (Original design by Roberto Deza): -** This code will setup the SAA7146_RPS1 to generate a square -** wave on GPIO3, changing when a field (TS_HEIGHT/2 "lines" of -** TS_WIDTH packets) has been acquired on SAA7146_D1B video port; -** then, this GPIO3 output which is connected to the D1B_VSYNC -** input, will trigger the acquisition of the alternate field -** and so on. -** Currently, the TT_budget / WinTV_Nova cards have two ICs -** (74HCT4040, LVC74) for the generation of this VSYNC signal, -** which seems that can be done perfectly without this :-)). -*/ - -/* New design (By Emard) -** this rps1 code will copy internal HS event to GPIO3 pin. -** GPIO3 is in budget-patch hardware connected to port B VSYNC - -** HS is an internal event of 7146, accessible with RPS -** and temporarily raised high every n lines -** (n in defined in the RPS_THRESH1 counter threshold) -** I think HS is raised high on the beginning of the n-th line -** and remains high until this n-th line that triggered -** it is completely received. When the reception of n-th line -** ends, HS is lowered. - -** To transmit data over DMA, 7146 needs changing state at -** port B VSYNC pin. Any changing of port B VSYNC will -** cause some DMA data transfer, with more or less packets loss. -** It depends on the phase and frequency of VSYNC and -** the way of 7146 is instructed to trigger on port B (defined -** in DD1_INIT register, 3rd nibble from the right valid -** numbers are 0-7, see datasheet) -** -** The correct triggering can minimize packet loss, -** dvbtraffic should give this stable bandwidths: -** 22k transponder = 33814 kbit/s -** 27.5k transponder = 38045 kbit/s -** by experiment it is found that the best results -** (stable bandwidths and almost no packet loss) -** are obtained using DD1_INIT triggering number 2 -** (Va at rising edge of VS Fa = HS x VS-failing forced toggle) -** and a VSYNC phase that occurs in the middle of DMA transfer -** (about byte 188*512=96256 in the DMA window). -** -** Phase of HS is still not clear to me how to control, -** It just happens to be so. It can be seen if one enables -** RPS_IRQ and print Event Counter 1 in vpeirq(). Every -** time RPS_INTERRUPT is called, the Event Counter 1 will -** increment. That's how the 7146 is programmed to do event -** counting in this budget-patch.c -** I *think* HPS setting has something to do with the phase -** of HS but I can't be 100% sure in that. - -** hardware debug note: a working budget card (including budget patch) -** with vpeirq() interrupt setup in mode "0x90" (every 64K) will -** generate 3 interrupts per 25-Hz DMA frame of 2*188*512 bytes -** and that means 3*25=75 Hz of interrupt frequency, as seen by -** watch cat /proc/interrupts -** -** If this frequency is 3x lower (and data received in the DMA -** buffer don't start with 0x47, but in the middle of packets, -** whose lengths appear to be like 188 292 188 104 etc. -** this means VSYNC line is not connected in the hardware. -** (check soldering pcb and pins) -** The same behaviour of missing VSYNC can be duplicated on budget -** cards, by setting DD1_INIT trigger mode 7 in 3rd nibble. -*/ - - // Setup RPS1 "program" (p35) - count = 0; - - - // Wait Source Line Counter Threshold (p36) - WRITE_RPS1(CMD_PAUSE | EVT_HS); - // Set GPIO3=1 (p42) - WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); - WRITE_RPS1(GPIO3_MSK); - WRITE_RPS1(SAA7146_GPIO_OUTHI<<24); -#if RPS_IRQ - // issue RPS1 interrupt - WRITE_RPS1(CMD_INTERRUPT); -#endif - // Wait reset Source Line Counter Threshold (p36) - WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS); - // Set GPIO3=0 (p42) - WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); - WRITE_RPS1(GPIO3_MSK); - WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); -#if RPS_IRQ - // issue RPS1 interrupt - WRITE_RPS1(CMD_INTERRUPT); -#endif - // Jump to begin of RPS program (p37) - WRITE_RPS1(CMD_JUMP); - WRITE_RPS1(dev->d_rps1.dma_handle); - - // Fix VSYNC level - saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); - // Set RPS1 Address register to point to RPS code (r108 p42) - saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); - - if (!(budget = kmalloc (sizeof(struct budget_patch), GFP_KERNEL))) - return -ENOMEM; - - dprintk(2, "budget: %p\n", budget); - - err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr); - if (err) { - kfree(budget); - return err; - } - - // Set Source Line Counter Threshold, using BRS (rCC p43) - // It generates HS event every TS_HEIGHT lines - // this is related to TS_WIDTH set in register - // NUM_LINE_BYTE3 in budget-core.c. If NUM_LINE_BYTE - // low 16 bits are set to TS_WIDTH bytes (TS_WIDTH=2*188 - //,then RPS_THRESH1 - // should be set to trigger every TS_HEIGHT (512) lines. - // - saa7146_write(dev, RPS_THRESH1, budget->buffer_height | MASK_12 ); - - // saa7146_write(dev, RPS_THRESH0, ((TS_HEIGHT/2)<<16) |MASK_28| (TS_HEIGHT/2) |MASK_12 ); - // Enable RPS1 (rFC p33) - saa7146_write(dev, MC1, (MASK_13 | MASK_29)); - - - dev->ext_priv = budget; - - budget->dvb_adapter.priv = budget; - frontend_init(budget); - - ttpci_budget_init_hooks(budget); - - return 0; -} - -static int budget_patch_detach (struct saa7146_dev* dev) -{ - struct budget_patch *budget = (struct budget_patch*) dev->ext_priv; - int err; - - if (budget->dvb_frontend) { - dvb_unregister_frontend(budget->dvb_frontend); - dvb_frontend_detach(budget->dvb_frontend); - } - err = ttpci_budget_deinit (budget); - - kfree (budget); - - return err; -} - -static int __init budget_patch_init(void) -{ - return saa7146_register_extension(&budget_extension); -} - -static void __exit budget_patch_exit(void) -{ - saa7146_unregister_extension(&budget_extension); -} - -static struct saa7146_extension budget_extension = { - .name = "budget_patch dvb", - .flags = 0, - - .module = THIS_MODULE, - .pci_tbl = pci_tbl, - .attach = budget_patch_attach, - .detach = budget_patch_detach, - - .irq_mask = MASK_10, - .irq_func = ttpci_budget_irq10_handler, -}; - -module_init(budget_patch_init); -module_exit(budget_patch_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Emard, Roberto Deza, Holger Waechtler, Michael Hunold, others"); -MODULE_DESCRIPTION("Driver for full TS modified DVB-S SAA7146+AV7110 based so-called Budget Patch cards"); -- cgit v1.2.3 From 5a053c05a81eb18b2393f4dd1634a3887831f037 Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:24:42 +0200 Subject: media: sp8870: coding style fixes: whitespace This patch fixes the following checkpatch warnings: ERROR:BRACKET_SPACE: space prohibited before open square bracket '[' ERROR:POINTER_LOCATION: "foo * bar" should be "foo *bar" ERROR:POINTER_LOCATION: "foo* bar" should be "foo *bar" ERROR:SPACING: space prohibited before that ',' (ctx:WxW) ERROR:SPACING: space required before the open brace '{' WARNING:SPACING: space prohibited between function name and open parenthesis '(' CHECK:PARENTHESIS_ALIGNMENT: Alignment should match open parenthesis Whitespace changes only. "git diff -w" shows no changes. Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/sp8870.c | 80 +++++++++++++++++------------------ drivers/staging/media/av7110/sp8870.h | 6 +-- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/drivers/staging/media/av7110/sp8870.c b/drivers/staging/media/av7110/sp8870.c index abf5c72607b6..4fb8518d2926 100644 --- a/drivers/staging/media/av7110/sp8870.c +++ b/drivers/staging/media/av7110/sp8870.c @@ -28,9 +28,9 @@ struct sp8870_state { - struct i2c_adapter* i2c; + struct i2c_adapter *i2c; - const struct sp8870_config* config; + const struct sp8870_config *config; struct dvb_frontend frontend; @@ -50,9 +50,9 @@ static int debug; /* starting point for firmware in file 'Sc_main.mc' */ #define SP8870_FIRMWARE_OFFSET 0x0A -static int sp8870_writereg (struct sp8870_state* state, u16 reg, u16 data) +static int sp8870_writereg(struct sp8870_state *state, u16 reg, u16 data) { - u8 buf [] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff }; + u8 buf[] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff }; struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 4 }; int err; @@ -64,15 +64,15 @@ static int sp8870_writereg (struct sp8870_state* state, u16 reg, u16 data) return 0; } -static int sp8870_readreg (struct sp8870_state* state, u16 reg) +static int sp8870_readreg(struct sp8870_state *state, u16 reg) { int ret; - u8 b0 [] = { reg >> 8 , reg & 0xff }; - u8 b1 [] = { 0, 0 }; + u8 b0[] = { reg >> 8, reg & 0xff }; + u8 b1[] = { 0, 0 }; struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 }, { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 } }; - ret = i2c_transfer (state->i2c, msg, 2); + ret = i2c_transfer(state->i2c, msg, 2); if (ret != 2) { dprintk("%s: readreg error (ret == %i)\n", __func__, ret); @@ -82,7 +82,7 @@ static int sp8870_readreg (struct sp8870_state* state, u16 reg) return (b1[0] << 8 | b1[1]); } -static int sp8870_firmware_upload (struct sp8870_state* state, const struct firmware *fw) +static int sp8870_firmware_upload(struct sp8870_state *state, const struct firmware *fw) { struct i2c_msg msg; const char *fw_buf = fw->data; @@ -107,7 +107,7 @@ static int sp8870_firmware_upload (struct sp8870_state* state, const struct firm // do firmware upload fw_pos = SP8870_FIRMWARE_OFFSET; - while (fw_pos < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET){ + while (fw_pos < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET) { tx_len = (fw_pos <= SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - 252) ? 252 : SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - fw_pos; // write register 0xCF0A tx_buf[0] = 0xCF; @@ -129,7 +129,7 @@ static int sp8870_firmware_upload (struct sp8870_state* state, const struct firm return 0; }; -static void sp8870_microcontroller_stop (struct sp8870_state* state) +static void sp8870_microcontroller_stop(struct sp8870_state *state) { sp8870_writereg(state, 0x0F08, 0x000); sp8870_writereg(state, 0x0F09, 0x000); @@ -138,7 +138,7 @@ static void sp8870_microcontroller_stop (struct sp8870_state* state) sp8870_writereg(state, 0x0F00, 0x000); } -static void sp8870_microcontroller_start (struct sp8870_state* state) +static void sp8870_microcontroller_start(struct sp8870_state *state) { sp8870_writereg(state, 0x0F08, 0x000); sp8870_writereg(state, 0x0F09, 0x000); @@ -150,12 +150,12 @@ static void sp8870_microcontroller_start (struct sp8870_state* state) sp8870_readreg(state, 0x0D01); } -static int sp8870_read_data_valid_signal(struct sp8870_state* state) +static int sp8870_read_data_valid_signal(struct sp8870_state *state) { return (sp8870_readreg(state, 0x0D02) > 0); } -static int configure_reg0xc05 (struct dtv_frontend_properties *p, u16 *reg0xc05) +static int configure_reg0xc05(struct dtv_frontend_properties *p, u16 *reg0xc05) { int known_parameters = 1; @@ -226,7 +226,7 @@ static int configure_reg0xc05 (struct dtv_frontend_properties *p, u16 *reg0xc05) return 0; } -static int sp8870_wake_up(struct sp8870_state* state) +static int sp8870_wake_up(struct sp8870_state *state) { // enable TS output and interface pins return sp8870_writereg(state, 0xC18, 0x00D); @@ -235,7 +235,7 @@ static int sp8870_wake_up(struct sp8870_state* state) static int sp8870_set_frontend_parameters(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct sp8870_state* state = fe->demodulator_priv; + struct sp8870_state *state = fe->demodulator_priv; int err; u16 reg0xc05; @@ -290,9 +290,9 @@ static int sp8870_set_frontend_parameters(struct dvb_frontend *fe) return 0; } -static int sp8870_init (struct dvb_frontend* fe) +static int sp8870_init(struct dvb_frontend *fe) { - struct sp8870_state* state = fe->demodulator_priv; + struct sp8870_state *state = fe->demodulator_priv; const struct firmware *fw = NULL; sp8870_wake_up(state); @@ -342,17 +342,17 @@ static int sp8870_init (struct dvb_frontend* fe) static int sp8870_read_status(struct dvb_frontend *fe, enum fe_status *fe_status) { - struct sp8870_state* state = fe->demodulator_priv; + struct sp8870_state *state = fe->demodulator_priv; int status; int signal; *fe_status = 0; - status = sp8870_readreg (state, 0x0200); + status = sp8870_readreg(state, 0x0200); if (status < 0) return -EIO; - signal = sp8870_readreg (state, 0x0303); + signal = sp8870_readreg(state, 0x0303); if (signal < 0) return -EIO; @@ -366,9 +366,9 @@ static int sp8870_read_status(struct dvb_frontend *fe, return 0; } -static int sp8870_read_ber (struct dvb_frontend* fe, u32 * ber) +static int sp8870_read_ber(struct dvb_frontend *fe, u32 *ber) { - struct sp8870_state* state = fe->demodulator_priv; + struct sp8870_state *state = fe->demodulator_priv; int ret; u32 tmp; @@ -393,21 +393,21 @@ static int sp8870_read_ber (struct dvb_frontend* fe, u32 * ber) return 0; } -static int sp8870_read_signal_strength(struct dvb_frontend* fe, u16 * signal) +static int sp8870_read_signal_strength(struct dvb_frontend *fe, u16 *signal) { - struct sp8870_state* state = fe->demodulator_priv; + struct sp8870_state *state = fe->demodulator_priv; int ret; u16 tmp; *signal = 0; - ret = sp8870_readreg (state, 0x306); + ret = sp8870_readreg(state, 0x306); if (ret < 0) return -EIO; tmp = ret << 8; - ret = sp8870_readreg (state, 0x303); + ret = sp8870_readreg(state, 0x303); if (ret < 0) return -EIO; @@ -419,9 +419,9 @@ static int sp8870_read_signal_strength(struct dvb_frontend* fe, u16 * signal) return 0; } -static int sp8870_read_uncorrected_blocks (struct dvb_frontend* fe, u32* ublocks) +static int sp8870_read_uncorrected_blocks(struct dvb_frontend *fe, u32 *ublocks) { - struct sp8870_state* state = fe->demodulator_priv; + struct sp8870_state *state = fe->demodulator_priv; int ret; *ublocks = 0; @@ -451,7 +451,7 @@ static int switches; static int sp8870_set_frontend(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct sp8870_state* state = fe->demodulator_priv; + struct sp8870_state *state = fe->demodulator_priv; /* The firmware of the sp8870 sometimes locks up after setting frontend parameters. @@ -507,15 +507,15 @@ static int sp8870_set_frontend(struct dvb_frontend *fe) return 0; } -static int sp8870_sleep(struct dvb_frontend* fe) +static int sp8870_sleep(struct dvb_frontend *fe) { - struct sp8870_state* state = fe->demodulator_priv; + struct sp8870_state *state = fe->demodulator_priv; // tristate TS output and disable interface pins return sp8870_writereg(state, 0xC18, 0x000); } -static int sp8870_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +static int sp8870_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *fesettings) { fesettings->min_delay_ms = 350; fesettings->step_size = 0; @@ -523,9 +523,9 @@ static int sp8870_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend return 0; } -static int sp8870_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +static int sp8870_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) { - struct sp8870_state* state = fe->demodulator_priv; + struct sp8870_state *state = fe->demodulator_priv; if (enable) { return sp8870_writereg(state, 0x206, 0x001); @@ -534,18 +534,18 @@ static int sp8870_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) } } -static void sp8870_release(struct dvb_frontend* fe) +static void sp8870_release(struct dvb_frontend *fe) { - struct sp8870_state* state = fe->demodulator_priv; + struct sp8870_state *state = fe->demodulator_priv; kfree(state); } static const struct dvb_frontend_ops sp8870_ops; -struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, - struct i2c_adapter* i2c) +struct dvb_frontend *sp8870_attach(const struct sp8870_config *config, + struct i2c_adapter *i2c) { - struct sp8870_state* state = NULL; + struct sp8870_state *state = NULL; /* allocate memory for the internal state */ state = kzalloc(sizeof(struct sp8870_state), GFP_KERNEL); diff --git a/drivers/staging/media/av7110/sp8870.h b/drivers/staging/media/av7110/sp8870.h index 5eacf39f425e..2d125c8f2493 100644 --- a/drivers/staging/media/av7110/sp8870.h +++ b/drivers/staging/media/av7110/sp8870.h @@ -19,15 +19,15 @@ struct sp8870_config u8 demod_address; /* request firmware for device */ - int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); + int (*request_firmware)(struct dvb_frontend *fe, const struct firmware **fw, char *name); }; #if IS_REACHABLE(CONFIG_DVB_SP8870) extern struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, struct i2c_adapter* i2c); #else -static inline struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, - struct i2c_adapter* i2c) +static inline struct dvb_frontend *sp8870_attach(const struct sp8870_config *config, + struct i2c_adapter *i2c) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; -- cgit v1.2.3 From 5f0edb7a8ba01f9328b6899282ac577c15d2a6af Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:24:43 +0200 Subject: media: sp8870: coding style fixes: newline, comments and braces This patch fixes the following checkpatch warnings: ERROR:OPEN_BRACE: open brace '{' following struct go on the same line ERROR:TRAILING_STATEMENTS: trailing statements should be on next line [*] WARNING:LINE_SPACING: Missing a blank line after declarations WARNING:BLOCK_COMMENT_STYLE: Block comments use a trailing */ on a separate line WARNING:BLOCK_COMMENT_STYLE: Block comments use * on subsequent lines CHECK:BRACES: Blank lines aren't necessary after an open brace '{' CHECK:LINE_SPACING: Please don't use multiple blank lines CHECK:LINE_SPACING: Please use a blank line after function/struct/union/enum declarations Except the 2 comment-blocks, newline changes only. [* The remaining trailing statement will be fixed in the logging patch.] Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/sp8870.c | 50 ++++++++++++++++++++--------------- drivers/staging/media/av7110/sp8870.h | 13 ++++----- 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/drivers/staging/media/av7110/sp8870.c b/drivers/staging/media/av7110/sp8870.c index 4fb8518d2926..4e2a1cbd4538 100644 --- a/drivers/staging/media/av7110/sp8870.c +++ b/drivers/staging/media/av7110/sp8870.c @@ -1,11 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - Driver for Spase SP8870 demodulator - - Copyright (C) 1999 Juergen Peitz - + * Driver for Spase SP8870 demodulator +.* + * Copyright (C) 1999 Juergen Peitz + */ -*/ /* * This driver needs external firmware. Please use the command * "/scripts/get_dvb_firmware alps_tdlb7" to @@ -25,9 +24,7 @@ #include #include "sp8870.h" - struct sp8870_state { - struct i2c_adapter *i2c; const struct sp8870_config *config; @@ -53,7 +50,13 @@ static int debug; static int sp8870_writereg(struct sp8870_state *state, u16 reg, u16 data) { u8 buf[] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff }; - struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 4 }; + struct i2c_msg msg = { + .addr = state->config->demod_address, + .flags = 0, + .buf = buf, + .len = 4 + }; + int err; if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { @@ -69,8 +72,10 @@ static int sp8870_readreg(struct sp8870_state *state, u16 reg) int ret; u8 b0[] = { reg >> 8, reg & 0xff }; u8 b1[] = { 0, 0 }; - struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 }, - { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 } }; + struct i2c_msg msg[] = { + { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 } + }; ret = i2c_transfer(state->i2c, msg, 2); @@ -108,7 +113,8 @@ static int sp8870_firmware_upload(struct sp8870_state *state, const struct firmw // do firmware upload fw_pos = SP8870_FIRMWARE_OFFSET; while (fw_pos < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET) { - tx_len = (fw_pos <= SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - 252) ? 252 : SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - fw_pos; + tx_len = (fw_pos <= SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - 252) ? 252 : + SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - fw_pos; // write register 0xCF0A tx_buf[0] = 0xCF; tx_buf[1] = 0x0A; @@ -248,7 +254,8 @@ static int sp8870_set_frontend_parameters(struct dvb_frontend *fe) // set tuner parameters if (fe->ops.tuner_ops.set_params) { fe->ops.tuner_ops.set_params(fe); - if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); } // sample rate correction bit [23..17] @@ -296,12 +303,12 @@ static int sp8870_init(struct dvb_frontend *fe) const struct firmware *fw = NULL; sp8870_wake_up(state); - if (state->initialised) return 0; + if (state->initialised) + return 0; state->initialised = 1; dprintk ("%s\n", __func__); - /* request the firmware, this will block until someone uploads it */ printk("sp8870: waiting for firmware upload (%s)...\n", SP8870_DEFAULT_FIRMWARE); if (state->config->request_firmware(fe, &fw, SP8870_DEFAULT_FIRMWARE)) { @@ -454,11 +461,11 @@ static int sp8870_set_frontend(struct dvb_frontend *fe) struct sp8870_state *state = fe->demodulator_priv; /* - The firmware of the sp8870 sometimes locks up after setting frontend parameters. - We try to detect this by checking the data valid signal. - If it is not set after MAXCHECKS we try to recover the lockup by setting - the frontend parameters again. - */ + * The firmware of the sp8870 sometimes locks up after setting frontend parameters. + * We try to detect this by checking the data valid signal. + * If it is not set after MAXCHECKS we try to recover the lockup by setting + * the frontend parameters again. + */ int err = 0; int valid = 0; @@ -468,7 +475,6 @@ static int sp8870_set_frontend(struct dvb_frontend *fe) dprintk("%s: frequency = %i\n", __func__, p->frequency); for (trials = 1; trials <= MAXTRIALS; trials++) { - err = sp8870_set_frontend_parameters(fe); if (err) return err; @@ -537,6 +543,7 @@ static int sp8870_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) static void sp8870_release(struct dvb_frontend *fe) { struct sp8870_state *state = fe->demodulator_priv; + kfree(state); } @@ -557,7 +564,8 @@ struct dvb_frontend *sp8870_attach(const struct sp8870_config *config, state->initialised = 0; /* check if the demod is there */ - if (sp8870_readreg(state, 0x0200) < 0) goto error; + if (sp8870_readreg(state, 0x0200) < 0) + goto error; /* create dvb_frontend */ memcpy(&state->frontend.ops, &sp8870_ops, sizeof(struct dvb_frontend_ops)); diff --git a/drivers/staging/media/av7110/sp8870.h b/drivers/staging/media/av7110/sp8870.h index 2d125c8f2493..2cfee1bb6d99 100644 --- a/drivers/staging/media/av7110/sp8870.h +++ b/drivers/staging/media/av7110/sp8870.h @@ -1,11 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - Driver for Spase SP8870 demodulator - - Copyright (C) 1999 Juergen Peitz - - -*/ + * Driver for Spase SP8870 demodulator + * + * Copyright (C) 1999 Juergen Peitz + */ #ifndef SP8870_H #define SP8870_H @@ -13,8 +11,7 @@ #include #include -struct sp8870_config -{ +struct sp8870_config { /* the demodulator's i2c address */ u8 demod_address; -- cgit v1.2.3 From 72707bdc22e2586232585935fa088bedb9a8940d Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:24:44 +0200 Subject: media: sp8870: coding style fixes: miscellaneous This patch fixes the following checkpatch: ERROR:ASSIGN_IN_IF: do not use assignment in if condition WARNING:BRACES: braces {} are not necessary for any arm of this statement WARNING:CONST_STRUCT: struct dvb_frontend_ops should normally be const WARNING:EXPORT_SYMBOL: EXPORT_SYMBOL(foo); should immediately follow its function/variable CHECK:ALLOC_SIZEOF_STRUCT: Prefer kzalloc(sizeof(*state)...) over kzalloc(sizeof(struct sp8870_state)...) CHECK:AVOID_EXTERNS: extern prototypes should be avoided in .h files CHECK:COMPARISON_TO_NULL: Comparison to NULL could be written ... CHECK:MULTIPLE_ASSIGNMENTS: multiple assignments should be avoided Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/sp8870.c | 24 +++++++++++++----------- drivers/staging/media/av7110/sp8870.h | 3 +-- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/staging/media/av7110/sp8870.c b/drivers/staging/media/av7110/sp8870.c index 4e2a1cbd4538..282beb15737f 100644 --- a/drivers/staging/media/av7110/sp8870.c +++ b/drivers/staging/media/av7110/sp8870.c @@ -59,7 +59,8 @@ static int sp8870_writereg(struct sp8870_state *state, u16 reg, u16 data) int err; - if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { + err = i2c_transfer(state->i2c, &msg, 1); + if (err != 1) { dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __func__, err, reg, data); return -EREMOTEIO; } @@ -123,7 +124,8 @@ static int sp8870_firmware_upload(struct sp8870_state *state, const struct firmw msg.flags = 0; msg.buf = tx_buf; msg.len = tx_len + 2; - if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { + err = i2c_transfer(state->i2c, &msg, 1); + if (err != 1) { printk("%s: firmware upload failed!\n", __func__); printk ("%s: i2c error (err == %i)\n", __func__, err); return err; @@ -245,7 +247,8 @@ static int sp8870_set_frontend_parameters(struct dvb_frontend *fe) int err; u16 reg0xc05; - if ((err = configure_reg0xc05(p, ®0xc05))) + err = configure_reg0xc05(p, ®0xc05); + if (err) return err; // system controller stop @@ -533,11 +536,10 @@ static int sp8870_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) { struct sp8870_state *state = fe->demodulator_priv; - if (enable) { + if (enable) return sp8870_writereg(state, 0x206, 0x001); - } else { + else return sp8870_writereg(state, 0x206, 0x000); - } } static void sp8870_release(struct dvb_frontend *fe) @@ -555,8 +557,9 @@ struct dvb_frontend *sp8870_attach(const struct sp8870_config *config, struct sp8870_state *state = NULL; /* allocate memory for the internal state */ - state = kzalloc(sizeof(struct sp8870_state), GFP_KERNEL); - if (state == NULL) goto error; + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + goto error; /* setup the state */ state->config = config; @@ -568,7 +571,7 @@ struct dvb_frontend *sp8870_attach(const struct sp8870_config *config, goto error; /* create dvb_frontend */ - memcpy(&state->frontend.ops, &sp8870_ops, sizeof(struct dvb_frontend_ops)); + memcpy(&state->frontend.ops, &sp8870_ops, sizeof(sp8870_ops)); state->frontend.demodulator_priv = state; return &state->frontend; @@ -576,6 +579,7 @@ error: kfree(state); return NULL; } +EXPORT_SYMBOL_GPL(sp8870_attach); static const struct dvb_frontend_ops sp8870_ops = { .delsys = { SYS_DVBT }, @@ -613,5 +617,3 @@ MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); MODULE_DESCRIPTION("Spase SP8870 DVB-T Demodulator driver"); MODULE_AUTHOR("Juergen Peitz"); MODULE_LICENSE("GPL"); - -EXPORT_SYMBOL_GPL(sp8870_attach); diff --git a/drivers/staging/media/av7110/sp8870.h b/drivers/staging/media/av7110/sp8870.h index 2cfee1bb6d99..3f566671cbf3 100644 --- a/drivers/staging/media/av7110/sp8870.h +++ b/drivers/staging/media/av7110/sp8870.h @@ -20,8 +20,7 @@ struct sp8870_config { }; #if IS_REACHABLE(CONFIG_DVB_SP8870) -extern struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, - struct i2c_adapter* i2c); +struct dvb_frontend *sp8870_attach(const struct sp8870_config *config, struct i2c_adapter *i2c); #else static inline struct dvb_frontend *sp8870_attach(const struct sp8870_config *config, struct i2c_adapter *i2c) -- cgit v1.2.3 From 47da4d15c93aaf055225106b382423453f5eecc8 Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:24:45 +0200 Subject: media: sp8870: coding style fixes: logging This patch fixes the following checkpatch warnings: WARNING:PREFER_PR_LEVEL: Prefer [subsystem eg: netdev]_dbg([subsystem]dev, ... then dev_dbg(dev, ... then pr_debug(... to printk(KERN_DEBUG ... WARNING:PRINTK_WITHOUT_KERN_LEVEL: printk() should include KERN_ facility level WARNING:TRACING_LOGGING: Unnecessary ftrace-like logging - prefer using ftrace ERROR:TRAILING_STATEMENTS: trailing statements should be on next line Convert logging to the recommend pr_* macros. Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/sp8870.c | 46 ++++++++++++++++++++--------------- drivers/staging/media/av7110/sp8870.h | 2 +- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/drivers/staging/media/av7110/sp8870.c b/drivers/staging/media/av7110/sp8870.c index 282beb15737f..0c813860f5b2 100644 --- a/drivers/staging/media/av7110/sp8870.c +++ b/drivers/staging/media/av7110/sp8870.c @@ -11,6 +11,12 @@ * download/extract it, and then copy it to /usr/lib/hotplug/firmware * or /lib/firmware (depending on configuration of firmware hotplug). */ + +#ifdef pr_fmt +#undef pr_fmt +#endif +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define SP8870_DEFAULT_FIRMWARE "dvb-fe-sp8870.fw" #include @@ -36,9 +42,10 @@ struct sp8870_state { }; static int debug; -#define dprintk(args...) \ +#define dprintk(fmt, arg...) \ do { \ - if (debug) printk(KERN_DEBUG "sp8870: " args); \ + if (debug) \ + pr_info("%s(): " fmt, __func__, ##arg); \ } while (0) /* firmware size for sp8870 */ @@ -61,7 +68,7 @@ static int sp8870_writereg(struct sp8870_state *state, u16 reg, u16 data) err = i2c_transfer(state->i2c, &msg, 1); if (err != 1) { - dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __func__, err, reg, data); + dprintk("writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", err, reg, data); return -EREMOTEIO; } @@ -81,7 +88,7 @@ static int sp8870_readreg(struct sp8870_state *state, u16 reg) ret = i2c_transfer(state->i2c, msg, 2); if (ret != 2) { - dprintk("%s: readreg error (ret == %i)\n", __func__, ret); + dprintk("readreg error (ret == %i)\n", ret); return -1; } @@ -97,7 +104,7 @@ static int sp8870_firmware_upload(struct sp8870_state *state, const struct firmw int tx_len; int err = 0; - dprintk ("%s: ...\n", __func__); + dprintk("start firmware upload...\n"); if (fw->size < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET) return -EINVAL; @@ -126,14 +133,14 @@ static int sp8870_firmware_upload(struct sp8870_state *state, const struct firmw msg.len = tx_len + 2; err = i2c_transfer(state->i2c, &msg, 1); if (err != 1) { - printk("%s: firmware upload failed!\n", __func__); - printk ("%s: i2c error (err == %i)\n", __func__, err); + pr_err("%s(): firmware upload failed!\n", __func__); + pr_err("%s(): i2c error (err == %i)\n", __func__, err); return err; } fw_pos += tx_len; } - dprintk ("%s: done!\n", __func__); + dprintk("firmware upload successful!\n"); return 0; }; @@ -310,22 +317,22 @@ static int sp8870_init(struct dvb_frontend *fe) return 0; state->initialised = 1; - dprintk ("%s\n", __func__); + dprintk("initialising frontend...\n"); /* request the firmware, this will block until someone uploads it */ - printk("sp8870: waiting for firmware upload (%s)...\n", SP8870_DEFAULT_FIRMWARE); + pr_info("waiting for firmware upload (%s)...\n", SP8870_DEFAULT_FIRMWARE); if (state->config->request_firmware(fe, &fw, SP8870_DEFAULT_FIRMWARE)) { - printk("sp8870: no firmware upload (timeout or file not found?)\n"); + pr_err("no firmware upload (timeout or file not found?)\n"); return -EIO; } if (sp8870_firmware_upload(state, fw)) { - printk("sp8870: writing firmware to device failed\n"); + pr_err("writing firmware to device failed\n"); release_firmware(fw); return -EIO; } release_firmware(fw); - printk("sp8870: firmware upload complete\n"); + pr_info("firmware upload complete\n"); /* enable TS output and interface pins */ sp8870_writereg(state, 0xc18, 0x00d); @@ -475,7 +482,7 @@ static int sp8870_set_frontend(struct dvb_frontend *fe) int trials = 0; int check_count = 0; - dprintk("%s: frequency = %i\n", __func__, p->frequency); + dprintk("frequency = %i\n", p->frequency); for (trials = 1; trials <= MAXTRIALS; trials++) { err = sp8870_set_frontend_parameters(fe); @@ -486,8 +493,7 @@ static int sp8870_set_frontend(struct dvb_frontend *fe) // valid = ((sp8870_readreg(i2c, 0x0200) & 4) == 0); valid = sp8870_read_data_valid_signal(state); if (valid) { - dprintk("%s: delay = %i usec\n", - __func__, check_count * 10); + dprintk("delay = %i usec\n", check_count * 10); break; } udelay(10); @@ -497,20 +503,20 @@ static int sp8870_set_frontend(struct dvb_frontend *fe) } if (!valid) { - printk("%s: firmware crash!!!!!!\n", __func__); + pr_err("%s(): firmware crash!!!!!!\n", __func__); return -EIO; } if (debug) { if (valid) { if (trials > 1) { - printk("%s: firmware lockup!!!\n", __func__); - printk("%s: recovered after %i trial(s))\n", __func__, trials - 1); + pr_info("%s(): firmware lockup!!!\n", __func__); + pr_info("%s(): recovered after %i trial(s))\n", __func__, trials - 1); lockups++; } } switches++; - printk("%s: switches = %i lockups = %i\n", __func__, switches, lockups); + pr_info("%s(): switches = %i lockups = %i\n", __func__, switches, lockups); } return 0; diff --git a/drivers/staging/media/av7110/sp8870.h b/drivers/staging/media/av7110/sp8870.h index 3f566671cbf3..3323d1dfa568 100644 --- a/drivers/staging/media/av7110/sp8870.h +++ b/drivers/staging/media/av7110/sp8870.h @@ -25,7 +25,7 @@ struct dvb_frontend *sp8870_attach(const struct sp8870_config *config, struct i2 static inline struct dvb_frontend *sp8870_attach(const struct sp8870_config *config, struct i2c_adapter *i2c) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + pr_warn(KBUILD_MODNAME ": %s(): driver disabled by Kconfig\n", __func__); return NULL; } #endif // CONFIG_DVB_SP8870 -- cgit v1.2.3 From 3b013d82145a433cd101096e9d1cec67eefb7fa1 Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:24:46 +0200 Subject: media: av7110: coding style fixes: pointer_location This patch fixes the following checkpatch warnings: ERROR:POINTER_LOCATION: "foo * bar" should be "foo *bar" ERROR:POINTER_LOCATION: "foo* bar" should be "foo *bar" ERROR:POINTER_LOCATION: "(foo*)" should be "(foo *)" Whitespace changes only. "git diff -w" shows no changes. Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 78 +++++++++++++++---------------- drivers/staging/media/av7110/av7110.h | 6 +-- drivers/staging/media/av7110/av7110_ca.c | 6 +-- drivers/staging/media/av7110/av7110_hw.c | 12 ++--- drivers/staging/media/av7110/av7110_v4l.c | 14 +++--- 5 files changed, 58 insertions(+), 58 deletions(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index a5a431c14ea7..eb1dc64bc8eb 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -429,7 +429,7 @@ static void debiirq(struct tasklet_struct *t) break; case DATA_DEBUG_MESSAGE: - ((s8*)av7110->debi_virt)[Reserved_SIZE - 1] = 0; + ((s8 *)av7110->debi_virt)[Reserved_SIZE - 1] = 0; printk("%s\n", (s8 *) av7110->debi_virt); xfer = RX_BUFF; break; @@ -1143,7 +1143,7 @@ static int dvb_get_stc(struct dmx_demux *demux, unsigned int num, static int av7110_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) { - struct av7110* av7110 = fe->dvb->priv; + struct av7110 *av7110 = fe->dvb->priv; switch (tone) { case SEC_TONE_ON: @@ -1157,18 +1157,18 @@ static int av7110_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) } } -static int av7110_diseqc_send_master_cmd(struct dvb_frontend* fe, - struct dvb_diseqc_master_cmd* cmd) +static int av7110_diseqc_send_master_cmd(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *cmd) { - struct av7110* av7110 = fe->dvb->priv; + struct av7110 *av7110 = fe->dvb->priv; return av7110_diseqc_send(av7110, cmd->msg_len, cmd->msg, -1); } -static int av7110_diseqc_send_burst(struct dvb_frontend* fe, +static int av7110_diseqc_send_burst(struct dvb_frontend *fe, enum fe_sec_mini_cmd minicmd) { - struct av7110* av7110 = fe->dvb->priv; + struct av7110 *av7110 = fe->dvb->priv; return av7110_diseqc_send(av7110, 0, NULL, minicmd); } @@ -1440,7 +1440,7 @@ u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg) ****************************************************************************/ -static int check_firmware(struct av7110* av7110) +static int check_firmware(struct av7110 *av7110) { u32 crc = 0, len = 0; unsigned char *ptr; @@ -1491,12 +1491,12 @@ static int check_firmware(struct av7110* av7110) return 0; } -static void put_firmware(struct av7110* av7110) +static void put_firmware(struct av7110 *av7110) { vfree(av7110->bin_fw); } -static int get_firmware(struct av7110* av7110) +static int get_firmware(struct av7110 *av7110) { int ret; const struct firmware *fw; @@ -1540,7 +1540,7 @@ static int get_firmware(struct av7110* av7110) static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; + struct av7110 *av7110 = fe->dvb->priv; u8 pwr = 0; u8 buf[4]; struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; @@ -1583,7 +1583,7 @@ static struct ves1x93_config alps_bsrv2_config = { static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; + struct av7110 *av7110 = fe->dvb->priv; u32 div; u8 data[4]; struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) }; @@ -1615,7 +1615,7 @@ static struct ves1820_config alps_tdbe2_config = { static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; + struct av7110 *av7110 = fe->dvb->priv; u32 div; u8 data[4]; struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; @@ -1642,7 +1642,7 @@ static struct tda8083_config grundig_29504_451_config = { static int philips_cd1516_tuner_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; + struct av7110 *av7110 = fe->dvb->priv; u32 div; u32 f = p->frequency; u8 data[4]; @@ -1674,7 +1674,7 @@ static struct ves1820_config philips_cd1516_config = { static int alps_tdlb7_tuner_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; + struct av7110 *av7110 = fe->dvb->priv; u32 div, pwr; u8 data[4]; struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = sizeof(data) }; @@ -1698,10 +1698,10 @@ static int alps_tdlb7_tuner_set_params(struct dvb_frontend *fe) return 0; } -static int alps_tdlb7_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) +static int alps_tdlb7_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name) { #if IS_ENABLED(CONFIG_DVB_SP8870) - struct av7110* av7110 = fe->dvb->priv; + struct av7110 *av7110 = fe->dvb->priv; return request_firmware(fw, name, &av7110->dev->pci->dev); #else @@ -1812,7 +1812,7 @@ static u8 nexusca_stv0297_inittab[] = { static int nexusca_stv0297_tuner_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; + struct av7110 *av7110 = fe->dvb->priv; u32 div; u8 data[4]; struct i2c_msg msg = { .addr = 0x63, .flags = 0, .buf = data, .len = sizeof(data) }; @@ -1868,7 +1868,7 @@ static struct stv0297_config nexusca_stv0297_config = { static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; - struct av7110* av7110 = fe->dvb->priv; + struct av7110 *av7110 = fe->dvb->priv; u32 div; u8 cfg, cpump, band_select; u8 data[4]; @@ -1956,7 +1956,7 @@ static int av7110_fe_lock_fix(struct av7110 *av7110, enum fe_status status) static int av7110_fe_set_frontend(struct dvb_frontend *fe) { - struct av7110* av7110 = fe->dvb->priv; + struct av7110 *av7110 = fe->dvb->priv; int ret = av7110_fe_lock_fix(av7110, 0); if (!ret) @@ -1965,9 +1965,9 @@ static int av7110_fe_set_frontend(struct dvb_frontend *fe) return ret; } -static int av7110_fe_init(struct dvb_frontend* fe) +static int av7110_fe_init(struct dvb_frontend *fe) { - struct av7110* av7110 = fe->dvb->priv; + struct av7110 *av7110 = fe->dvb->priv; int ret = av7110_fe_lock_fix(av7110, 0); if (!ret) @@ -1978,7 +1978,7 @@ static int av7110_fe_init(struct dvb_frontend* fe) static int av7110_fe_read_status(struct dvb_frontend *fe, enum fe_status *status) { - struct av7110* av7110 = fe->dvb->priv; + struct av7110 *av7110 = fe->dvb->priv; /* call the real implementation */ int ret = av7110->fe_read_status(fe, status); @@ -1988,9 +1988,9 @@ static int av7110_fe_read_status(struct dvb_frontend *fe, return ret; } -static int av7110_fe_diseqc_reset_overload(struct dvb_frontend* fe) +static int av7110_fe_diseqc_reset_overload(struct dvb_frontend *fe) { - struct av7110* av7110 = fe->dvb->priv; + struct av7110 *av7110 = fe->dvb->priv; int ret = av7110_fe_lock_fix(av7110, 0); if (!ret) @@ -1998,10 +1998,10 @@ static int av7110_fe_diseqc_reset_overload(struct dvb_frontend* fe) return ret; } -static int av7110_fe_diseqc_send_master_cmd(struct dvb_frontend* fe, - struct dvb_diseqc_master_cmd* cmd) +static int av7110_fe_diseqc_send_master_cmd(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *cmd) { - struct av7110* av7110 = fe->dvb->priv; + struct av7110 *av7110 = fe->dvb->priv; int ret = av7110_fe_lock_fix(av7110, 0); if (!ret) { @@ -2014,7 +2014,7 @@ static int av7110_fe_diseqc_send_master_cmd(struct dvb_frontend* fe, static int av7110_fe_diseqc_send_burst(struct dvb_frontend *fe, enum fe_sec_mini_cmd minicmd) { - struct av7110* av7110 = fe->dvb->priv; + struct av7110 *av7110 = fe->dvb->priv; int ret = av7110_fe_lock_fix(av7110, 0); if (!ret) { @@ -2027,7 +2027,7 @@ static int av7110_fe_diseqc_send_burst(struct dvb_frontend *fe, static int av7110_fe_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) { - struct av7110* av7110 = fe->dvb->priv; + struct av7110 *av7110 = fe->dvb->priv; int ret = av7110_fe_lock_fix(av7110, 0); if (!ret) { @@ -2040,7 +2040,7 @@ static int av7110_fe_set_tone(struct dvb_frontend *fe, static int av7110_fe_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage voltage) { - struct av7110* av7110 = fe->dvb->priv; + struct av7110 *av7110 = fe->dvb->priv; int ret = av7110_fe_lock_fix(av7110, 0); if (!ret) { @@ -2050,9 +2050,9 @@ static int av7110_fe_set_voltage(struct dvb_frontend *fe, return ret; } -static int av7110_fe_dishnetwork_send_legacy_command(struct dvb_frontend* fe, unsigned long cmd) +static int av7110_fe_dishnetwork_send_legacy_command(struct dvb_frontend *fe, unsigned long cmd) { - struct av7110* av7110 = fe->dvb->priv; + struct av7110 *av7110 = fe->dvb->priv; int ret = av7110_fe_lock_fix(av7110, 0); if (!ret) @@ -2060,7 +2060,7 @@ static int av7110_fe_dishnetwork_send_legacy_command(struct dvb_frontend* fe, un return ret; } -static void dvb_s_recover(struct av7110* av7110) +static void dvb_s_recover(struct av7110 *av7110) { av7110_fe_init(av7110->fe); @@ -2077,7 +2077,7 @@ static void dvb_s_recover(struct av7110* av7110) av7110_fe_set_frontend(av7110->fe); } -static u8 read_pwm(struct av7110* av7110) +static u8 read_pwm(struct av7110 *av7110) { u8 b = 0xff; u8 pwm; @@ -2346,7 +2346,7 @@ static int frontend_init(struct av7110 *av7110) * The same behaviour of missing VSYNC can be duplicated on budget * cards, by setting DD1_INIT trigger mode 7 in 3rd nibble. */ -static int av7110_attach(struct saa7146_dev* dev, +static int av7110_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *pci_ext) { const int length = TS_WIDTH * TS_HEIGHT; @@ -2453,7 +2453,7 @@ static int av7110_attach(struct saa7146_dev* dev, return -ENOMEM; } - av7110->card_name = (char*) pci_ext->ext_priv; + av7110->card_name = (char *)pci_ext->ext_priv; av7110->dev = dev; dev->ext_priv = av7110; @@ -2746,7 +2746,7 @@ err_kfree_0: goto out; } -static int av7110_detach(struct saa7146_dev* saa) +static int av7110_detach(struct saa7146_dev *saa) { struct av7110 *av7110 = saa->ext_priv; dprintk(4, "%p\n", av7110); @@ -2803,7 +2803,7 @@ static int av7110_detach(struct saa7146_dev* saa) } -static void av7110_irq(struct saa7146_dev* dev, u32 *isr) +static void av7110_irq(struct saa7146_dev *dev, u32 *isr) { struct av7110 *av7110 = dev->ext_priv; diff --git a/drivers/staging/media/av7110/av7110.h b/drivers/staging/media/av7110/av7110.h index 809d938ae166..e251f989e4c2 100644 --- a/drivers/staging/media/av7110/av7110.h +++ b/drivers/staging/media/av7110/av7110.h @@ -260,19 +260,19 @@ struct av7110 { unsigned char *bin_root; unsigned long size_root; - struct dvb_frontend* fe; + struct dvb_frontend *fe; enum fe_status fe_status; struct mutex ioctl_mutex; /* crash recovery */ - void (*recover)(struct av7110* av7110); + void (*recover)(struct av7110 *av7110); enum fe_sec_voltage saved_voltage; enum fe_sec_tone_mode saved_tone; struct dvb_diseqc_master_cmd saved_master_cmd; enum fe_sec_mini_cmd saved_minicmd; - int (*fe_init)(struct dvb_frontend* fe); + int (*fe_init)(struct dvb_frontend *fe); int (*fe_read_status)(struct dvb_frontend *fe, enum fe_status *status); int (*fe_diseqc_reset_overload)(struct dvb_frontend *fe); int (*fe_diseqc_send_master_cmd)(struct dvb_frontend *fe, diff --git a/drivers/staging/media/av7110/av7110_ca.c b/drivers/staging/media/av7110/av7110_ca.c index c1338e074a3d..9ff5bd6f5f27 100644 --- a/drivers/staging/media/av7110/av7110_ca.c +++ b/drivers/staging/media/av7110/av7110_ca.c @@ -294,7 +294,7 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) case CA_SET_DESCR: { - struct ca_descr *descr = (struct ca_descr*) parg; + struct ca_descr *descr = (struct ca_descr *)parg; if (descr->index >= 16 || descr->parity > 1) { mutex_unlock(&av7110->ioctl_mutex); @@ -369,12 +369,12 @@ void av7110_ca_unregister(struct av7110 *av7110) dvb_unregister_device(av7110->ca_dev); } -int av7110_ca_init(struct av7110* av7110) +int av7110_ca_init(struct av7110 *av7110) { return ci_ll_init(&av7110->ci_rbuffer, &av7110->ci_wbuffer, 8192); } -void av7110_ca_exit(struct av7110* av7110) +void av7110_ca_exit(struct av7110 *av7110) { ci_ll_release(&av7110->ci_rbuffer, &av7110->ci_wbuffer); } diff --git a/drivers/staging/media/av7110/av7110_hw.c b/drivers/staging/media/av7110/av7110_hw.c index a0be37717259..714a4438bd89 100644 --- a/drivers/staging/media/av7110/av7110_hw.c +++ b/drivers/staging/media/av7110/av7110_hw.c @@ -318,7 +318,7 @@ int av7110_wait_msgstate(struct av7110 *av7110, u16 flags) return 0; } -static int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) +static int __av7110_send_fw_cmd(struct av7110 *av7110, u16 *buf, int length) { int i; unsigned long start; @@ -453,7 +453,7 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) return 0; } -static int av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) +static int av7110_send_fw_cmd(struct av7110 *av7110, u16 *buf, int length) { int ret; @@ -606,7 +606,7 @@ int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, return 0; } -static int av7110_fw_query(struct av7110 *av7110, u16 tag, u16* buf, s16 length) +static int av7110_fw_query(struct av7110 *av7110, u16 tag, u16 *buf, s16 length) { int ret; ret = av7110_fw_request(av7110, &tag, 0, buf, length); @@ -851,7 +851,7 @@ static inline int WaitUntilBmpLoaded(struct av7110 *av7110) } static inline int LoadBitmap(struct av7110 *av7110, - u16 dx, u16 dy, int inc, u8 __user * data) + u16 dx, u16 dy, int inc, u8 __user *data) { u16 format; int bpp; @@ -960,7 +960,7 @@ static int OSDSetColor(struct av7110 *av7110, u8 color, u8 r, u8 g, u8 b, u8 ble return ret; } -static int OSDSetPalette(struct av7110 *av7110, u32 __user * colors, u8 first, u8 last) +static int OSDSetPalette(struct av7110 *av7110, u32 __user *colors, u8 first, u8 last) { int i; int length = last - first + 1; @@ -986,7 +986,7 @@ static int OSDSetPalette(struct av7110 *av7110, u32 __user * colors, u8 first, u } static int OSDSetBlock(struct av7110 *av7110, int x0, int y0, - int x1, int y1, int inc, u8 __user * data) + int x1, int y1, int inc, u8 __user *data) { uint w, h, bpp, bpl, size, lpb, bnum, brest; int i; diff --git a/drivers/staging/media/av7110/av7110_v4l.c b/drivers/staging/media/av7110/av7110_v4l.c index ed2c605808e8..2c4707a604cd 100644 --- a/drivers/staging/media/av7110/av7110_v4l.c +++ b/drivers/staging/media/av7110/av7110_v4l.c @@ -175,7 +175,7 @@ static int ves1820_set_tv_freq(struct saa7146_dev *dev, u32 freq) static int stv0297_set_tv_freq(struct saa7146_dev *dev, u32 freq) { - struct av7110 *av7110 = (struct av7110*)dev->ext_priv; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; u32 div; u8 data[4]; @@ -215,7 +215,7 @@ static const struct v4l2_audio msp3400_v4l2_audio = { static int av7110_dvb_c_switch(struct saa7146_dev *dev) { - struct av7110 *av7110 = (struct av7110*)dev->ext_priv; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; u16 adswitch; int source, sync; @@ -610,7 +610,7 @@ static int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) { struct saa7146_dev *dev = video_drvdata(file); - struct av7110 *av7110 = (struct av7110*) dev->ext_priv; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; struct v4l2_sliced_vbi_data d; int rc; @@ -795,7 +795,7 @@ int av7110_init_analog_module(struct av7110 *av7110) int av7110_init_v4l(struct av7110 *av7110) { - struct saa7146_dev* dev = av7110->dev; + struct saa7146_dev *dev = av7110->dev; struct saa7146_ext_vv *vv_data; int ret; @@ -853,7 +853,7 @@ int av7110_init_v4l(struct av7110 *av7110) int av7110_exit_v4l(struct av7110 *av7110) { - struct saa7146_dev* dev = av7110->dev; + struct saa7146_dev *dev = av7110->dev; saa7146_unregister_device(&av7110->v4l_dev, av7110->dev); saa7146_unregister_device(&av7110->vbi_dev, av7110->dev); @@ -909,9 +909,9 @@ static struct saa7146_standard dvb_standard[] = { } }; -static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std) +static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std) { - struct av7110 *av7110 = (struct av7110*) dev->ext_priv; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; if (std->id & V4L2_STD_PAL) { av7110->vidmode = AV7110_VIDEO_MODE_PAL; -- cgit v1.2.3 From a0706503158556555fdb6e13b86116ac3da4467a Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:24:47 +0200 Subject: media: av7110: coding style fixes: blank lines This patch fixes the following checkpatch warnings: CHECK:LINE_SPACING: Please don't use multiple blank lines CHECK:LINE_SPACING: Please use a blank line after function/struct/union/enum declarations CHECK:BRACES: Blank lines aren't necessary after an open brace '{' CHECK:BRACES: Blank lines aren't necessary before a close brace '}' WARNING:LINE_SPACING: Missing a blank line after declarations Added or removed blank lines only. "git diff --ignore-blank-lines" shows no changes. Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 55 ++++++++--------------------- drivers/staging/media/av7110/av7110.h | 10 ------ drivers/staging/media/av7110/av7110_av.c | 33 +++++++---------- drivers/staging/media/av7110/av7110_av.h | 1 - drivers/staging/media/av7110/av7110_ca.c | 4 --- drivers/staging/media/av7110/av7110_hw.c | 10 ++---- drivers/staging/media/av7110/av7110_hw.h | 12 ------- drivers/staging/media/av7110/av7110_ipack.c | 11 +----- drivers/staging/media/av7110/av7110_v4l.c | 7 +--- drivers/staging/media/av7110/dvb_filter.h | 4 --- 10 files changed, 33 insertions(+), 114 deletions(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index eb1dc64bc8eb..b19c27f6e478 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -12,7 +12,6 @@ * the project's page is at https://linuxtv.org */ - #include #include #include @@ -36,7 +35,6 @@ #include #include - #include #include @@ -57,7 +55,6 @@ #define TS_BUFLEN (TS_WIDTH*TS_HEIGHT) #define TS_MAX_PACKETS (TS_BUFLEN/TS_SIZE) - int av7110_debug; static int vidmode = CVBS_RGB_OUT; @@ -113,7 +110,6 @@ static int av7110_num; } \ } - static void init_av7110_av(struct av7110 *av7110) { int ret; @@ -273,7 +269,6 @@ static int arm_thread(void *data) return 0; } - /**************************************************************************** * IRQ handling ****************************************************************************/ @@ -325,12 +320,12 @@ static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len, } } - //#define DEBUG_TIMING static inline void print_time(char *s) { #ifdef DEBUG_TIMING struct timespec64 ts; + ktime_get_real_ts64(&ts); printk("%s: %lld.%09ld\n", s, (s64)ts.tv_sec, ts.tv_nsec); #endif @@ -376,7 +371,6 @@ static void debiirq(struct tasklet_struct *t) av7110->debitype = -1; switch (type & 0xff) { - case DATA_TS_RECORD: dvb_dmx_swfilter_packets(&av7110->demux, (const u8 *) av7110->debi_virt, @@ -410,6 +404,7 @@ static void debiirq(struct tasklet_struct *t) if (data_0 < 2 && data[2] == 0xff) { int flags = 0; + if (data[5] > 0) flags |= CA_CI_MODULE_PRESENT; if (data[5] > 5) @@ -489,7 +484,6 @@ static void gpioirq(struct tasklet_struct *t) dprintk(8, "GPIO0 irq 0x%04x %d\n", av7110->debitype, av7110->debilen); switch (av7110->debitype & 0xff) { - case DATA_TS_PLAY: case DATA_PES_PLAY: break; @@ -674,7 +668,6 @@ static void gpioirq(struct tasklet_struct *t) spin_unlock(&av7110->debilock); } - #ifdef CONFIG_DVB_AV7110_OSD static int dvb_osd_ioctl(struct file *file, unsigned int cmd, void *parg) @@ -692,7 +685,6 @@ static int dvb_osd_ioctl(struct file *file, return -EINVAL; } - static const struct file_operations dvb_osd_fops = { .owner = THIS_MODULE, .unlocked_ioctl = dvb_generic_ioctl, @@ -710,7 +702,6 @@ static struct dvb_device dvbdev_osd = { }; #endif /* CONFIG_DVB_AV7110_OSD */ - static inline int SetPIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, u16 subpid, u16 pcrpid) { @@ -738,6 +729,7 @@ int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, u16 subpid, u16 pcrpid) { int ret = 0; + dprintk(4, "%p\n", av7110); if (mutex_lock_interruptible(&av7110->pid_mutex)) @@ -763,7 +755,6 @@ int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, return ret; } - /****************************************************************************** * hardware filter functions ******************************************************************************/ @@ -856,7 +847,6 @@ static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter) return ret; } - static int dvb_feed_start_pid(struct dvb_demux_feed *dvbdmxfeed) { struct dvb_demux *dvbdmx = dvbdmxfeed->demux; @@ -1008,12 +998,12 @@ static int av7110_start_feed(struct dvb_demux_feed *feed) return ret; } - static int av7110_stop_feed(struct dvb_demux_feed *feed) { struct dvb_demux *demux = feed->demux; struct av7110 *av7110 = demux->priv; int i, rc, ret = 0; + dprintk(4, "%p\n", av7110); if (feed->type == DMX_TYPE_TS) { @@ -1056,7 +1046,6 @@ static int av7110_stop_feed(struct dvb_demux_feed *feed) return ret; } - static void restart_feeds(struct av7110 *av7110) { struct dvb_demux *dvbdmx = &av7110->demux; @@ -1135,12 +1124,10 @@ static int dvb_get_stc(struct dmx_demux *demux, unsigned int num, return 0; } - /****************************************************************************** * SEC device file operations ******************************************************************************/ - static int av7110_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone) { struct av7110 *av7110 = fe->dvb->priv; @@ -1365,7 +1352,6 @@ static int av7110_register(struct av7110 *av7110) return 0; } - static void dvb_unregister(struct av7110 *av7110) { struct dvb_demux *dvbdemux = &av7110->demux; @@ -1401,7 +1387,6 @@ static void dvb_unregister(struct av7110 *av7110) av7110_ca_unregister(av7110); } - /**************************************************************************** * I2C client commands ****************************************************************************/ @@ -1439,7 +1424,6 @@ u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg) * INITIALIZATION ****************************************************************************/ - static int check_firmware(struct av7110 *av7110) { u32 crc = 0, len = 0; @@ -1609,9 +1593,6 @@ static struct ves1820_config alps_tdbe2_config = { .selagc = VES1820_SELAGC_SIGNAMPERR, }; - - - static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; @@ -1637,8 +1618,6 @@ static struct tda8083_config grundig_29504_451_config = { .demod_address = 0x68, }; - - static int philips_cd1516_tuner_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; @@ -1669,8 +1648,6 @@ static struct ves1820_config philips_cd1516_config = { .selagc = VES1820_SELAGC_SIGNAMPERR, }; - - static int alps_tdlb7_tuner_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; @@ -1710,12 +1687,10 @@ static int alps_tdlb7_request_firmware(struct dvb_frontend *fe, const struct fir } static const struct sp8870_config alps_tdlb7_config = { - .demod_address = 0x71, .request_firmware = alps_tdlb7_request_firmware, }; - static u8 nexusca_stv0297_inittab[] = { 0x80, 0x01, 0x80, 0x00, @@ -1856,15 +1831,12 @@ static int nexusca_stv0297_tuner_set_params(struct dvb_frontend *fe) } static struct stv0297_config nexusca_stv0297_config = { - .demod_address = 0x1C, .inittab = nexusca_stv0297_inittab, .invert = 1, .stop_during_read = 1, }; - - static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; @@ -1911,8 +1883,6 @@ static struct l64781_config grundig_29504_401_config = { .demod_address = 0x55, }; - - static int av7110_fe_lock_fix(struct av7110 *av7110, enum fe_status status) { int ret = 0; @@ -1959,6 +1929,7 @@ static int av7110_fe_set_frontend(struct dvb_frontend *fe) struct av7110 *av7110 = fe->dvb->priv; int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) ret = av7110->fe_set_frontend(fe); @@ -1970,6 +1941,7 @@ static int av7110_fe_init(struct dvb_frontend *fe) struct av7110 *av7110 = fe->dvb->priv; int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) ret = av7110->fe_init(fe); return ret; @@ -1982,6 +1954,7 @@ static int av7110_fe_read_status(struct dvb_frontend *fe, /* call the real implementation */ int ret = av7110->fe_read_status(fe, status); + if (!ret) if (((*status ^ av7110->fe_status) & FE_HAS_LOCK) && (*status & FE_HAS_LOCK)) ret = av7110_fe_lock_fix(av7110, *status); @@ -1993,6 +1966,7 @@ static int av7110_fe_diseqc_reset_overload(struct dvb_frontend *fe) struct av7110 *av7110 = fe->dvb->priv; int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) ret = av7110->fe_diseqc_reset_overload(fe); return ret; @@ -2004,6 +1978,7 @@ static int av7110_fe_diseqc_send_master_cmd(struct dvb_frontend *fe, struct av7110 *av7110 = fe->dvb->priv; int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) { av7110->saved_master_cmd = *cmd; ret = av7110->fe_diseqc_send_master_cmd(fe, cmd); @@ -2017,6 +1992,7 @@ static int av7110_fe_diseqc_send_burst(struct dvb_frontend *fe, struct av7110 *av7110 = fe->dvb->priv; int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) { av7110->saved_minicmd = minicmd; ret = av7110->fe_diseqc_send_burst(fe, minicmd); @@ -2030,6 +2006,7 @@ static int av7110_fe_set_tone(struct dvb_frontend *fe, struct av7110 *av7110 = fe->dvb->priv; int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) { av7110->saved_tone = tone; ret = av7110->fe_set_tone(fe, tone); @@ -2043,6 +2020,7 @@ static int av7110_fe_set_voltage(struct dvb_frontend *fe, struct av7110 *av7110 = fe->dvb->priv; int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) { av7110->saved_voltage = voltage; ret = av7110->fe_set_voltage(fe, voltage); @@ -2055,6 +2033,7 @@ static int av7110_fe_dishnetwork_send_legacy_command(struct dvb_frontend *fe, un struct av7110 *av7110 = fe->dvb->priv; int ret = av7110_fe_lock_fix(av7110, 0); + if (!ret) ret = av7110->fe_dishnetwork_send_legacy_command(fe, cmd); return ret; @@ -2490,6 +2469,7 @@ static int av7110_attach(struct saa7146_dev *dev, /* check for full-ts flag in eeprom */ if (i2c_readreg(av7110, 0xaa, 0) == 0x4f && i2c_readreg(av7110, 0xaa, 1) == 0x45) { u8 flags = i2c_readreg(av7110, 0xaa, 2); + if (flags != 0xff && (flags & 0x01)) av7110->full_ts = true; } @@ -2645,7 +2625,6 @@ static int av7110_attach(struct saa7146_dev *dev, if (!av7110->debi_virt) goto err_saa71466_vfree_4; - av7110->iobuf = vmalloc(AVOUTLEN+AOUTLEN+BMPLEN+4*IPACKS); if (!av7110->iobuf) goto err_pci_free_5; @@ -2749,6 +2728,7 @@ err_kfree_0: static int av7110_detach(struct saa7146_dev *saa) { struct av7110 *av7110 = saa->ext_priv; + dprintk(4, "%p\n", av7110); #if IS_ENABLED(CONFIG_DVB_AV7110_IR) @@ -2802,7 +2782,6 @@ static int av7110_detach(struct saa7146_dev *saa) return 0; } - static void av7110_irq(struct saa7146_dev *dev, u32 *isr) { struct av7110 *av7110 = dev->ext_priv; @@ -2844,7 +2823,6 @@ static void av7110_irq(struct saa7146_dev *dev, u32 *isr) tasklet_schedule(&av7110->vpe_tasklet); } - static struct saa7146_extension av7110_extension_driver; #define MAKE_AV7110_INFO(x_var,x_name) \ @@ -2887,7 +2865,6 @@ static const struct pci_device_id pci_tbl[] = { MODULE_DEVICE_TABLE(pci, pci_tbl); - static struct saa7146_extension av7110_extension_driver = { .name = "av7110", .flags = SAA7146_USE_I2C_IRQ, @@ -2901,13 +2878,11 @@ static struct saa7146_extension av7110_extension_driver = { .irq_func = av7110_irq, }; - static int __init av7110_init(void) { return saa7146_register_extension(&av7110_extension_driver); } - static void __exit av7110_exit(void) { saa7146_unregister_extension(&av7110_extension_driver); diff --git a/drivers/staging/media/av7110/av7110.h b/drivers/staging/media/av7110/av7110.h index e251f989e4c2..2290300aee5e 100644 --- a/drivers/staging/media/av7110/av7110.h +++ b/drivers/staging/media/av7110/av7110.h @@ -35,7 +35,6 @@ #include - #define ANALOG_TUNER_VES1820 1 #define ANALOG_TUNER_STV0297 2 @@ -76,7 +75,6 @@ struct dvb_video_events { spinlock_t lock; }; - struct av7110; /* infrared remote control */ @@ -88,7 +86,6 @@ struct infrared { /* place to store all the necessary device information */ struct av7110 { - /* devices */ struct dvb_device dvb_dev; @@ -118,7 +115,6 @@ struct av7110 { #define DVB_ADAC_MSP34x5 3 #define DVB_ADAC_NONE -1 - /* buffers */ void *iobuf; /* memory for all buffers */ @@ -139,7 +135,6 @@ struct av7110 { #define BMP_LOADED 2 wait_queue_head_t bmpq; - /* DEBI and polled command interface */ spinlock_t debilock; @@ -147,7 +142,6 @@ struct av7110 { volatile int debitype; volatile int debilen; - /* Recording and playback flags */ int rec_mode; @@ -157,7 +151,6 @@ struct av7110 { #define RP_AUDIO 2 #define RP_AV 3 - /* OSD */ int osdwin; /* currently active window */ @@ -213,7 +206,6 @@ struct av7110 { int arm_errors; int registered; - /* AV711X */ u32 arm_fw; @@ -288,7 +280,6 @@ struct av7110 { int (*fe_set_frontend)(struct dvb_frontend *fe); }; - extern int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, u16 subpid, u16 pcrpid); @@ -307,7 +298,6 @@ extern int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val); extern u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg); extern int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val); - extern int av7110_init_analog_module(struct av7110 *av7110); extern int av7110_init_v4l(struct av7110 *av7110); extern int av7110_exit_v4l(struct av7110 *av7110); diff --git a/drivers/staging/media/av7110/av7110_av.c b/drivers/staging/media/av7110/av7110_av.c index 00dd6a7fea64..96309a896bca 100644 --- a/drivers/staging/media/av7110/av7110_av.c +++ b/drivers/staging/media/av7110/av7110_av.c @@ -70,12 +70,10 @@ #define PIECE_RATE 0x40 #define SEAM_SPLICE 0x20 - static void p_to_t(u8 const *buf, long int length, u16 pid, u8 *counter, struct dvb_demux_feed *feed); static int write_ts_to_decoder(struct av7110 *av7110, int type, const u8 *buf, size_t len); - int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len) { struct dvb_demux_feed *dvbdmxfeed = p2t->priv; @@ -149,6 +147,7 @@ int av7110_av_start_record(struct av7110 *av7110, int av, int av7110_av_start_play(struct av7110 *av7110, int av) { int ret = 0; + dprintk(2, "av7110:%p, \n", av7110); if (av7110->rec_mode) @@ -183,6 +182,7 @@ int av7110_av_start_play(struct av7110 *av7110, int av) int av7110_av_stop(struct av7110 *av7110, int av) { int ret = 0; + dprintk(2, "av7110:%p, \n", av7110); if (!(av7110->playing & av) && !(av7110->rec_mode & av)) @@ -217,7 +217,6 @@ int av7110_av_stop(struct av7110 *av7110, int av) return ret; } - int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen) { int len; @@ -263,7 +262,6 @@ int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen) return blen; } - int av7110_set_volume(struct av7110 *av7110, unsigned int volleft, unsigned int volright) { @@ -320,6 +318,7 @@ int av7110_set_volume(struct av7110 *av7110, unsigned int volleft, int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode) { int ret; + dprintk(2, "av7110:%p, \n", av7110); ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, LoadVidCode, 1, mode); @@ -335,7 +334,6 @@ int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode) return ret; } - static enum av7110_video_mode sw2mode[16] = { AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_NTSC, AV7110_VIDEO_MODE_NTSC, AV7110_VIDEO_MODE_PAL, @@ -377,7 +375,6 @@ static int get_video_format(struct av7110 *av7110, u8 *buf, int count) return ret; } - /**************************************************************************** * I/O buffer management and control ****************************************************************************/ @@ -408,6 +405,7 @@ static inline long aux_ring_buffer_write(struct dvb_ringbuffer *rbuf, static void play_video_cb(u8 *buf, int count, void *priv) { struct av7110 *av7110 = (struct av7110 *) priv; + dprintk(2, "av7110:%p, \n", av7110); if ((buf[3] & 0xe0) == 0xe0) { @@ -420,12 +418,12 @@ static void play_video_cb(u8 *buf, int count, void *priv) static void play_audio_cb(u8 *buf, int count, void *priv) { struct av7110 *av7110 = (struct av7110 *) priv; + dprintk(2, "av7110:%p, \n", av7110); aux_ring_buffer_write(&av7110->aout, buf, count); } - #define FREE_COND_TS (dvb_ringbuffer_free(rb) >= 4096) static ssize_t ts_play(struct av7110 *av7110, const char __user *buf, @@ -463,7 +461,6 @@ static ssize_t ts_play(struct av7110 *av7110, const char __user *buf, return count - todo; } - #define FREE_COND (dvb_ringbuffer_free(&av7110->avout) >= 20 * 1024 && \ dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024) @@ -471,6 +468,7 @@ static ssize_t dvb_play(struct av7110 *av7110, const char __user *buf, unsigned long count, int nonblock, int type) { unsigned long todo = count, n; + dprintk(2, "av7110:%p, \n", av7110); if (!av7110->kbuf[type]) @@ -504,6 +502,7 @@ static ssize_t dvb_play_kernel(struct av7110 *av7110, const u8 *buf, unsigned long count, int nonblock, int type) { unsigned long todo = count, n; + dprintk(2, "av7110:%p, \n", av7110); if (!av7110->kbuf[type]) @@ -534,6 +533,7 @@ static ssize_t dvb_aplay(struct av7110 *av7110, const char __user *buf, unsigned long count, int nonblock, int type) { unsigned long todo = count, n; + dprintk(2, "av7110:%p, \n", av7110); if (!av7110->kbuf[type]) @@ -580,7 +580,6 @@ static void clear_p2t(struct av7110_p2t *p) p->frags = 0; } - static int find_pes_header(u8 const *buf, long int length, int *frags) { int c = 0; @@ -723,7 +722,6 @@ void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t } } - static int write_ts_header2(u16 pid, u8 *counter, int pes_start, u8 *buf, u8 length) { int i; @@ -758,7 +756,6 @@ static int write_ts_header2(u16 pid, u8 *counter, int pes_start, u8 *buf, u8 len return c; } - static void p_to_t(u8 const *buf, long int length, u16 pid, u8 *counter, struct dvb_demux_feed *feed) { @@ -806,7 +803,6 @@ static void p_to_t(u8 const *buf, long int length, u16 pid, u8 *counter, } } - static int write_ts_to_decoder(struct av7110 *av7110, int type, const u8 *buf, size_t len) { struct ipack *ipack = &av7110->ipack[type]; @@ -833,7 +829,6 @@ static int write_ts_to_decoder(struct av7110 *av7110, int type, const u8 *buf, s return 0; } - int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len) { struct dvb_demux *demux = feed->demux; @@ -860,8 +855,6 @@ int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t l return write_ts_to_decoder(av7110, feed->pes_type, buf, len); } - - /****************************************************************************** * Video MPEG decoder events ******************************************************************************/ @@ -887,7 +880,6 @@ void dvb_video_add_event(struct av7110 *av7110, struct video_event *event) wake_up_interruptible(&events->wait_queue); } - static int dvb_video_get_event (struct av7110 *av7110, struct video_event *event, int flags) { struct dvb_video_events *events = &av7110->video_events; @@ -1039,6 +1031,7 @@ static int play_iframe(struct av7110 *av7110, char __user *buf, unsigned int len /* search in buf for instances of 00 00 01 b5 1? */ for (i = 0; i < len; i++) { unsigned char c; + if (get_user(c, buf + i)) return -EFAULT; if (match == 5) { @@ -1086,6 +1079,7 @@ struct compat_video_still_picture { compat_uptr_t iFrame; int32_t size; }; + #define VIDEO_STILLPICTURE32 _IOW('o', 30, struct compat_video_still_picture) struct compat_video_event { @@ -1098,6 +1092,7 @@ struct compat_video_event { unsigned char vsync_field; /* unknown/odd/even/progressive */ } u; }; + #define VIDEO_GET_EVENT32 _IOR('o', 28, struct compat_video_event) static int dvb_compat_video_get_event(struct av7110 *av7110, @@ -1223,6 +1218,7 @@ static int dvb_video_ioctl(struct file *file, case VIDEO_SET_DISPLAY_FORMAT: { video_displayformat_t format = (video_displayformat_t) arg; + switch (format) { case VIDEO_PAN_SCAN: av7110->display_panscan = VID_PAN_SCAN_PREF; @@ -1483,6 +1479,7 @@ static int dvb_audio_ioctl(struct file *file, case AUDIO_SET_MIXER: { struct audio_mixer *amix = (struct audio_mixer *)parg; + ret = av7110_set_volume(av7110, amix->volume_left, amix->volume_right); break; } @@ -1498,7 +1495,6 @@ static int dvb_audio_ioctl(struct file *file, return ret; } - static int dvb_video_open(struct inode *inode, struct file *file) { struct dvb_device *dvbdev = file->private_data; @@ -1564,8 +1560,6 @@ static int dvb_audio_release(struct inode *inode, struct file *file) return dvb_generic_release(inode, file); } - - /****************************************************************************** * driver registration ******************************************************************************/ @@ -1609,7 +1603,6 @@ static struct dvb_device dvbdev_audio = { .kernel_ioctl = dvb_audio_ioctl, }; - int av7110_av_register(struct av7110 *av7110) { av7110->audiostate.AV_sync_state = 0; diff --git a/drivers/staging/media/av7110/av7110_av.h b/drivers/staging/media/av7110/av7110_av.h index 71bbd4391f57..56b0e14bba82 100644 --- a/drivers/staging/media/av7110/av7110_av.h +++ b/drivers/staging/media/av7110/av7110_av.h @@ -28,5 +28,4 @@ extern void av7110_av_unregister(struct av7110 *av7110); extern int av7110_av_init(struct av7110 *av7110); extern void av7110_av_exit(struct av7110 *av7110); - #endif /* _AV7110_AV_H_ */ diff --git a/drivers/staging/media/av7110/av7110_ca.c b/drivers/staging/media/av7110/av7110_ca.c index 9ff5bd6f5f27..38a8f3f49a56 100644 --- a/drivers/staging/media/av7110/av7110_ca.c +++ b/drivers/staging/media/av7110/av7110_ca.c @@ -23,7 +23,6 @@ #include "av7110_hw.h" #include "av7110_ca.h" - void CI_handle(struct av7110 *av7110, u8 *data, u16 len) { dprintk(8, "av7110:%p\n",av7110); @@ -54,7 +53,6 @@ void CI_handle(struct av7110 *av7110, u8 *data, u16 len) } } - void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len) { if (dvb_ringbuffer_free(cibuf) < len + 2) @@ -66,7 +64,6 @@ void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len) wake_up_interruptible(&cibuf->queue); } - /****************************************************************************** * CI link layer file ops ******************************************************************************/ @@ -357,7 +354,6 @@ static struct dvb_device dvbdev_ca = { .kernel_ioctl = dvb_ca_ioctl, }; - int av7110_ca_register(struct av7110 *av7110) { return dvb_register_device(&av7110->dvb_adapter, &av7110->ca_dev, diff --git a/drivers/staging/media/av7110/av7110_hw.c b/drivers/staging/media/av7110/av7110_hw.c index 714a4438bd89..d75c8947dfe7 100644 --- a/drivers/staging/media/av7110/av7110_hw.c +++ b/drivers/staging/media/av7110/av7110_hw.c @@ -93,8 +93,6 @@ u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, unsigned int co return result; } - - /* av7110 ARM core boot stuff */ #if 0 void av7110_reset_arm(struct av7110 *av7110) @@ -188,7 +186,6 @@ static int load_dram(struct av7110 *av7110, u32 *data, int len) return 0; } - /* we cannot write av7110 DRAM directly, so load a bootloader into * the DPRAM which implements a simple boot protocol */ int av7110_bootarm(struct av7110 *av7110) @@ -609,13 +606,13 @@ int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, static int av7110_fw_query(struct av7110 *av7110, u16 tag, u16 *buf, s16 length) { int ret; + ret = av7110_fw_request(av7110, &tag, 0, buf, length); if (ret) printk(KERN_ERR "dvb-ttpci: av7110_fw_query error %d\n", ret); return ret; } - /**************************************************************************** * Firmware commands ****************************************************************************/ @@ -655,7 +652,6 @@ int av7110_firmversion(struct av7110 *av7110) return 0; } - int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst) { int i, ret; @@ -684,7 +680,6 @@ int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long bu return ret; } - #ifdef CONFIG_DVB_AV7110_OSD static inline int SetColorBlend(struct av7110 *av7110, u8 windownr) @@ -829,10 +824,10 @@ static inline int CreateOSDWindow(struct av7110 *av7110, u8 windownr, windownr, disptype, width, height); } - static enum av7110_osd_palette_type bpp2pal[8] = { Pal1Bit, Pal2Bit, 0, Pal4Bit, 0, 0, 0, Pal8Bit }; + static osd_raw_window_t bpp2bit[8] = { OSD_BITMAP1, OSD_BITMAP2, 0, OSD_BITMAP4, 0, 0, 0, OSD_BITMAP8 }; @@ -1087,6 +1082,7 @@ int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc) int i, len = dc->x0-dc->color+1; u8 __user *colors = (u8 __user *)dc->data; u8 r, g = 0, b = 0, blend = 0; + ret = 0; for (i = 0; i /* for memcpy() */ #include - void av7110_ipack_reset(struct ipack *p) { p->found = 0; @@ -20,7 +19,6 @@ void av7110_ipack_reset(struct ipack *p) p->count = 0; } - int av7110_ipack_init(struct ipack *p, int size, void (*func)(u8 *buf, int size, void *priv)) { @@ -35,13 +33,11 @@ int av7110_ipack_init(struct ipack *p, int size, return 0; } - void av7110_ipack_free(struct ipack *p) { vfree(p->buf); } - static void send_ipack(struct ipack *p) { int off; @@ -108,7 +104,6 @@ static void send_ipack(struct ipack *p) } } - void av7110_ipack_flush(struct ipack *p) { if (p->plength != MMAX_PLENGTH - 6 || p->found <= 6) @@ -119,7 +114,6 @@ void av7110_ipack_flush(struct ipack *p) av7110_ipack_reset(p); } - static void write_ipack(struct ipack *p, const u8 *data, int count) { u8 headr[3] = { 0x00, 0x00, 0x01 }; @@ -134,6 +128,7 @@ static void write_ipack(struct ipack *p, const u8 *data, int count) p->count += count; } else { int rest = p->size - p->count; + memcpy(p->buf+p->count, data, rest); p->count += rest; send_ipack(p); @@ -142,7 +137,6 @@ static void write_ipack(struct ipack *p, const u8 *data, int count) } } - int av7110_ipack_instant_repack (const u8 *buf, int count, struct ipack *p) { int l; @@ -284,7 +278,6 @@ int av7110_ipack_instant_repack (const u8 *buf, int count, struct ipack *p) } if (p->mpeg == 1 && p->which < 2000) { - if (p->found == 7) { p->check = p->flag1; p->hlength = 1; @@ -368,7 +361,6 @@ int av7110_ipack_instant_repack (const u8 *buf, int count, struct ipack *p) } p->which = 2000; } - } while (c < count && p->found < p->plength + 6) { @@ -382,7 +374,6 @@ int av7110_ipack_instant_repack (const u8 *buf, int count, struct ipack *p) break; } - if (p->done) { if (p->found + count - c < p->plength + 6) { p->found += count - c; diff --git a/drivers/staging/media/av7110/av7110_v4l.c b/drivers/staging/media/av7110/av7110_v4l.c index 2c4707a604cd..3bf08cbcfce7 100644 --- a/drivers/staging/media/av7110/av7110_v4l.c +++ b/drivers/staging/media/av7110/av7110_v4l.c @@ -201,8 +201,6 @@ static int stv0297_set_tv_freq(struct saa7146_dev *dev, u32 freq) return tuner_write(dev, 0x63, data); } - - static struct saa7146_standard analog_standard[]; static struct saa7146_standard dvb_standard[]; static struct saa7146_standard standard[]; @@ -341,6 +339,7 @@ static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner * struct saa7146_dev *dev = video_drvdata(file); struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; u16 fm_matrix, src; + dprintk(2, "VIDIOC_S_TUNER: %d\n", t->index); if (!av7110->analog_tuner_flags || av7110->current_input != 1) @@ -691,7 +690,6 @@ static u8 saa7113_init_regs[] = { 0xff }; - static struct saa7146_ext_vv av7110_vv_data_st; static struct saa7146_ext_vv av7110_vv_data_c; @@ -863,8 +861,6 @@ int av7110_exit_v4l(struct av7110 *av7110) return 0; } - - /* FIXME: these values are experimental values that look better than the values from the latest "official" driver -- at least for me... (MiHu) */ static struct saa7146_standard standard[] = { @@ -927,7 +923,6 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std) return 0; } - static struct saa7146_ext_vv av7110_vv_data_st = { .inputs = 1, .audios = 1, diff --git a/drivers/staging/media/av7110/dvb_filter.h b/drivers/staging/media/av7110/dvb_filter.h index 67a3c6333bca..928b3343679d 100644 --- a/drivers/staging/media/av7110/dvb_filter.h +++ b/drivers/staging/media/av7110/dvb_filter.h @@ -36,7 +36,6 @@ void dvb_filter_pes2ts_init(struct dvb_filter_pes2ts *p2ts, unsigned short pid, int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes, int len, int payload_start); - #define PROG_STREAM_MAP 0xBC #define PRIVATE_STREAM1 0xBD #define PADDING_STREAM 0xBE @@ -78,7 +77,6 @@ int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes, #define INIT_DISP_HORIZONTAL_SIZE 540 #define INIT_DISP_VERTICAL_SIZE 576 - //flags2 #define PTS_DTS_FLAGS 0xC0 #define ESCR_FLAG 0x20 @@ -119,7 +117,6 @@ int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes, #define PIECE_RATE 0x40 #define SEAM_SPLICE 0x20 - #define MAX_PLENGTH 0xFFFF #define MMAX_PLENGTH (256*MAX_PLENGTH) @@ -238,5 +235,4 @@ struct dvb_audio_info { int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr); - #endif -- cgit v1.2.3 From 83ab91364917b379c66ff1068239e1017992cf57 Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:24:48 +0200 Subject: media: av7110: coding style fixes: whitespace This patch fixes the following checkpatch warnings: ERROR:SPACING: need consistent spacing around '&' (ctx:WxO) ERROR:SPACING: need consistent spacing around '&' (ctx:WxV) ERROR:SPACING: need consistent spacing around '-' (ctx:WxV) ERROR:SPACING: space prohibited after that '~' (ctx:OxW) ERROR:SPACING: space prohibited after that open parenthesis '(' ERROR:SPACING: space prohibited before that close parenthesis ')' ERROR:SPACING: space prohibited before that ':' (ctx:WxE) ERROR:SPACING: space prohibited before that ',' (ctx:WxW) ERROR:SPACING: space required after that ',' (ctx:VxV) ERROR:SPACING: space required before that '-' (ctx:OxV) ERROR:SPACING: space required before that '~' (ctx:OxV) ERROR:SPACING: space required before the open brace '{' ERROR:SPACING: space required before the open parenthesis '(' ERROR:SPACING: spaces required around that '&&' (ctx:ExV) ERROR:SPACING: spaces required around that '!=' (ctx:VxO) ERROR:SPACING: spaces required around that '&=' (ctx:VxO) ERROR:SPACING: spaces required around that '==' (ctx:VxO) ERROR:SPACING: spaces required around that '!=' (ctx:VxV) ERROR:SPACING: spaces required around that '+=' (ctx:VxV) ERROR:SPACING: spaces required around that '-=' (ctx:VxV) ERROR:SPACING: spaces required around that '<' (ctx:VxV) ERROR:SPACING: spaces required around that '<=' (ctx:VxV) ERROR:SPACING: spaces required around that '=' (ctx:VxV) ERROR:SPACING: spaces required around that '>' (ctx:VxV) ERROR:SPACING: spaces required around that '>=' (ctx:VxV) ERROR:SPACING: spaces required around that '|=' (ctx:VxV) WARNING:LEADING_SPACE: please, no spaces at the start of a line WARNING:QUOTED_WHITESPACE_BEFORE_NEWLINE: unnecessary whitespace before a quoted newline WARNING:SPACING: space prohibited before semicolon WARNING:SPACING: space prohibited between function name and open parenthesis '(' WARNING:SUSPECT_CODE_INDENT: suspect code indent for conditional statements (16, 23) WARNING:SUSPECT_CODE_INDENT: suspect code indent for conditional statements (16, 32) WARNING:SUSPECT_CODE_INDENT: suspect code indent for conditional statements (32, 39) WARNING:SUSPECT_CODE_INDENT: suspect code indent for conditional statements (39, 47) WARNING:SUSPECT_CODE_INDENT: suspect code indent for conditional statements (47, 55) WARNING:SUSPECT_CODE_INDENT: suspect code indent for conditional statements (8, 18) WARNING:TABSTOP: Statements should start on a tabstop CHECK:SPACING: No space is necessary after a cast CHECK:SPACING: space preferred before that '|' (ctx:VxE) CHECK:SPACING: spaces preferred around that '&' (ctx:VxV) CHECK:SPACING: spaces preferred around that '*' (ctx:VxV) CHECK:SPACING: spaces preferred around that '+' (ctx:VxV) CHECK:SPACING: spaces preferred around that '-' (ctx:VxV) CHECK:SPACING: spaces preferred around that '/' (ctx:VxV) CHECK:SPACING: spaces preferred around that '<<' (ctx:VxV) CHECK:SPACING: spaces preferred around that '>>' (ctx:VxV) CHECK:SPACING: spaces preferred around that '|' (ctx:VxV) Whitespace changes only. "git diff -w" shows no changes. Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 62 ++++++++++++++--------------- drivers/staging/media/av7110/av7110_av.c | 30 +++++++------- drivers/staging/media/av7110/av7110_ca.c | 6 +-- drivers/staging/media/av7110/av7110_hw.c | 32 +++++++-------- drivers/staging/media/av7110/av7110_hw.h | 6 +-- drivers/staging/media/av7110/av7110_ipack.c | 14 +++---- drivers/staging/media/av7110/av7110_v4l.c | 8 ++-- drivers/staging/media/av7110/dvb_filter.c | 48 +++++++++++----------- drivers/staging/media/av7110/dvb_filter.h | 2 +- 9 files changed, 104 insertions(+), 104 deletions(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index b19c27f6e478..557f4235251b 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -72,11 +72,11 @@ static int full_ts; module_param_named(debug, av7110_debug, int, 0644); MODULE_PARM_DESC(debug, "debug level (bitmask, default 0)"); module_param(vidmode, int, 0444); -MODULE_PARM_DESC(vidmode,"analog video out: 0 off, 1 CVBS+RGB (default), 2 CVBS+YC, 3 YC"); +MODULE_PARM_DESC(vidmode, "analog video out: 0 off, 1 CVBS+RGB (default), 2 CVBS+YC, 3 YC"); module_param(pids_off, int, 0444); -MODULE_PARM_DESC(pids_off,"clear video/audio/PCR PID filters when demux is closed"); +MODULE_PARM_DESC(pids_off, "clear video/audio/PCR PID filters when demux is closed"); module_param(adac, int, 0444); -MODULE_PARM_DESC(adac,"audio DAC type: 0 TI, 1 CRYSTAL, 2 MSP (use if autodetection fails)"); +MODULE_PARM_DESC(adac, "audio DAC type: 0 TI, 1 CRYSTAL, 2 MSP (use if autodetection fails)"); module_param(hw_sections, int, 0444); MODULE_PARM_DESC(hw_sections, "0 use software section filter, 1 use hardware"); module_param(rgb_on, int, 0444); @@ -951,14 +951,14 @@ static int av7110_start_feed(struct dvb_demux_feed *feed) switch (demux->dmx.frontend->source) { case DMX_MEMORY_FE: if (feed->ts_type & TS_DECODER) - if (feed->pes_type < 2 && - !(demux->pids[0] & 0x8000) && - !(demux->pids[1] & 0x8000)) { - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); - ret = av7110_av_start_play(av7110,RP_AV); - if (!ret) - demux->playing = 1; + if (feed->pes_type < 2 && + !(demux->pids[0] & 0x8000) && + !(demux->pids[1] & 0x8000)) { + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); + ret = av7110_av_start_play(av7110, RP_AV); + if (!ret) + demux->playing = 1; } break; default: @@ -1029,7 +1029,7 @@ static int av7110_stop_feed(struct dvb_demux_feed *feed) } if (feed->type == DMX_TYPE_SEC) { - for (i = 0; ifilternum; i++) { + for (i = 0; i < demux->filternum; i++) { if (demux->filter[i].state == DMX_STATE_GO && demux->filter[i].filter.parent == &feed->feed.sec) { demux->filter[i].state = DMX_STATE_READY; @@ -1466,7 +1466,7 @@ static int check_firmware(struct av7110 *av7110) printk("dvb-ttpci: root file has strange size (%d). aborting.\n", len); return -EINVAL; } - if( crc != crc32_le(0, ptr, len)) { + if (crc != crc32_le(0, ptr, len)) { printk("dvb-ttpci: crc32 of root file does not match.\n"); return -EINVAL; } @@ -1553,7 +1553,7 @@ static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) + if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) return -EIO; return 0; } @@ -1819,7 +1819,7 @@ static int nexusca_stv0297_tuner_set_params(struct dvb_frontend *fe) } // wait for PLL lock - for(i = 0; i < 20; i++) { + for (i = 0; i < 20; i++) { if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); if (i2c_transfer(&av7110->i2c_adap, &readmsg, 1) == 1) @@ -2060,8 +2060,8 @@ static u8 read_pwm(struct av7110 *av7110) { u8 b = 0xff; u8 pwm; - struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 }, - { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} }; + struct i2c_msg msg[] = { { .addr = 0x50, .flags = 0, .buf = &b, .len = 1 }, + { .addr = 0x50, .flags = I2C_M_RD, .buf = &pwm, .len = 1} }; if ((i2c_transfer(&av7110->i2c_adap, msg, 2) != 2) || (pwm == 0xff)) pwm = 0x48; @@ -2074,7 +2074,7 @@ static int frontend_init(struct av7110 *av7110) int ret; if (av7110->dev->pci->subsystem_vendor == 0x110a) { - switch(av7110->dev->pci->subsystem_device) { + switch (av7110->dev->pci->subsystem_device) { case 0x0000: // Fujitsu/Siemens DVB-Cable (ves1820/Philips CD1516(??)) av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, &av7110->i2c_adap, read_pwm(av7110)); @@ -2085,7 +2085,7 @@ static int frontend_init(struct av7110 *av7110) } } else if (av7110->dev->pci->subsystem_vendor == 0x13c2) { - switch(av7110->dev->pci->subsystem_device) { + switch (av7110->dev->pci->subsystem_device) { case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X case 0x0003: // Hauppauge/TT WinTV Nexus-S Rev 2.X case 0x1002: // Hauppauge/TT WinTV DVB-S rev1.3SE @@ -2126,7 +2126,7 @@ static int frontend_init(struct av7110 *av7110) } /* Try DVB-C cards */ - switch(av7110->dev->pci->subsystem_device) { + switch (av7110->dev->pci->subsystem_device) { case 0x0000: /* Siemens DVB-C (full-length card) VES1820/Philips CD1516 */ av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, &av7110->i2c_adap, @@ -2398,14 +2398,14 @@ static int av7110_attach(struct saa7146_dev *dev, * use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled * use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called */ - saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); + saa7146_write(dev, EC1SSR, (0x03 << 2) | 3); /* set event counter 1 threshold to maximum allowed value (rEC p55) */ - saa7146_write(dev, ECT1R, 0x3fff ); + saa7146_write(dev, ECT1R, 0x3fff); #endif /* Set RPS1 Address register to point to RPS code (r108 p42) */ saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); /* Enable RPS1, (rFC p33) */ - saa7146_write(dev, MC1, (MASK_13 | MASK_29 )); + saa7146_write(dev, MC1, (MASK_13 | MASK_29)); mdelay(10); /* now send VSYNC_B to rps1 by rising GPIO3 */ @@ -2419,7 +2419,7 @@ static int av7110_attach(struct saa7146_dev *dev, printk("dvb-ttpci: BUDGET-PATCH DETECTED.\n"); } /* Disable RPS1 */ - saa7146_write(dev, MC1, ( MASK_29 )); + saa7146_write(dev, MC1, (MASK_29)); #if RPS_IRQ printk("dvb-ttpci: Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff ); #endif @@ -2533,9 +2533,9 @@ static int av7110_attach(struct saa7146_dev *dev, * use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled * use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called */ - saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); + saa7146_write(dev, EC1SSR, (0x03 << 2) | 3); /* set event counter 1 threshold to maximum allowed value (rEC p55) */ - saa7146_write(dev, ECT1R, 0x3fff ); + saa7146_write(dev, ECT1R, 0x3fff); #endif /* Setup BUDGETPATCH MAIN RPS1 "program" (p35) */ count = 0; @@ -2576,7 +2576,7 @@ static int av7110_attach(struct saa7146_dev *dev, * then RPS_THRESH1 should be set to trigger * every TS_HEIGHT (512) lines. */ - saa7146_write(dev, RPS_THRESH1, (TS_HEIGHT*1) | MASK_12 ); + saa7146_write(dev, RPS_THRESH1, (TS_HEIGHT * 1) | MASK_12); /* Enable RPS1 (rFC p33) */ saa7146_write(dev, MC1, (MASK_13 | MASK_29)); @@ -2650,7 +2650,7 @@ static int av7110_attach(struct saa7146_dev *dev, if (ret < 0) goto err_stop_arm_9; - if (FW_VERSION(av7110->arm_app)<0x2501) + if (FW_VERSION(av7110->arm_app) < 0x2501) printk(KERN_WARNING "dvb-ttpci: Warning, firmware version 0x%04x is too old. System might be unstable!\n", FW_VERSION(av7110->arm_app)); @@ -2769,7 +2769,7 @@ static int av7110_detach(struct saa7146_dev *saa) i2c_del_adapter(&av7110->i2c_adap); - dvb_unregister_adapter (&av7110->dvb_adapter); + dvb_unregister_adapter(&av7110->dvb_adapter); av7110_num--; @@ -2825,12 +2825,12 @@ static void av7110_irq(struct saa7146_dev *dev, u32 *isr) static struct saa7146_extension av7110_extension_driver; -#define MAKE_AV7110_INFO(x_var,x_name) \ +#define MAKE_AV7110_INFO(x_var, x_name) \ static struct saa7146_pci_extension_data x_var = { \ .ext_priv = x_name, \ .ext = &av7110_extension_driver } -MAKE_AV7110_INFO(tts_1_X_fsc,"Technotrend/Hauppauge WinTV DVB-S rev1.X or Fujitsu Siemens DVB-C"); +MAKE_AV7110_INFO(tts_1_X_fsc, "Technotrend/Hauppauge WinTV DVB-S rev1.X or Fujitsu Siemens DVB-C"); MAKE_AV7110_INFO(ttt_1_X, "Technotrend/Hauppauge WinTV DVB-T rev1.X"); MAKE_AV7110_INFO(ttc_1_X, "Technotrend/Hauppauge WinTV Nexus-CA rev1.X"); MAKE_AV7110_INFO(ttc_2_X, "Technotrend/Hauppauge WinTV DVB-C rev2.X"); diff --git a/drivers/staging/media/av7110/av7110_av.c b/drivers/staging/media/av7110/av7110_av.c index 96309a896bca..0b384e8325c2 100644 --- a/drivers/staging/media/av7110/av7110_av.c +++ b/drivers/staging/media/av7110/av7110_av.c @@ -238,8 +238,8 @@ int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen) sync |= DVB_RINGBUFFER_PEEK(buf, 2) << 8; sync |= DVB_RINGBUFFER_PEEK(buf, 3); - if (((sync &~ 0x0f) == 0x000001e0) || - ((sync &~ 0x1f) == 0x000001c0) || + if (((sync & ~0x0f) == 0x000001e0) || + ((sync & ~0x1f) == 0x000001c0) || (sync == 0x000001bd)) break; printk("resync\n"); @@ -296,7 +296,7 @@ int av7110_set_volume(struct av7110 *av7110, unsigned int volleft, vol = (volleft > volright) ? volleft : volright; val = (vol * 0x73 / 255) << 8; if (vol > 0) - balance = ((volright - volleft) * 127) / vol; + balance = ((volright - volleft) * 127) / vol; msp_writereg(av7110, MSP_WR_DSP, 0x0001, balance << 8); msp_writereg(av7110, MSP_WR_DSP, 0x0000, val); /* loudspeaker */ msp_writereg(av7110, MSP_WR_DSP, 0x0006, val); /* headphonesr */ @@ -362,8 +362,8 @@ static int get_video_format(struct av7110 *av7110, u8 *buf, int count) if (p[0] || p[1] || p[2] != 0x01 || p[3] != 0xb3) continue; p += 4; - hsize = ((p[1] &0xF0) >> 4) | (p[0] << 4); - vsize = ((p[1] &0x0F) << 8) | (p[2]); + hsize = ((p[1] & 0xF0) >> 4) | (p[0] << 4); + vsize = ((p[1] & 0x0F) << 8) | (p[2]); sw = (p[3] & 0x0F); ret = av7110_set_vidmode(av7110, sw2mode[sw]); if (!ret) { @@ -590,7 +590,7 @@ static int find_pes_header(u8 const *buf, long int length, int *frags) while (c < length - 3 && !found) { if (buf[c] == 0x00 && buf[c + 1] == 0x00 && buf[c + 2] == 0x01) { - switch ( buf[c + 3] ) { + switch (buf[c + 3]) { case PROG_STREAM_MAP: case PRIVATE_STREAM2: case PROG_STREAM_DIR: @@ -635,9 +635,9 @@ void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t c = 0; c2 = 0; - if (p->frags){ + if (p->frags) { check = 0; - switch(p->frags) { + switch (p->frags) { case 1: if (buf[c] == 0x00 && buf[c + 1] == 0x01) { check = 1; @@ -703,7 +703,7 @@ void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t c2 = find_pes_header(buf + c + add, length - c - add, &p->frags); if (c2 >= 0) { c2 += c + add; - if (c2 > c){ + if (c2 > c) { p_to_t(buf + c, c2 - c, pid, &p->counter, p->feed); c = c2; clear_p2t(p); @@ -787,7 +787,7 @@ static void p_to_t(u8 const *buf, long int length, u16 pid, u8 *counter, while (c < length) { memset(obuf, 0, TS_SIZE); - if (length - c >= (TS_SIZE - 4)){ + if (length - c >= (TS_SIZE - 4)) { l = write_ts_header2(pid, counter, pes_start, obuf, (TS_SIZE - 4)); memcpy(obuf + l, buf + c, TS_SIZE - l); @@ -880,7 +880,7 @@ void dvb_video_add_event(struct av7110 *av7110, struct video_event *event) wake_up_interruptible(&events->wait_queue); } -static int dvb_video_get_event (struct av7110 *av7110, struct video_event *event, int flags) +static int dvb_video_get_event(struct av7110 *av7110, struct video_event *event, int flags) { struct dvb_video_events *events = &av7110->video_events; @@ -1124,8 +1124,8 @@ static int dvb_video_ioctl(struct file *file, dprintk(1, "av7110:%p, cmd=%04x\n", av7110,cmd); if ((file->f_flags & O_ACCMODE) == O_RDONLY) { - if ( cmd != VIDEO_GET_STATUS && cmd != VIDEO_GET_EVENT && - cmd != VIDEO_GET_SIZE ) { + if (cmd != VIDEO_GET_STATUS && cmd != VIDEO_GET_EVENT && + cmd != VIDEO_GET_SIZE) { return -EPERM; } } @@ -1419,7 +1419,7 @@ static int dvb_audio_ioctl(struct file *file, case AUDIO_CHANNEL_SELECT: av7110->audiostate.channel_select = (audio_channel_select_t) arg; - switch(av7110->audiostate.channel_select) { + switch (av7110->audiostate.channel_select) { case AUDIO_STEREO: ret = audcom(av7110, AUDIO_CMD_STEREO); if (!ret) { @@ -1624,7 +1624,7 @@ int av7110_av_register(struct av7110 *av7110) spin_lock_init(&av7110->video_events.lock); av7110->video_events.eventw = av7110->video_events.eventr = 0; av7110->video_events.overflow = 0; - memset(&av7110->video_size, 0, sizeof (video_size_t)); + memset(&av7110->video_size, 0, sizeof(video_size_t)); dvb_register_device(&av7110->dvb_adapter, &av7110->video_dev, &dvbdev_video, av7110, DVB_DEVICE_VIDEO, 0); diff --git a/drivers/staging/media/av7110/av7110_ca.c b/drivers/staging/media/av7110/av7110_ca.c index 38a8f3f49a56..93b888c705f3 100644 --- a/drivers/staging/media/av7110/av7110_ca.c +++ b/drivers/staging/media/av7110/av7110_ca.c @@ -206,7 +206,7 @@ static int dvb_ca_open(struct inode *inode, struct file *file) return 0; } -static __poll_t dvb_ca_poll (struct file *file, poll_table *wait) +static __poll_t dvb_ca_poll(struct file *file, poll_table *wait) { struct dvb_device *dvbdev = file->private_data; struct av7110 *av7110 = dvbdev->priv; @@ -260,7 +260,7 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) case CA_GET_SLOT_INFO: { - struct ca_slot_info *info=(struct ca_slot_info *)parg; + struct ca_slot_info *info = (struct ca_slot_info *)parg; if (info->num < 0 || info->num > 1) { mutex_unlock(&av7110->ioctl_mutex); @@ -285,7 +285,7 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) info.num = 16; info.type = CA_ECD; - memcpy(parg, &info, sizeof (info)); + memcpy(parg, &info, sizeof(info)); break; } diff --git a/drivers/staging/media/av7110/av7110_hw.c b/drivers/staging/media/av7110/av7110_hw.c index d75c8947dfe7..d7439c522cce 100644 --- a/drivers/staging/media/av7110/av7110_hw.c +++ b/drivers/staging/media/av7110/av7110_hw.c @@ -465,7 +465,7 @@ static int av7110_send_fw_cmd(struct av7110 *av7110, u16 *buf, int length) ret = __av7110_send_fw_cmd(av7110, buf, length); mutex_unlock(&av7110->dcomlock); - if (ret && ret!=-ERESTARTSYS) + if (ret && ret != -ERESTARTSYS) printk(KERN_ERR "dvb-ttpci: %s(): av7110_send_fw_cmd error %d\n", __func__, ret); return ret; @@ -511,9 +511,9 @@ int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len) dprintk(4, "%p\n", av7110); - for(i = 0; i < len && i < 32; i++) + for (i = 0; i < len && i < 32; i++) { - if(i % 2 == 0) + if (i % 2 == 0) cmd[(i / 2) + 2] = (u16)(buf[i]) << 8; else cmd[(i / 2) + 2] |= buf[i]; @@ -675,7 +675,7 @@ int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long bu buf[i + 4] = msg[i]; ret = av7110_send_fw_cmd(av7110, buf, 18); - if (ret && ret!=-ERESTARTSYS) + if (ret && ret != -ERESTARTSYS) printk(KERN_ERR "dvb-ttpci: av7110_diseqc_send error %d\n", ret); return ret; } @@ -777,7 +777,7 @@ static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, char *buf) wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, 0, 2); ret = __av7110_send_fw_cmd(av7110, cbuf, 5); mutex_unlock(&av7110->dcomlock); - if (ret && ret!=-ERESTARTSYS) + if (ret && ret != -ERESTARTSYS) printk(KERN_ERR "dvb-ttpci: WriteText error %d\n", ret); return ret; } @@ -861,13 +861,13 @@ static inline int LoadBitmap(struct av7110 *av7110, av7110->bmp_state = BMP_LOADING; if (format == OSD_BITMAP8) { - bpp=8; delta = 1; + bpp = 8; delta = 1; } else if (format == OSD_BITMAP4) { - bpp=4; delta = 2; + bpp = 4; delta = 2; } else if (format == OSD_BITMAP2) { - bpp=2; delta = 4; + bpp = 2; delta = 4; } else if (format == OSD_BITMAP1) { - bpp=1; delta = 8; + bpp = 1; delta = 8; } else { av7110->bmp_state = BMP_NONE; return -EINVAL; @@ -927,8 +927,8 @@ static u32 RGB2YUV(u16 R, u16 G, u16 B) u16 Y, Cr, Cb; y = R * 77 + G * 150 + B * 29; /* Luma=0.299R+0.587G+0.114B 0..65535 */ - u = 2048 + B * 8 -(y >> 5); /* Cr 0..4095 */ - v = 2048 + R * 8 -(y >> 5); /* Cb 0..4095 */ + u = 2048 + B * 8 - (y >> 5); /* Cr 0..4095 */ + v = 2048 + R * 8 - (y >> 5); /* Cb 0..4095 */ Y = y / 256; Cb = u / 16; @@ -944,7 +944,7 @@ static int OSDSetColor(struct av7110 *av7110, u8 color, u8 r, u8 g, u8 b, u8 ble u16 ch, cl; u32 yuv; - yuv = blend ? RGB2YUV(r,g,b) : 0; + yuv = blend ? RGB2YUV(r, g, b) : 0; cl = (yuv & 0xffff); ch = ((yuv >> 16) & 0xffff); ret = SetColor_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]], @@ -985,7 +985,7 @@ static int OSDSetBlock(struct av7110 *av7110, int x0, int y0, { uint w, h, bpp, bpl, size, lpb, bnum, brest; int i; - int rc,release_rc; + int rc, release_rc; w = x1 - x0 + 1; h = y1 - y0 + 1; @@ -1084,14 +1084,14 @@ int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc) u8 r, g = 0, b = 0, blend = 0; ret = 0; - for (i = 0; icolor + i, r, g, b, blend); if (ret) break; @@ -1177,7 +1177,7 @@ int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc) } mutex_unlock(&av7110->osd_mutex); - if (ret==-ERESTARTSYS) + if (ret == -ERESTARTSYS) dprintk(1, "av7110_osd_cmd(%d) returns with -ERESTARTSYS\n",dc->cmd); else if (ret) dprintk(1, "av7110_osd_cmd(%d) returns with %d\n",dc->cmd,ret); diff --git a/drivers/staging/media/av7110/av7110_hw.h b/drivers/staging/media/av7110/av7110_hw.h index df13d55f7d80..3aff5db56828 100644 --- a/drivers/staging/media/av7110/av7110_hw.h +++ b/drivers/staging/media/av7110/av7110_hw.h @@ -394,8 +394,8 @@ static inline u32 irdebi(struct av7110 *av7110, u32 config, int addr, u32 val, u { u32 res; - res=av7110_debiread(av7110, config, addr, count); - if (count<=4) + res = av7110_debiread(av7110, config, addr, count); + if (count <= 4) memcpy(av7110->debi_virt, (char *) &res, count); return res; } @@ -416,7 +416,7 @@ static inline u32 rdebi(struct av7110 *av7110, u32 config, int addr, u32 val, un u32 res; spin_lock_irqsave(&av7110->debilock, flags); - res=av7110_debiread(av7110, config, addr, count); + res = av7110_debiread(av7110, config, addr, count); spin_unlock_irqrestore(&av7110->debilock, flags); return res; } diff --git a/drivers/staging/media/av7110/av7110_ipack.c b/drivers/staging/media/av7110/av7110_ipack.c index 9a2b51d54e72..fc953db36d6b 100644 --- a/drivers/staging/media/av7110/av7110_ipack.c +++ b/drivers/staging/media/av7110/av7110_ipack.c @@ -123,7 +123,7 @@ static void write_ipack(struct ipack *p, const u8 *data, int count) p->count = 6; } - if (p->count + count < p->size){ + if (p->count + count < p->size) { memcpy(p->buf+p->count, data, count); p->count += count; } else { @@ -137,7 +137,7 @@ static void write_ipack(struct ipack *p, const u8 *data, int count) } } -int av7110_ipack_instant_repack (const u8 *buf, int count, struct ipack *p) +int av7110_ipack_instant_repack(const u8 *buf, int count, struct ipack *p) { int l; int c = 0; @@ -170,10 +170,10 @@ int av7110_ipack_instant_repack (const u8 *buf, int count, struct ipack *p) case PROG_STREAM_MAP: case PRIVATE_STREAM2: case PROG_STREAM_DIR: - case ECM_STREAM : - case EMM_STREAM : - case PADDING_STREAM : - case DSM_CC_STREAM : + case ECM_STREAM: + case EMM_STREAM: + case PADDING_STREAM: + case DSM_CC_STREAM: case ISO13522_STREAM: p->done = 1; fallthrough; @@ -334,7 +334,7 @@ int av7110_ipack_instant_repack (const u8 *buf, int count, struct ipack *p) if (c == count) return count; - if (p->which > 2){ + if (p->which > 2) { if ((p->flag2 & PTS_DTS_FLAGS) == PTS_ONLY) { while (c < count && p->which < 7) { p->pts[p->which - 2] = buf[c]; diff --git a/drivers/staging/media/av7110/av7110_v4l.c b/drivers/staging/media/av7110/av7110_v4l.c index 3bf08cbcfce7..c494e940d90f 100644 --- a/drivers/staging/media/av7110/av7110_v4l.c +++ b/drivers/staging/media/av7110/av7110_v4l.c @@ -26,7 +26,7 @@ int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val) { - u8 msg[5] = { dev, reg >> 8, reg & 0xff, val >> 8 , val & 0xff }; + u8 msg[5] = { dev, reg >> 8, reg & 0xff, val >> 8, val & 0xff }; struct i2c_msg msgs = { .flags = 0, .len = 5, .buf = msg }; switch (av7110->adac_type) { @@ -53,7 +53,7 @@ static int msp_readreg(struct av7110 *av7110, u8 dev, u16 reg, u16 *val) u8 msg1[3] = { dev, reg >> 8, reg & 0xff }; u8 msg2[2]; struct i2c_msg msgs[2] = { - { .flags = 0 , .len = 3, .buf = msg1 }, + { .flags = 0, .len = 3, .buf = msg1 }, { .flags = I2C_M_RD, .len = 2, .buf = msg2 } }; @@ -132,7 +132,7 @@ static int ves1820_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data) return 0; } -static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data [4]) +static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data[4]) { struct av7110 *av7110 = dev->ext_priv; struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 }; @@ -724,7 +724,7 @@ int av7110_init_analog_module(struct av7110 *av7110) msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume msp_writereg(av7110, MSP_WR_DSP, 0x000d, 0x1900); // prescale SCART - if (i2c_writereg(av7110, 0x48, 0x01, 0x00)!=1) { + if (i2c_writereg(av7110, 0x48, 0x01, 0x00) != 1) { pr_info("saa7113 not accessible\n"); } else { u8 *i = saa7113_init_regs; diff --git a/drivers/staging/media/av7110/dvb_filter.c b/drivers/staging/media/av7110/dvb_filter.c index 8c2eca5dcdc9..4f1f92290396 100644 --- a/drivers/staging/media/av7110/dvb_filter.c +++ b/drivers/staging/media/av7110/dvb_filter.c @@ -26,10 +26,10 @@ int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int p u8 frame = 0; int fr = 0; - while ( !found && c < count){ + while (!found && c < count) { u8 *b = mbuf+c; - if ( b[0] == 0x0b && b[1] == 0x77 ) + if (b[0] == 0x0b && b[1] == 0x77) found = 1; else { c++; @@ -52,8 +52,8 @@ int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int p if (pr) printk(KERN_CONT " BRate: %d kb/s", (int) ai->bit_rate/1000); - ai->frequency = (headr[2] & 0xc0 ) >> 6; - fr = (headr[2] & 0xc0 ) >> 6; + ai->frequency = (headr[2] & 0xc0) >> 6; + fr = (headr[2] & 0xc0) >> 6; ai->frequency = freq[fr]*100; if (pr) printk(KERN_CONT " Freq: %d Hz\n", (int) ai->frequency); @@ -70,46 +70,46 @@ int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int p void dvb_filter_pes2ts_init(struct dvb_filter_pes2ts *p2ts, unsigned short pid, dvb_filter_pes2ts_cb_t *cb, void *priv) { - unsigned char *buf=p2ts->buf; - - buf[0]=0x47; - buf[1]=(pid>>8); - buf[2]=pid&0xff; - p2ts->cc=0; - p2ts->cb=cb; - p2ts->priv=priv; + unsigned char *buf = p2ts->buf; + + buf[0] = 0x47; + buf[1] = (pid >> 8); + buf[2] = pid & 0xff; + p2ts->cc = 0; + p2ts->cb = cb; + p2ts->priv = priv; } int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes, int len, int payload_start) { - unsigned char *buf=p2ts->buf; - int ret=0, rest; + unsigned char *buf = p2ts->buf; + int ret = 0, rest; //len=6+((pes[4]<<8)|pes[5]); if (payload_start) - buf[1]|=0x40; + buf[1] |= 0x40; else - buf[1]&=~0x40; - while (len>=184) { - buf[3]=0x10|((p2ts->cc++)&0x0f); + buf[1] &= ~0x40; + while (len >= 184) { + buf[3] = 0x10 | ((p2ts->cc++) & 0x0f); memcpy(buf+4, pes, 184); if ((ret=p2ts->cb(p2ts->priv, buf))) return ret; - len-=184; pes+=184; - buf[1]&=~0x40; + len -= 184; pes += 184; + buf[1] &= ~0x40; } if (!len) return 0; - buf[3]=0x30|((p2ts->cc++)&0x0f); - rest=183-len; + buf[3] = 0x30 | ((p2ts->cc++) & 0x0f); + rest = 183 - len; if (rest) { - buf[5]=0x00; + buf[5] = 0x00; if (rest-1) memset(buf+6, 0xff, rest-1); } - buf[4]=rest; + buf[4] = rest; memcpy(buf+5+rest, pes, len); return p2ts->cb(p2ts->priv, buf); } diff --git a/drivers/staging/media/av7110/dvb_filter.h b/drivers/staging/media/av7110/dvb_filter.h index 928b3343679d..43a36d653e4a 100644 --- a/drivers/staging/media/av7110/dvb_filter.h +++ b/drivers/staging/media/av7110/dvb_filter.h @@ -227,7 +227,7 @@ struct dvb_audio_info { u32 bit_rate; u32 frequency; u32 mode; - u32 mode_extension ; + u32 mode_extension; u32 emphasis; u32 framesize; u32 off; -- cgit v1.2.3 From 2ee76a601ec3246424483ccfd88d7bd0ddeea2ef Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:24:49 +0200 Subject: media: av7110: coding style fixes: newline This patch fixes the following checkpatch warnings: ERROR:ELSE_AFTER_BRACE: else should follow close brace '}' ERROR:OPEN_BRACE: open brace '{' following enum go on the same line ERROR:OPEN_BRACE: open brace '{' following struct go on the same line ERROR:OPEN_BRACE: that open brace { should be on the previous line ERROR:TRAILING_STATEMENTS: trailing statements should be on next line CHECK:LOGICAL_CONTINUATIONS: Logical continuations should be on the previous line Newline and whitespace changes only. Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 22 +++++++++---------- drivers/staging/media/av7110/av7110_av.c | 9 +++++--- drivers/staging/media/av7110/av7110_hw.c | 9 +++----- drivers/staging/media/av7110/av7110_hw.h | 13 +++++------- drivers/staging/media/av7110/av7110_ipack.c | 8 +++---- drivers/staging/media/av7110/av7110_v4l.c | 12 ++++++----- drivers/staging/media/av7110/dvb_filter.c | 33 +++++++++++++++++------------ 7 files changed, 54 insertions(+), 52 deletions(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index 557f4235251b..8a4e7bd8222e 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -161,13 +161,11 @@ static void init_av7110_av(struct av7110 *av7110) */ } else if (0 == av7110_init_analog_module(av7110)) { /* done. */ - } - else if (dev->pci->subsystem_vendor == 0x110a) { + } else if (dev->pci->subsystem_vendor == 0x110a) { printk("dvb-ttpci: DVB-C w/o analog module @ card %d detected\n", av7110->dvb_adapter.num); av7110->adac_type = DVB_ADAC_NONE; - } - else { + } else { av7110->adac_type = adac; printk("dvb-ttpci: adac type set to %d @ card %d\n", av7110->adac_type, av7110->dvb_adapter.num); @@ -504,8 +502,7 @@ static void gpioirq(struct tasklet_struct *t) event.type = VIDEO_EVENT_SIZE_CHANGED; event.u.size.w = av7110->video_size.w; event.u.size.h = av7110->video_size.h; - switch ((h_ar >> 12) & 0xf) - { + switch ((h_ar >> 12) & 0xf) { case 3: av7110->video_size.aspect_ratio = VIDEO_FORMAT_16_9; event.u.size.aspect_ratio = VIDEO_FORMAT_16_9; @@ -576,8 +573,8 @@ static void gpioirq(struct tasklet_struct *t) len = av7110_pes_play(av7110->debi_virt, &av7110->aout, 2048); spin_unlock(&av7110->aout.lock); } - if (len <= 0 && (av7110->debitype & 0x200) - &&av7110->videostate.play_state != VIDEO_FREEZED) { + if (len <= 0 && (av7110->debitype & 0x200) && + av7110->videostate.play_state != VIDEO_FREEZED) { spin_lock(&av7110->avout.lock); len = av7110_pes_play(av7110->debi_virt, &av7110->avout, 2048); spin_unlock(&av7110->avout.lock); @@ -874,8 +871,7 @@ static int dvb_feed_start_pid(struct dvb_demux_feed *dvbdmxfeed) } if (dvbdmxfeed->pes_type < 2 && npids[0]) - if (av7110->fe_synced) - { + if (av7110->fe_synced) { ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); if (ret) return ret; @@ -1823,7 +1819,8 @@ static int nexusca_stv0297_tuner_set_params(struct dvb_frontend *fe) if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); if (i2c_transfer(&av7110->i2c_adap, &readmsg, 1) == 1) - if (data[0] & 0x40) break; + if (data[0] & 0x40) + break; msleep(10); } @@ -1875,7 +1872,8 @@ static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe) if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) return -EIO; + if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) + return -EIO; return 0; } diff --git a/drivers/staging/media/av7110/av7110_av.c b/drivers/staging/media/av7110/av7110_av.c index 0b384e8325c2..56fa1f180ffe 100644 --- a/drivers/staging/media/av7110/av7110_av.c +++ b/drivers/staging/media/av7110/av7110_av.c @@ -1043,13 +1043,16 @@ static int play_iframe(struct av7110 *av7110, char __user *buf, unsigned int len continue; } switch (match++) { - case 2: if (c == 0x01) + case 2: + if (c == 0x01) continue; break; - case 3: if (c == 0xb5) + case 3: + if (c == 0xb5) continue; break; - case 4: if ((c & 0xf0) == 0x10) + case 4: + if ((c & 0xf0) == 0x10) continue; break; } diff --git a/drivers/staging/media/av7110/av7110_hw.c b/drivers/staging/media/av7110/av7110_hw.c index d7439c522cce..d3aa524f605f 100644 --- a/drivers/staging/media/av7110/av7110_hw.c +++ b/drivers/staging/media/av7110/av7110_hw.c @@ -440,8 +440,7 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16 *buf, int length) if (stat & GPMQOver) { printk(KERN_ERR "dvb-ttpci: %s(): GPMQOver\n", __func__); return -ENOSPC; - } - else if (stat & OSDQOver) { + } else if (stat & OSDQOver) { printk(KERN_ERR "dvb-ttpci: %s(): OSDQOver\n", __func__); return -ENOSPC; } @@ -511,8 +510,7 @@ int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len) dprintk(4, "%p\n", av7110); - for (i = 0; i < len && i < 32; i++) - { + for (i = 0; i < len && i < 32; i++) { if (i % 2 == 0) cmd[(i / 2) + 2] = (u16)(buf[i]) << 8; else @@ -588,8 +586,7 @@ int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, printk(KERN_ERR "%s: GPMQOver\n", __func__); mutex_unlock(&av7110->dcomlock); return -1; - } - else if (stat & OSDQOver) { + } else if (stat & OSDQOver) { printk(KERN_ERR "%s: OSDQOver\n", __func__); mutex_unlock(&av7110->dcomlock); return -1; diff --git a/drivers/staging/media/av7110/av7110_hw.h b/drivers/staging/media/av7110/av7110_hw.h index 3aff5db56828..39488c2fc0a8 100644 --- a/drivers/staging/media/av7110/av7110_hw.h +++ b/drivers/staging/media/av7110/av7110_hw.h @@ -14,15 +14,14 @@ #define ARM_WAIT_SHAKE (HZ/5) #define ARM_WAIT_OSD (HZ) -enum av7110_bootstate -{ +enum av7110_bootstate { BOOTSTATE_BUFFER_EMPTY = 0, BOOTSTATE_BUFFER_FULL = 1, BOOTSTATE_AV7110_BOOT_COMPLETE = 2 }; -enum av7110_type_rec_play_format -{ RP_None, +enum av7110_type_rec_play_format { + RP_None, AudioPES, AudioMp2, AudioPCM, @@ -30,8 +29,7 @@ enum av7110_type_rec_play_format AV_PES }; -enum av7110_osd_palette_type -{ +enum av7110_osd_palette_type { NoPalet = 0, /* No palette */ Pal1Bit = 2, /* 2 colors for 1 Bit Palette */ Pal2Bit = 4, /* 4 colors for 2 bit palette */ @@ -50,8 +48,7 @@ enum av7110_osd_palette_type #define FB_ON SAA7146_GPIO_OUTHI /* FastBlank on (RGB-Mode) */ #define FB_LOOP SAA7146_GPIO_INPUT /* FastBlank loop-through (PC graphics ???) */ -enum av7110_video_output_mode -{ +enum av7110_video_output_mode { NO_OUT = 0, /* disable analog output */ CVBS_RGB_OUT = 1, CVBS_YC_OUT = 2, diff --git a/drivers/staging/media/av7110/av7110_ipack.c b/drivers/staging/media/av7110/av7110_ipack.c index fc953db36d6b..ac00bff4462b 100644 --- a/drivers/staging/media/av7110/av7110_ipack.c +++ b/drivers/staging/media/av7110/av7110_ipack.c @@ -80,8 +80,8 @@ static void send_ipack(struct ipack *p) p->buf[7] = 0x00; p->buf[8] = 0x00; p->count = 9; - if (p->repack_subids && p->cid == PRIVATE_STREAM1 - && (streamid & 0xf8) == 0x80) { + if (p->repack_subids && p->cid == PRIVATE_STREAM1 && + (streamid & 0xf8) == 0x80) { p->count += 4; p->buf[9] = streamid; p->buf[10] = (ac3_off >> 8) & 0xff; @@ -144,8 +144,8 @@ int av7110_ipack_instant_repack(const u8 *buf, int count, struct ipack *p) while (c < count && (p->mpeg == 0 || (p->mpeg == 1 && p->found < 7) || - (p->mpeg == 2 && p->found < 9)) - && (p->found < 5 || !p->done)) { + (p->mpeg == 2 && p->found < 9)) && + (p->found < 5 || !p->done)) { switch (p->found) { case 0: case 1: diff --git a/drivers/staging/media/av7110/av7110_v4l.c b/drivers/staging/media/av7110/av7110_v4l.c index c494e940d90f..202a05561b9e 100644 --- a/drivers/staging/media/av7110/av7110_v4l.c +++ b/drivers/staging/media/av7110/av7110_v4l.c @@ -729,13 +729,16 @@ int av7110_init_analog_module(struct av7110 *av7110) } else { u8 *i = saa7113_init_regs; - if ((av7110->dev->pci->subsystem_vendor == 0x110a) && (av7110->dev->pci->subsystem_device == 0x0000)) { + if ((av7110->dev->pci->subsystem_vendor == 0x110a) && + (av7110->dev->pci->subsystem_device == 0x0000)) { /* Fujitsu/Siemens DVB-Cable */ av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; - } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x0002)) { + } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && + (av7110->dev->pci->subsystem_device == 0x0002)) { /* Hauppauge/TT DVB-C premium */ av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; - } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x000A)) { + } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && + (av7110->dev->pci->subsystem_device == 0x000A)) { /* Hauppauge/TT DVB-C premium */ av7110->analog_tuner_flags |= ANALOG_TUNER_STV0297; } @@ -912,8 +915,7 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std) if (std->id & V4L2_STD_PAL) { av7110->vidmode = AV7110_VIDEO_MODE_PAL; av7110_set_vidmode(av7110, av7110->vidmode); - } - else if (std->id & V4L2_STD_NTSC) { + } else if (std->id & V4L2_STD_NTSC) { av7110->vidmode = AV7110_VIDEO_MODE_NTSC; av7110_set_vidmode(av7110, av7110->vidmode); } diff --git a/drivers/staging/media/av7110/dvb_filter.c b/drivers/staging/media/av7110/dvb_filter.c index 4f1f92290396..4c41682ff687 100644 --- a/drivers/staging/media/av7110/dvb_filter.c +++ b/drivers/staging/media/av7110/dvb_filter.c @@ -6,17 +6,19 @@ static u32 freq[4] = {480, 441, 320, 0}; -static unsigned int ac3_bitrates[32] = - {32,40,48,56,64,80,96,112,128,160,192,224,256,320,384,448,512,576,640, - 0,0,0,0,0,0,0,0,0,0,0,0,0}; - -static u32 ac3_frames[3][32] = - {{64,80,96,112,128,160,192,224,256,320,384,448,512,640,768,896,1024, - 1152,1280,0,0,0,0,0,0,0,0,0,0,0,0,0}, - {69,87,104,121,139,174,208,243,278,348,417,487,557,696,835,975,1114, - 1253,1393,0,0,0,0,0,0,0,0,0,0,0,0,0}, - {96,120,144,168,192,240,288,336,384,480,576,672,768,960,1152,1344, - 1536,1728,1920,0,0,0,0,0,0,0,0,0,0,0,0,0}}; +static unsigned int ac3_bitrates[32] = { + 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, 512, 576, 640, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static u32 ac3_frames[3][32] = { + {64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, 512, 640, 768, 896, 1024, + 1152, 1280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {69, 87, 104, 121, 139, 174, 208, 243, 278, 348, 417, 487, 557, 696, 835, 975, 1114, + 1253, 1393, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {96, 120, 144, 168, 192, 240, 288, 336, 384, 480, 576, 672, 768, 960, 1152, 1344, + 1536, 1728, 1920, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +}; int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr) { @@ -36,12 +38,14 @@ int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int p } } - if (!found) return -1; + if (!found) + return -1; if (pr) printk(KERN_DEBUG "Audiostream: AC3"); ai->off = c; - if (c+5 >= count) return -1; + if (c + 5 >= count) + return -1; ai->layer = 0; // 0 for AC3 headr = mbuf+c+2; @@ -59,7 +63,8 @@ int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int p printk(KERN_CONT " Freq: %d Hz\n", (int) ai->frequency); ai->framesize = ac3_frames[fr][frame >> 1]; - if ((frame & 1) && (fr == 1)) ai->framesize++; + if ((frame & 1) && (fr == 1)) + ai->framesize++; ai->framesize = ai->framesize << 1; if (pr) printk(KERN_DEBUG " Framesize %d\n", (int) ai->framesize); -- cgit v1.2.3 From 8e016209e2c28d58dfaf760527502fede44e6993 Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:24:50 +0200 Subject: media: av7110: coding style fixes: whitespace (strict) This patch fixes the following checkpatch warnings: CHECK:PARENTHESIS_ALIGNMENT: Alignment should match open parenthesis CHECK:SPACING: No space is necessary after a cast CHECK:SPACING: space preferred before that '|' (ctx:VxE) CHECK:SPACING: spaces preferred around that '&' (ctx:VxV) CHECK:SPACING: spaces preferred around that '*' (ctx:VxV) CHECK:SPACING: spaces preferred around that '+' (ctx:VxV) CHECK:SPACING: spaces preferred around that '-' (ctx:VxV) CHECK:SPACING: spaces preferred around that '/' (ctx:VxV) CHECK:SPACING: spaces preferred around that '<<' (ctx:VxV) CHECK:SPACING: spaces preferred around that '>>' (ctx:VxV) CHECK:SPACING: spaces preferred around that '|' (ctx:VxV) Additional whitespace fixes found by "checkpatch.pl --strict". Whitespace changes only. "git diff -w" shows no changes. Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 60 ++++++++++++++--------------- drivers/staging/media/av7110/av7110.h | 8 ++-- drivers/staging/media/av7110/av7110_av.c | 58 ++++++++++++++-------------- drivers/staging/media/av7110/av7110_av.h | 2 +- drivers/staging/media/av7110/av7110_ca.c | 12 +++--- drivers/staging/media/av7110/av7110_hw.c | 28 +++++++------- drivers/staging/media/av7110/av7110_hw.h | 16 ++++---- drivers/staging/media/av7110/av7110_ipack.c | 8 ++-- drivers/staging/media/av7110/av7110_v4l.c | 14 +++---- drivers/staging/media/av7110/dvb_filter.c | 18 ++++----- drivers/staging/media/av7110/dvb_filter.h | 4 +- 11 files changed, 114 insertions(+), 114 deletions(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index 8a4e7bd8222e..27019e3f3a45 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -52,8 +52,8 @@ #define TS_WIDTH 376 #define TS_HEIGHT 512 -#define TS_BUFLEN (TS_WIDTH*TS_HEIGHT) -#define TS_MAX_PACKETS (TS_BUFLEN/TS_SIZE) +#define TS_BUFLEN (TS_WIDTH * TS_HEIGHT) +#define TS_MAX_PACKETS (TS_BUFLEN / TS_SIZE) int av7110_debug; @@ -122,7 +122,7 @@ static void init_av7110_av(struct av7110 *av7110) printk("dvb-ttpci:cannot set internal volume to maximum:%d\n",ret); ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, - 1, (u16) av7110->display_ar); + 1, (u16)av7110->display_ar); if (ret < 0) printk("dvb-ttpci: unable to set aspect ratio\n"); ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, @@ -234,7 +234,7 @@ static int arm_thread(void *data) for (;;) { timeout = wait_event_interruptible_timeout(av7110->arm_wait, - kthread_should_stop(), 5 * HZ); + kthread_should_stop(), 5 * HZ); if (-ERESTARTSYS == timeout || kthread_should_stop()) { /* got signal or told to quit*/ @@ -371,7 +371,7 @@ static void debiirq(struct tasklet_struct *t) switch (type & 0xff) { case DATA_TS_RECORD: dvb_dmx_swfilter_packets(&av7110->demux, - (const u8 *) av7110->debi_virt, + (const u8 *)av7110->debi_virt, av7110->debilen / 188); xfer = RX_BUFF; break; @@ -379,7 +379,7 @@ static void debiirq(struct tasklet_struct *t) case DATA_PES_RECORD: if (av7110->demux.recording) av7110_record_cb(&av7110->p2t[handle], - (u8 *) av7110->debi_virt, + (u8 *)av7110->debi_virt, av7110->debilen); xfer = RX_BUFF; break; @@ -611,11 +611,11 @@ static void gpioirq(struct tasklet_struct *t) len = 2 * 1024; iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); - memcpy(av7110->debi_virt, av7110->bmpbuf+av7110->bmpp, len); + memcpy(av7110->debi_virt, av7110->bmpbuf + av7110->bmpp, len); av7110->bmpp += len; av7110->bmplen -= len; dprintk(8, "gpio DATA_BMP_LOAD DMA len %d\n", len); - start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE+txbuf, len); + start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE + txbuf, len); spin_unlock(&av7110->debilock); return; @@ -633,7 +633,7 @@ static void gpioirq(struct tasklet_struct *t) case DATA_TS_RECORD: case DATA_PES_RECORD: dprintk(8, "DMA: TS_REC etc.\n"); - start_debi_dma(av7110, DEBI_READ, DPRAM_BASE+rxbuf, len); + start_debi_dma(av7110, DEBI_READ, DPRAM_BASE + rxbuf, len); spin_unlock(&av7110->debilock); return; @@ -675,9 +675,9 @@ static int dvb_osd_ioctl(struct file *file, dprintk(4, "%p\n", av7110); if (cmd == OSD_SEND_CMD) - return av7110_osd_cmd(av7110, (osd_cmd_t *) parg); + return av7110_osd_cmd(av7110, (osd_cmd_t *)parg); if (cmd == OSD_GET_CAPABILITY) - return av7110_osd_capability(av7110, (osd_cap_t *) parg); + return av7110_osd_capability(av7110, (osd_cap_t *)parg); return -EINVAL; } @@ -723,7 +723,7 @@ static inline int SetPIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, } int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, - u16 subpid, u16 pcrpid) + u16 subpid, u16 pcrpid) { int ret = 0; @@ -856,7 +856,7 @@ static int dvb_feed_start_pid(struct dvb_demux_feed *dvbdmxfeed) npids[0] = npids[1] = npids[2] = npids[3] = npids[4] = 0xffff; i = dvbdmxfeed->pes_type; - npids[i] = (pid[i]&0x8000) ? 0 : pid[i]; + npids[i] = (pid[i] & 0x8000) ? 0 : pid[i]; if ((i == 2) && npids[i] && (dvbdmxfeed->ts_type & TS_PACKET)) { npids[i] = 0; ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]); @@ -919,7 +919,7 @@ static int dvb_feed_stop_pid(struct dvb_demux_feed *dvbdmxfeed) case 4: if (!pids_off) return 0; - npids[i] = (pid[i]&0x8000) ? 0 : pid[i]; + npids[i] = (pid[i] & 0x8000) ? 0 : pid[i]; break; } if (!ret) @@ -1111,8 +1111,8 @@ static int dvb_get_stc(struct dmx_demux *demux, unsigned int num, dprintk(2, "fwstc = %04hx %04hx %04hx %04hx\n", fwstc[0], fwstc[1], fwstc[2], fwstc[3]); - *stc = (((uint64_t) ((fwstc[3] & 0x8000) >> 15)) << 32) | - (((uint64_t) fwstc[1]) << 16) | ((uint64_t) fwstc[0]); + *stc = (((uint64_t)((fwstc[3] & 0x8000) >> 15)) << 32) | + (((uint64_t)fwstc[1]) << 16) | ((uint64_t)fwstc[0]); *base = 1; dprintk(4, "stc = %lu\n", (unsigned long)*stc); @@ -1218,7 +1218,7 @@ static int budget_stop_feed(struct dvb_demux_feed *feed) static void vpeirq(struct tasklet_struct *t) { struct av7110 *budget = from_tasklet(budget, t, vpe_tasklet); - u8 *mem = (u8 *) (budget->grabbing); + u8 *mem = (u8 *)(budget->grabbing); u32 olddma = budget->ttbp; u32 newdma = saa7146_read(budget->dev, PCI_VDP3); struct dvb_demux *demux = budget->full_ts ? &budget->demux : &budget->demux1; @@ -1268,7 +1268,7 @@ static int av7110_register(struct av7110 *av7110) av7110->registered = 1; - dvbdemux->priv = (void *) av7110; + dvbdemux->priv = (void *)av7110; for (i = 0; i < 32; i++) av7110->handle2filter[i] = NULL; @@ -1323,7 +1323,7 @@ static int av7110_register(struct av7110 *av7110) /* initialize software demux1 without its own frontend * demux1 hardware is connected to frontend0 of demux0 */ - dvbdemux1->priv = (void *) av7110; + dvbdemux1->priv = (void *)av7110; dvbdemux1->filternum = 256; dvbdemux1->feednum = 256; @@ -1901,7 +1901,7 @@ static int av7110_fe_lock_fix(struct av7110 *av7110, enum fe_status status) if (synced) { ret = SetPIDs(av7110, av7110->pids[DMX_PES_VIDEO], - av7110->pids[DMX_PES_AUDIO], + av7110->pids[DMX_PES_AUDIO], av7110->pids[DMX_PES_TELETEXT], 0, av7110->pids[DMX_PES_PCR]); if (!ret) @@ -2075,7 +2075,7 @@ static int frontend_init(struct av7110 *av7110) switch (av7110->dev->pci->subsystem_device) { case 0x0000: // Fujitsu/Siemens DVB-Cable (ves1820/Philips CD1516(??)) av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, - &av7110->i2c_adap, read_pwm(av7110)); + &av7110->i2c_adap, read_pwm(av7110)); if (av7110->fe) { av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params; } @@ -2379,9 +2379,9 @@ static int av7110_attach(struct saa7146_dev *dev, /* RPS1 timeout disable */ saa7146_write(dev, RPS_TOV1, 0); WRITE_RPS1(CMD_PAUSE | EVT_VBI_B); - WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL >> 2)); WRITE_RPS1(GPIO3_MSK); - WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); + WRITE_RPS1(SAA7146_GPIO_OUTLO << 24); #if RPS_IRQ /* issue RPS1 interrupt to increment counter */ WRITE_RPS1(CMD_INTERRUPT); @@ -2541,9 +2541,9 @@ static int av7110_attach(struct saa7146_dev *dev, /* Wait Source Line Counter Threshold (p36) */ WRITE_RPS1(CMD_PAUSE | EVT_HS); /* Set GPIO3=1 (p42) */ - WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL >> 2)); WRITE_RPS1(GPIO3_MSK); - WRITE_RPS1(SAA7146_GPIO_OUTHI<<24); + WRITE_RPS1(SAA7146_GPIO_OUTHI << 24); #if RPS_IRQ /* issue RPS1 interrupt */ WRITE_RPS1(CMD_INTERRUPT); @@ -2551,9 +2551,9 @@ static int av7110_attach(struct saa7146_dev *dev, /* Wait reset Source Line Counter Threshold (p36) */ WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS); /* Set GPIO3=0 (p42) */ - WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL >> 2)); WRITE_RPS1(GPIO3_MSK); - WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); + WRITE_RPS1(SAA7146_GPIO_OUTLO << 24); #if RPS_IRQ /* issue RPS1 interrupt */ WRITE_RPS1(CMD_INTERRUPT); @@ -2623,7 +2623,7 @@ static int av7110_attach(struct saa7146_dev *dev, if (!av7110->debi_virt) goto err_saa71466_vfree_4; - av7110->iobuf = vmalloc(AVOUTLEN+AOUTLEN+BMPLEN+4*IPACKS); + av7110->iobuf = vmalloc(AVOUTLEN + AOUTLEN + BMPLEN + 4 * IPACKS); if (!av7110->iobuf) goto err_pci_free_5; @@ -2632,7 +2632,7 @@ static int av7110_attach(struct saa7146_dev *dev, goto err_iobuf_vfree_6; /* init BMP buffer */ - av7110->bmpbuf = av7110->iobuf+AVOUTLEN+AOUTLEN; + av7110->bmpbuf = av7110->iobuf + AVOUTLEN + AOUTLEN; init_waitqueue_head(&av7110->bmpq); ret = av7110_ca_init(av7110); @@ -2653,7 +2653,7 @@ static int av7110_attach(struct saa7146_dev *dev, "dvb-ttpci: Warning, firmware version 0x%04x is too old. System might be unstable!\n", FW_VERSION(av7110->arm_app)); - thread = kthread_run(arm_thread, (void *) av7110, "arm_mon"); + thread = kthread_run(arm_thread, (void *)av7110, "arm_mon"); if (IS_ERR(thread)) { ret = PTR_ERR(thread); goto err_stop_arm_9; diff --git a/drivers/staging/media/av7110/av7110.h b/drivers/staging/media/av7110/av7110.h index 2290300aee5e..1cfe13df67f9 100644 --- a/drivers/staging/media/av7110/av7110.h +++ b/drivers/staging/media/av7110/av7110.h @@ -119,11 +119,11 @@ struct av7110 { void *iobuf; /* memory for all buffers */ struct dvb_ringbuffer avout; /* buffer for video or A/V mux */ -#define AVOUTLEN (128*1024) +#define AVOUTLEN (128 * 1024) struct dvb_ringbuffer aout; /* buffer for audio */ -#define AOUTLEN (64*1024) +#define AOUTLEN (64 * 1024) void *bmpbuf; -#define BMPLEN (8*32768+1024) +#define BMPLEN (8 * 32768 + 1024) /* bitmap buffers and states */ @@ -281,7 +281,7 @@ struct av7110 { }; extern int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, - u16 subpid, u16 pcrpid); + u16 subpid, u16 pcrpid); void av7110_ir_handler(struct av7110 *av7110, u32 ircom); int av7110_set_ir_config(struct av7110 *av7110); diff --git a/drivers/staging/media/av7110/av7110_av.c b/drivers/staging/media/av7110/av7110_av.c index 56fa1f180ffe..e031d602843d 100644 --- a/drivers/staging/media/av7110/av7110_av.c +++ b/drivers/staging/media/av7110/av7110_av.c @@ -91,7 +91,7 @@ int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len) static int dvb_filter_pes2ts_cb(void *priv, unsigned char *data) { - struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) priv; + struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)priv; dvbdmxfeed->cb.ts(data, 188, NULL, 0, &dvbdmxfeed->feed.ts, NULL); @@ -117,7 +117,7 @@ int av7110_av_start_record(struct av7110 *av7110, int av, dvb_filter_pes2ts_init(&av7110->p2t[0], dvbdmx->pesfilter[0]->pid, dvb_filter_pes2ts_cb, - (void *) dvbdmx->pesfilter[0]); + (void *)dvbdmx->pesfilter[0]); ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); break; @@ -125,7 +125,7 @@ int av7110_av_start_record(struct av7110 *av7110, int av, dvb_filter_pes2ts_init(&av7110->p2t[1], dvbdmx->pesfilter[1]->pid, dvb_filter_pes2ts_cb, - (void *) dvbdmx->pesfilter[1]); + (void *)dvbdmx->pesfilter[1]); ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); break; @@ -133,11 +133,11 @@ int av7110_av_start_record(struct av7110 *av7110, int av, dvb_filter_pes2ts_init(&av7110->p2t[0], dvbdmx->pesfilter[0]->pid, dvb_filter_pes2ts_cb, - (void *) dvbdmx->pesfilter[0]); + (void *)dvbdmx->pesfilter[0]); dvb_filter_pes2ts_init(&av7110->p2t[1], dvbdmx->pesfilter[1]->pid, dvb_filter_pes2ts_cb, - (void *) dvbdmx->pesfilter[1]); + (void *)dvbdmx->pesfilter[1]); ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AV_PES, 0); break; } @@ -254,10 +254,10 @@ int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen) return -1; } - dvb_ringbuffer_read(buf, dest, (size_t) blen); + dvb_ringbuffer_read(buf, dest, (size_t)blen); dprintk(2, "pread=0x%08lx, pwrite=0x%08lx\n", - (unsigned long) buf->pread, (unsigned long) buf->pwrite); + (unsigned long)buf->pread, (unsigned long)buf->pwrite); wake_up(&buf->queue); return blen; } @@ -325,7 +325,7 @@ int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode) if (!ret && !av7110->playing) { ret = ChangePIDs(av7110, av7110->pids[DMX_PES_VIDEO], - av7110->pids[DMX_PES_AUDIO], + av7110->pids[DMX_PES_AUDIO], av7110->pids[DMX_PES_TELETEXT], 0, av7110->pids[DMX_PES_PCR]); if (!ret) @@ -404,7 +404,7 @@ static inline long aux_ring_buffer_write(struct dvb_ringbuffer *rbuf, static void play_video_cb(u8 *buf, int count, void *priv) { - struct av7110 *av7110 = (struct av7110 *) priv; + struct av7110 *av7110 = (struct av7110 *)priv; dprintk(2, "av7110:%p, \n", av7110); @@ -417,7 +417,7 @@ static void play_video_cb(u8 *buf, int count, void *priv) static void play_audio_cb(u8 *buf, int count, void *priv) { - struct av7110 *av7110 = (struct av7110 *) priv; + struct av7110 *av7110 = (struct av7110 *)priv; dprintk(2, "av7110:%p, \n", av7110); @@ -499,7 +499,7 @@ static ssize_t dvb_play(struct av7110 *av7110, const char __user *buf, } static ssize_t dvb_play_kernel(struct av7110 *av7110, const u8 *buf, - unsigned long count, int nonblock, int type) + unsigned long count, int nonblock, int type) { unsigned long todo = count, n; @@ -546,8 +546,8 @@ static ssize_t dvb_aplay(struct av7110 *av7110, const char __user *buf, if (nonblock) return count - todo; if (wait_event_interruptible(av7110->aout.queue, - (dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024))) - return count-todo; + (dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024))) + return count - todo; } n = todo; if (n > IPACKS * 2) @@ -688,7 +688,7 @@ void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t if (p->pos) { c2 = find_pes_header(buf + c, length - c, &p->frags); if (c2 >= 0 && c2 < (TS_SIZE - 4) - p->pos) - l = c2+c; + l = c2 + c; else l = (TS_SIZE - 4) - p->pos; memcpy(p->pes + p->pos, buf, l); @@ -765,7 +765,7 @@ static void p_to_t(u8 const *buf, long int length, u16 pid, u8 *counter, pes_start = 0; if (length > 3 && - buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x01) + buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x01) switch (buf[3]) { case PROG_STREAM_MAP: case PRIVATE_STREAM2: @@ -1121,7 +1121,7 @@ static int dvb_video_ioctl(struct file *file, { struct dvb_device *dvbdev = file->private_data; struct av7110 *av7110 = dvbdev->priv; - unsigned long arg = (unsigned long) parg; + unsigned long arg = (unsigned long)parg; int ret = 0; dprintk(1, "av7110:%p, cmd=%04x\n", av7110,cmd); @@ -1143,7 +1143,7 @@ static int dvb_video_ioctl(struct file *file, ret = av7110_av_stop(av7110, RP_VIDEO); else ret = vidcom(av7110, AV_VIDEO_CMD_STOP, - av7110->videostate.video_blank ? 0 : 1); + av7110->videostate.video_blank ? 0 : 1); if (!ret) av7110->trickmode = TRICK_NONE; break; @@ -1193,11 +1193,11 @@ static int dvb_video_ioctl(struct file *file, break; case VIDEO_SELECT_SOURCE: - av7110->videostate.stream_source = (video_stream_source_t) arg; + av7110->videostate.stream_source = (video_stream_source_t)arg; break; case VIDEO_SET_BLANK: - av7110->videostate.video_blank = (int) arg; + av7110->videostate.video_blank = (int)arg; break; case VIDEO_GET_STATUS: @@ -1220,7 +1220,7 @@ static int dvb_video_ioctl(struct file *file, case VIDEO_SET_DISPLAY_FORMAT: { - video_displayformat_t format = (video_displayformat_t) arg; + video_displayformat_t format = (video_displayformat_t)arg; switch (format) { case VIDEO_PAN_SCAN: @@ -1250,14 +1250,14 @@ static int dvb_video_ioctl(struct file *file, } av7110->display_ar = arg; ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, - 1, (u16) arg); + 1, (u16)arg); break; #ifdef CONFIG_COMPAT case VIDEO_STILLPICTURE32: { struct compat_video_still_picture *pic = - (struct compat_video_still_picture *) parg; + (struct compat_video_still_picture *)parg; av7110->videostate.stream_source = VIDEO_SOURCE_MEMORY; dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); ret = play_iframe(av7110, compat_ptr(pic->iFrame), @@ -1269,7 +1269,7 @@ static int dvb_video_ioctl(struct file *file, case VIDEO_STILLPICTURE: { struct video_still_picture *pic = - (struct video_still_picture *) parg; + (struct video_still_picture *)parg; av7110->videostate.stream_source = VIDEO_SOURCE_MEMORY; dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); ret = play_iframe(av7110, pic->iFrame, pic->size, @@ -1291,7 +1291,7 @@ static int dvb_video_ioctl(struct file *file, break; case VIDEO_SLOWMOTION: - if (av7110->playing&RP_VIDEO) { + if (av7110->playing & RP_VIDEO) { if (av7110->trickmode != TRICK_SLOW) ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0); if (!ret) @@ -1353,7 +1353,7 @@ static int dvb_audio_ioctl(struct file *file, { struct dvb_device *dvbdev = file->private_data; struct av7110 *av7110 = dvbdev->priv; - unsigned long arg = (unsigned long) parg; + unsigned long arg = (unsigned long)parg; int ret = 0; dprintk(1, "av7110:%p, cmd=%04x\n", av7110,cmd); @@ -1398,19 +1398,19 @@ static int dvb_audio_ioctl(struct file *file, break; case AUDIO_SELECT_SOURCE: - av7110->audiostate.stream_source = (audio_stream_source_t) arg; + av7110->audiostate.stream_source = (audio_stream_source_t)arg; break; case AUDIO_SET_MUTE: { ret = audcom(av7110, arg ? AUDIO_CMD_MUTE : AUDIO_CMD_UNMUTE); if (!ret) - av7110->audiostate.mute_state = (int) arg; + av7110->audiostate.mute_state = (int)arg; break; } case AUDIO_SET_AV_SYNC: - av7110->audiostate.AV_sync_state = (int) arg; + av7110->audiostate.AV_sync_state = (int)arg; ret = audcom(av7110, arg ? AUDIO_CMD_SYNC_ON : AUDIO_CMD_SYNC_OFF); break; @@ -1421,7 +1421,7 @@ static int dvb_audio_ioctl(struct file *file, break; case AUDIO_CHANNEL_SELECT: - av7110->audiostate.channel_select = (audio_channel_select_t) arg; + av7110->audiostate.channel_select = (audio_channel_select_t)arg; switch (av7110->audiostate.channel_select) { case AUDIO_STEREO: ret = audcom(av7110, AUDIO_CMD_STEREO); diff --git a/drivers/staging/media/av7110/av7110_av.h b/drivers/staging/media/av7110/av7110_av.h index 56b0e14bba82..ce0f9945dbcf 100644 --- a/drivers/staging/media/av7110/av7110_av.h +++ b/drivers/staging/media/av7110/av7110_av.h @@ -15,7 +15,7 @@ extern int av7110_set_volume(struct av7110 *av7110, unsigned int volleft, unsigned int volright); extern int av7110_av_stop(struct av7110 *av7110, int av); extern int av7110_av_start_record(struct av7110 *av7110, int av, - struct dvb_demux_feed *dvbdmxfeed); + struct dvb_demux_feed *dvbdmxfeed); extern int av7110_av_start_play(struct av7110 *av7110, int av); extern void dvb_video_add_event(struct av7110 *av7110, struct video_event *event); diff --git a/drivers/staging/media/av7110/av7110_ca.c b/drivers/staging/media/av7110/av7110_ca.c index 93b888c705f3..682204a6f31b 100644 --- a/drivers/staging/media/av7110/av7110_ca.c +++ b/drivers/staging/media/av7110/av7110_ca.c @@ -232,7 +232,7 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) { struct dvb_device *dvbdev = file->private_data; struct av7110 *av7110 = dvbdev->priv; - unsigned long arg = (unsigned long) parg; + unsigned long arg = (unsigned long)parg; int ret = 0; dprintk(8, "av7110:%p\n",av7110); @@ -298,11 +298,11 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) return -EINVAL; } av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetDescr, 5, - (descr->index<<8)|descr->parity, - (descr->cw[0]<<8)|descr->cw[1], - (descr->cw[2]<<8)|descr->cw[3], - (descr->cw[4]<<8)|descr->cw[5], - (descr->cw[6]<<8)|descr->cw[7]); + (descr->index << 8) | descr->parity, + (descr->cw[0] << 8) | descr->cw[1], + (descr->cw[2] << 8) | descr->cw[3], + (descr->cw[4] << 8) | descr->cw[5], + (descr->cw[6] << 8) | descr->cw[7]); break; } diff --git a/drivers/staging/media/av7110/av7110_hw.c b/drivers/staging/media/av7110/av7110_hw.c index d3aa524f605f..ef4090fb179d 100644 --- a/drivers/staging/media/av7110/av7110_hw.c +++ b/drivers/staging/media/av7110/av7110_hw.c @@ -410,14 +410,14 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16 *buf, int length) } for (i = 2; i < length; i++) - wdebi(av7110, DEBINOSWAP, COMMAND + 2 * i, (u32) buf[i], 2); + wdebi(av7110, DEBINOSWAP, COMMAND + 2 * i, (u32)buf[i], 2); if (length) - wdebi(av7110, DEBINOSWAP, COMMAND + 2, (u32) buf[1], 2); + wdebi(av7110, DEBINOSWAP, COMMAND + 2, (u32)buf[1], 2); else wdebi(av7110, DEBINOSWAP, COMMAND + 2, 0, 2); - wdebi(av7110, DEBINOSWAP, COMMAND, (u32) buf[0], 2); + wdebi(av7110, DEBINOSWAP, COMMAND, (u32)buf[0], 2); if (FW_VERSION(av7110->arm_app) <= 0x261f) wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0x0000, 2); @@ -685,14 +685,14 @@ static inline int SetColorBlend(struct av7110 *av7110, u8 windownr) } static inline int SetBlend_(struct av7110 *av7110, u8 windownr, - enum av7110_osd_palette_type colordepth, u16 index, u8 blending) + enum av7110_osd_palette_type colordepth, u16 index, u8 blending) { return av7110_fw_cmd(av7110, COMTYPE_OSD, SetBlend, 4, windownr, colordepth, index, blending); } static inline int SetColor_(struct av7110 *av7110, u8 windownr, - enum av7110_osd_palette_type colordepth, u16 index, u16 colorhi, u16 colorlo) + enum av7110_osd_palette_type colordepth, u16 index, u16 colorhi, u16 colorlo) { return av7110_fw_cmd(av7110, COMTYPE_OSD, SetColor, 5, windownr, colordepth, index, colorhi, colorlo); @@ -832,7 +832,7 @@ static osd_raw_window_t bpp2bit[8] = { static inline int WaitUntilBmpLoaded(struct av7110 *av7110) { int ret = wait_event_timeout(av7110->bmpq, - av7110->bmp_state != BMP_LOADING, 10*HZ); + av7110->bmp_state != BMP_LOADING, 10 * HZ); if (ret == 0) { printk("dvb-ttpci: warning: timeout waiting in LoadBitmap: %d, %d\n", ret, av7110->bmp_state); @@ -1046,7 +1046,7 @@ int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc) case OSD_Open: av7110->osdbpp[av7110->osdwin] = (dc->color - 1) & 7; ret = CreateOSDWindow(av7110, av7110->osdwin, - bpp2bit[av7110->osdbpp[av7110->osdwin]], + bpp2bit[av7110->osdbpp[av7110->osdwin]], dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1); if (ret) break; @@ -1076,7 +1076,7 @@ int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc) if (FW_VERSION(av7110->arm_app) >= 0x2618) ret = OSDSetPalette(av7110, dc->data, dc->color, dc->x0); else { - int i, len = dc->x0-dc->color+1; + int i, len = dc->x0 - dc->color + 1; u8 __user *colors = (u8 __user *)dc->data; u8 r, g = 0, b = 0, blend = 0; @@ -1097,7 +1097,7 @@ int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc) break; case OSD_SetPixel: ret = DrawLine(av7110, av7110->osdwin, - dc->x0, dc->y0, 0, 0, dc->color); + dc->x0, dc->y0, 0, 0, dc->color); break; case OSD_SetRow: dc->y1 = dc->y0; @@ -1107,15 +1107,15 @@ int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc) break; case OSD_FillRow: ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0, - dc->x1-dc->x0+1, dc->y1, dc->color); + dc->x1 - dc->x0 + 1, dc->y1, dc->color); break; case OSD_FillBlock: ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0, - dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1, dc->color); + dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1, dc->color); break; case OSD_Line: ret = DrawLine(av7110, av7110->osdwin, - dc->x0, dc->y0, dc->x1 - dc->x0, dc->y1 - dc->y0, dc->color); + dc->x0, dc->y0, dc->x1 - dc->x0, dc->y1 - dc->y0, dc->color); break; case OSD_Text: { @@ -1129,7 +1129,7 @@ int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc) if (dc->x1 > 3) dc->x1 = 3; ret = SetFont(av7110, av7110->osdwin, dc->x1, - (u16) (dc->color & 0xffff), (u16) (dc->color >> 16)); + (u16)(dc->color & 0xffff), (u16)(dc->color >> 16)); if (!ret) ret = FlushText(av7110); if (!ret) @@ -1159,7 +1159,7 @@ int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc) else av7110->osdbpp[av7110->osdwin] = 0; ret = CreateOSDWindow(av7110, av7110->osdwin, (osd_raw_window_t)dc->color, - dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1); + dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1); if (ret) break; if (!dc->data) { diff --git a/drivers/staging/media/av7110/av7110_hw.h b/drivers/staging/media/av7110/av7110_hw.h index 39488c2fc0a8..3d0afae7fd00 100644 --- a/drivers/staging/media/av7110/av7110_hw.h +++ b/drivers/staging/media/av7110/av7110_hw.h @@ -11,7 +11,7 @@ #define DEBISWAP 0x002e0000 #define ARM_WAIT_FREE (HZ) -#define ARM_WAIT_SHAKE (HZ/5) +#define ARM_WAIT_SHAKE (HZ / 5) #define ARM_WAIT_OSD (HZ) enum av7110_bootstate { @@ -311,13 +311,13 @@ enum av7110_command_type { #define DATA_BUFF0_BASE (DPRAM_BASE + 0x200) #define DATA_BUFF0_SIZE 0x0800 -#define DATA_BUFF1_BASE (DATA_BUFF0_BASE+DATA_BUFF0_SIZE) +#define DATA_BUFF1_BASE (DATA_BUFF0_BASE + DATA_BUFF0_SIZE) #define DATA_BUFF1_SIZE 0x0800 -#define DATA_BUFF2_BASE (DATA_BUFF1_BASE+DATA_BUFF1_SIZE) +#define DATA_BUFF2_BASE (DATA_BUFF1_BASE + DATA_BUFF1_SIZE) #define DATA_BUFF2_SIZE 0x0800 -#define DATA_BUFF3_BASE (DATA_BUFF2_BASE+DATA_BUFF2_SIZE) +#define DATA_BUFF3_BASE (DATA_BUFF2_BASE + DATA_BUFF2_SIZE) #define DATA_BUFF3_SIZE 0x0400 #define Reserved (DPRAM_BASE + 0x1E00) @@ -393,7 +393,7 @@ static inline u32 irdebi(struct av7110 *av7110, u32 config, int addr, u32 val, u res = av7110_debiread(av7110, config, addr, count); if (count <= 4) - memcpy(av7110->debi_virt, (char *) &res, count); + memcpy(av7110->debi_virt, (char *)&res, count); return res; } @@ -456,14 +456,14 @@ static inline int av7710_set_video_mode(struct av7110 *av7110, int mode) static inline int vidcom(struct av7110 *av7110, u32 com, u32 arg) { return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_VIDEO_COMMAND, 4, - (com>>16), (com&0xffff), - (arg>>16), (arg&0xffff)); + (com >> 16), (com & 0xffff), + (arg >> 16), (arg & 0xffff)); } static inline int audcom(struct av7110 *av7110, u32 com) { return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_AUDIO_COMMAND, 2, - (com>>16), (com&0xffff)); + (com >> 16), (com & 0xffff)); } static inline int Set22K(struct av7110 *av7110, int state) diff --git a/drivers/staging/media/av7110/av7110_ipack.c b/drivers/staging/media/av7110/av7110_ipack.c index ac00bff4462b..9fe18f4707f5 100644 --- a/drivers/staging/media/av7110/av7110_ipack.c +++ b/drivers/staging/media/av7110/av7110_ipack.c @@ -59,7 +59,7 @@ static void send_ipack(struct ipack *p) streamid = p->buf[off]; if ((streamid & 0xf8) == 0x80) { ai.off = 0; - ac3_off = ((p->buf[off + 2] << 8)| + ac3_off = ((p->buf[off + 2] << 8) | p->buf[off + 3]); if (ac3_off < p->count) f = dvb_filter_get_ac3info(p->buf + off + 3 + ac3_off, @@ -124,12 +124,12 @@ static void write_ipack(struct ipack *p, const u8 *data, int count) } if (p->count + count < p->size) { - memcpy(p->buf+p->count, data, count); + memcpy(p->buf + p->count, data, count); p->count += count; } else { int rest = p->size - p->count; - memcpy(p->buf+p->count, data, rest); + memcpy(p->buf + p->count, data, rest); p->count += rest; send_ipack(p); if (count - rest > 0) @@ -191,7 +191,7 @@ int av7110_ipack_instant_repack(const u8 *buf, int count, struct ipack *p) break; case 4: - if (count-c > 1) { + if (count - c > 1) { p->plen[0] = buf[c]; c++; p->plen[1] = buf[c]; diff --git a/drivers/staging/media/av7110/av7110_v4l.c b/drivers/staging/media/av7110/av7110_v4l.c index 202a05561b9e..3090efb00166 100644 --- a/drivers/staging/media/av7110/av7110_v4l.c +++ b/drivers/staging/media/av7110/av7110_v4l.c @@ -86,7 +86,7 @@ static struct v4l2_input inputs[4] = { .type = V4L2_INPUT_TYPE_CAMERA, .audioset = 1, .tuner = 0, /* ignored */ - .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, + .std = V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, .status = 0, .capabilities = V4L2_IN_CAP_STD, }, { @@ -95,7 +95,7 @@ static struct v4l2_input inputs[4] = { .type = V4L2_INPUT_TYPE_TUNER, .audioset = 1, .tuner = 0, - .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, + .std = V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, .status = 0, .capabilities = V4L2_IN_CAP_STD, }, { @@ -104,7 +104,7 @@ static struct v4l2_input inputs[4] = { .type = V4L2_INPUT_TYPE_CAMERA, .audioset = 0, .tuner = 0, - .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, + .std = V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, .status = 0, .capabilities = V4L2_IN_CAP_STD, }, { @@ -113,7 +113,7 @@ static struct v4l2_input inputs[4] = { .type = V4L2_INPUT_TYPE_CAMERA, .audioset = 0, .tuner = 0, - .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, + .std = V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, .status = 0, .capabilities = V4L2_IN_CAP_STD, } @@ -529,7 +529,7 @@ static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio * } static int vidioc_g_sliced_vbi_cap(struct file *file, void *fh, - struct v4l2_sliced_vbi_cap *cap) + struct v4l2_sliced_vbi_cap *cap) { struct saa7146_dev *dev = video_drvdata(file); struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; @@ -545,7 +545,7 @@ static int vidioc_g_sliced_vbi_cap(struct file *file, void *fh, } static int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh, - struct v4l2_format *f) + struct v4l2_format *f) { struct saa7146_dev *dev = video_drvdata(file); struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; @@ -584,7 +584,7 @@ static int vidioc_try_fmt_sliced_vbi_out(struct file *file, void *fh, } static int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, - struct v4l2_format *f) + struct v4l2_format *f) { struct saa7146_dev *dev = video_drvdata(file); struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; diff --git a/drivers/staging/media/av7110/dvb_filter.c b/drivers/staging/media/av7110/dvb_filter.c index 4c41682ff687..515b4288b213 100644 --- a/drivers/staging/media/av7110/dvb_filter.c +++ b/drivers/staging/media/av7110/dvb_filter.c @@ -29,7 +29,7 @@ int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int p int fr = 0; while (!found && c < count) { - u8 *b = mbuf+c; + u8 *b = mbuf + c; if (b[0] == 0x0b && b[1] == 0x77) found = 1; @@ -48,17 +48,17 @@ int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int p return -1; ai->layer = 0; // 0 for AC3 - headr = mbuf+c+2; + headr = mbuf + c + 2; - frame = (headr[2]&0x3f); - ai->bit_rate = ac3_bitrates[frame >> 1]*1000; + frame = (headr[2] & 0x3f); + ai->bit_rate = ac3_bitrates[frame >> 1] * 1000; if (pr) printk(KERN_CONT " BRate: %d kb/s", (int) ai->bit_rate/1000); ai->frequency = (headr[2] & 0xc0) >> 6; fr = (headr[2] & 0xc0) >> 6; - ai->frequency = freq[fr]*100; + ai->frequency = freq[fr] * 100; if (pr) printk(KERN_CONT " Freq: %d Hz\n", (int) ai->frequency); @@ -99,7 +99,7 @@ int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes, buf[1] &= ~0x40; while (len >= 184) { buf[3] = 0x10 | ((p2ts->cc++) & 0x0f); - memcpy(buf+4, pes, 184); + memcpy(buf + 4, pes, 184); if ((ret=p2ts->cb(p2ts->priv, buf))) return ret; len -= 184; pes += 184; @@ -111,10 +111,10 @@ int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes, rest = 183 - len; if (rest) { buf[5] = 0x00; - if (rest-1) - memset(buf+6, 0xff, rest-1); + if (rest - 1) + memset(buf + 6, 0xff, rest - 1); } buf[4] = rest; - memcpy(buf+5+rest, pes, len); + memcpy(buf + 5 + rest, pes, len); return p2ts->cb(p2ts->priv, buf); } diff --git a/drivers/staging/media/av7110/dvb_filter.h b/drivers/staging/media/av7110/dvb_filter.h index 43a36d653e4a..471b53058d7a 100644 --- a/drivers/staging/media/av7110/dvb_filter.h +++ b/drivers/staging/media/av7110/dvb_filter.h @@ -21,7 +21,7 @@ #include -typedef int (dvb_filter_pes2ts_cb_t) (void *, unsigned char *); +typedef int (dvb_filter_pes2ts_cb_t)(void *, unsigned char *); struct dvb_filter_pes2ts { unsigned char buf[188]; @@ -118,7 +118,7 @@ int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes, #define SEAM_SPLICE 0x20 #define MAX_PLENGTH 0xFFFF -#define MMAX_PLENGTH (256*MAX_PLENGTH) +#define MMAX_PLENGTH (256 * MAX_PLENGTH) #ifndef IPACKS #define IPACKS 2048 -- cgit v1.2.3 From 043dc67f27d64fb4764d24ec9f5686b00aa9a1cd Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:24:51 +0200 Subject: media: av7110: coding style fixes: comments This patch fixes the following checkpatch warnings: WARNING:BLOCK_COMMENT_STYLE: Block comments use a trailing */ on a separate line WARNING:BLOCK_COMMENT_STYLE: Block comments use * on subsequent lines WARNING:SPDX_LICENSE_TAG: Missing or malformed SPDX-License-Identifier tag in line 1 WARNING:EMBEDDED_FILENAME: It's generally not useful to have the filename in the file Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 14 ++++++++------ drivers/staging/media/av7110/av7110_av.c | 6 ++++-- drivers/staging/media/av7110/av7110_ca.c | 3 ++- drivers/staging/media/av7110/av7110_hw.c | 9 ++++++--- drivers/staging/media/av7110/av7110_hw.h | 6 ++++-- drivers/staging/media/av7110/av7110_v4l.c | 14 +++++++++----- drivers/staging/media/av7110/dvb_filter.h | 12 ++++++------ 7 files changed, 39 insertions(+), 25 deletions(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index 27019e3f3a45..49735cbb2ea4 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * driver for the SAA7146 based AV110 cards (like the Fujitsu-Siemens DVB) - * av7110.c: initialization and demux stuff + * - initialization and demux stuff * * Copyright (C) 1999-2002 Ralph Metzler * & Marcus Metzler for convergence integrated media GmbH @@ -2444,7 +2444,8 @@ static int av7110_attach(struct saa7146_dev *dev, goto err_put_firmware_1; /* the Siemens DVB needs this if you want to have the i2c chips - get recognized before the main driver is fully loaded */ + * get recognized before the main driver is fully loaded + */ saa7146_write(dev, GPIO_CTRL, 0x500000); strscpy(av7110->i2c_adap.name, pci_ext->ext_priv, @@ -2671,8 +2672,9 @@ static int av7110_attach(struct saa7146_dev *dev, init_av7110_av(av7110); /* special case DVB-C: these cards have an analog tuner - plus need some special handling, so we have separate - saa7146_ext_vv data for these... */ + * plus need some special handling, so we have separate + * saa7146_ext_vv data for these... + */ ret = av7110_init_v4l(av7110); if (ret < 0) goto err_av7110_unregister_11; @@ -2853,8 +2855,8 @@ static const struct pci_device_id pci_tbl[] = { MAKE_EXTENSION_PCI(tts_2_3, 0x13c2, 0x000e), MAKE_EXTENSION_PCI(tts_1_3se, 0x13c2, 0x1002), -/* MAKE_EXTENSION_PCI(???, 0x13c2, 0x0005), UNDEFINED CARD */ // Technisat SkyStar1 -/* MAKE_EXTENSION_PCI(???, 0x13c2, 0x0009), UNDEFINED CARD */ // TT/Hauppauge WinTV Nexus-CA v???? +// MAKE_EXTENSION_PCI(???, 0x13c2, 0x0005), UNDEFINED CARD // Technisat SkyStar1 +// MAKE_EXTENSION_PCI(???, 0x13c2, 0x0009), UNDEFINED CARD // TT/Hauppauge WinTV Nexus-CA v??? { .vendor = 0, diff --git a/drivers/staging/media/av7110/av7110_av.c b/drivers/staging/media/av7110/av7110_av.c index e031d602843d..f5200aa3dd17 100644 --- a/drivers/staging/media/av7110/av7110_av.c +++ b/drivers/staging/media/av7110/av7110_av.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * av7110_av.c: audio and video MPEG decoder stuff + * driver for the SAA7146 based AV110 cards + * - audio and video MPEG decoder stuff * * Copyright (C) 1999-2002 Ralph Metzler * & Marcus Metzler for convergence integrated media GmbH @@ -1060,7 +1061,8 @@ static int play_iframe(struct av7110 *av7110, char __user *buf, unsigned int len } /* setting n always > 1, fixes problems when playing stillframes - consisting of I- and P-Frames */ + * consisting of I- and P-Frames + */ n = MIN_IFRAME / len + 1; /* FIXME: nonblock? */ diff --git a/drivers/staging/media/av7110/av7110_ca.c b/drivers/staging/media/av7110/av7110_ca.c index 682204a6f31b..c28e41d42ba6 100644 --- a/drivers/staging/media/av7110/av7110_ca.c +++ b/drivers/staging/media/av7110/av7110_ca.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * av7110_ca.c: CA and CI stuff + * driver for the SAA7146 based AV110 cards + * - CA and CI stuff * * Copyright (C) 1999-2002 Ralph Metzler * & Marcus Metzler for convergence integrated media GmbH diff --git a/drivers/staging/media/av7110/av7110_hw.c b/drivers/staging/media/av7110/av7110_hw.c index ef4090fb179d..c7ea72128899 100644 --- a/drivers/staging/media/av7110/av7110_hw.c +++ b/drivers/staging/media/av7110/av7110_hw.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * av7110_hw.c: av7110 low level hardware access and firmware interface + * driver for the SAA7146 based AV110 cards + * - av7110 low level hardware access and firmware interface * * Copyright (C) 1999-2002 Ralph Metzler * & Marcus Metzler for convergence integrated media GmbH @@ -38,7 +39,8 @@ ****************************************************************************/ /* This DEBI code is based on the Stradis driver - by Nathan Laredo */ + * by Nathan Laredo + */ int av7110_debiwrite(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count) @@ -187,7 +189,8 @@ static int load_dram(struct av7110 *av7110, u32 *data, int len) } /* we cannot write av7110 DRAM directly, so load a bootloader into - * the DPRAM which implements a simple boot protocol */ + * the DPRAM which implements a simple boot protocol + */ int av7110_bootarm(struct av7110 *av7110) { const struct firmware *fw; diff --git a/drivers/staging/media/av7110/av7110_hw.h b/drivers/staging/media/av7110/av7110_hw.h index 3d0afae7fd00..c13b8c479eb7 100644 --- a/drivers/staging/media/av7110/av7110_hw.h +++ b/drivers/staging/media/av7110/av7110_hw.h @@ -250,7 +250,8 @@ enum av7110_command_type { #define DATA_TS_PLAY 0x13 /* ancient CI command codes, only two are actually still used - * by the link level CI firmware */ + * by the link level CI firmware + */ #define CI_CMD_ERROR 0x00 #define CI_CMD_ACK 0x01 #define CI_CMD_SYSTEM_READY 0x02 @@ -286,7 +287,8 @@ enum av7110_command_type { /* base address of the dual ported RAM which serves as communication * area between PCI bus and av7110, - * as seen by the DEBI bus of the saa7146 */ + * as seen by the DEBI bus of the saa7146 + */ #define DPRAM_BASE 0x4000 /* boot protocol area */ diff --git a/drivers/staging/media/av7110/av7110_v4l.c b/drivers/staging/media/av7110/av7110_v4l.c index 3090efb00166..633dbb697943 100644 --- a/drivers/staging/media/av7110/av7110_v4l.c +++ b/drivers/staging/media/av7110/av7110_v4l.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * av7110_v4l.c: av7110 video4linux interface for DVB and Siemens DVB-C analog module + * driver for the SAA7146 based AV110 cards + * - video4linux interface for DVB and Siemens DVB-C analog module * * Copyright (C) 1999-2002 Ralph Metzler * & Marcus Metzler for convergence integrated media GmbH @@ -153,7 +154,8 @@ static int ves1820_set_tv_freq(struct saa7146_dev *dev, u32 freq) dprintk(4, "freq: 0x%08x\n", freq); /* magic number: 614. tuning with the frequency given by v4l2 - is always off by 614*62.5 = 38375 kHz...*/ + * is always off by 614*62.5 = 38375 kHz... + */ div = freq + 614; buf[0] = (div >> 8) & 0x7f; @@ -801,8 +803,9 @@ int av7110_init_v4l(struct av7110 *av7110) int ret; /* special case DVB-C: these cards have an analog tuner - plus need some special handling, so we have separate - saa7146_ext_vv data for these... */ + * plus need some special handling, so we have separate + * saa7146_ext_vv data for these... + */ if (av7110->analog_tuner_flags) vv_data = &av7110_vv_data_c; else @@ -865,7 +868,8 @@ int av7110_exit_v4l(struct av7110 *av7110) } /* FIXME: these values are experimental values that look better than the - values from the latest "official" driver -- at least for me... (MiHu) */ + * values from the latest "official" driver -- at least for me... (MiHu) + */ static struct saa7146_standard standard[] = { { .name = "PAL", .id = V4L2_STD_PAL_BG, diff --git a/drivers/staging/media/av7110/dvb_filter.h b/drivers/staging/media/av7110/dvb_filter.h index 471b53058d7a..38b483508e07 100644 --- a/drivers/staging/media/av7110/dvb_filter.h +++ b/drivers/staging/media/av7110/dvb_filter.h @@ -1,6 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * dvb_filter.h - * * Copyright (C) 2003 Convergence GmbH * * This program is free software; you can redistribute it and/or @@ -184,10 +183,11 @@ struct mpg_picture { s8 matrix_change_flag; u8 picture_header_parameter; - /* bit 0 - 2: bwd f code - bit 3 : fpb vector - bit 4 - 6: fwd f code - bit 7 : fpf vector */ + /* bit 0 - 2: bwd f code + * bit 3 : fpb vector + * bit 4 - 6: fwd f code + * bit 7 : fpf vector + */ int mpeg1_flag; int progressive_sequence; -- cgit v1.2.3 From 28ada7d1f9fbf82d17665a68730b17af59874862 Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:24:52 +0200 Subject: media: av7110: coding style fixes: braces This patch fixes the following checkpatch warnings and errors: WARNING:BRACES: braces {} are not necessary for any arm of this statement WARNING:BRACES: braces {} are not necessary for single statement blocks CHECK:BRACES: braces {} should be used on all arms of this statement CHECK:BRACES: Unbalanced braces around else statement Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 27 ++++++++++++--------------- drivers/staging/media/av7110/av7110_av.c | 16 ++++++++++------ drivers/staging/media/av7110/av7110_hw.c | 8 ++++---- drivers/staging/media/av7110/av7110_ipack.c | 4 ++-- drivers/staging/media/av7110/av7110_v4l.c | 11 +++++++---- drivers/staging/media/av7110/dvb_filter.c | 3 +-- 6 files changed, 36 insertions(+), 33 deletions(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index 49735cbb2ea4..78d708ffc8f3 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -408,10 +408,11 @@ static void debiirq(struct tasklet_struct *t) if (data[5] > 5) flags |= CA_CI_MODULE_READY; av7110->ci_slot[data_0].flags = flags; - } else + } else { ci_get_data(&av7110->ci_rbuffer, av7110->debi_virt, av7110->debilen); + } xfer = RX_BUFF; break; } @@ -1010,10 +1011,9 @@ static int av7110_stop_feed(struct dvb_demux_feed *feed) demux->pids[feed->pes_type] |= 0x8000; demux->pesfilter[feed->pes_type] = NULL; } - if (feed->ts_type & TS_DECODER && - feed->pes_type < DMX_PES_OTHER) { + if (feed->ts_type & TS_DECODER && feed->pes_type < DMX_PES_OTHER) ret = dvb_feed_stop_pid(feed); - } else + else if ((feed->ts_type & TS_PACKET) && (demux->dmx.frontend->source != DMX_MEMORY_FE)) ret = StopHWFilter(feed->filter); @@ -1245,10 +1245,10 @@ static void vpeirq(struct tasklet_struct *t) saa7146_read(budget->dev, EC1R) & 0x3fff); #endif - if (newdma > olddma) + if (newdma > olddma) { /* no wraparound, dump olddma..newdma */ dvb_dmx_swfilter_packets(demux, mem + olddma, (newdma - olddma) / 188); - else { + } else { /* wraparound, dump olddma..buflen and 0..newdma */ dvb_dmx_swfilter_packets(demux, mem + olddma, (TS_BUFLEN - olddma) / 188); dvb_dmx_swfilter_packets(demux, mem, newdma / 188); @@ -1488,9 +1488,10 @@ static int get_firmware(struct av7110 *av7110) printk(KERN_ERR "dvb-ttpci: could not load firmware, file not found: dvb-ttpci-01.fw\n"); printk(KERN_ERR "dvb-ttpci: usually this should be in /usr/lib/hotplug/firmware or /lib/firmware\n"); printk(KERN_ERR "dvb-ttpci: and can be downloaded from https://linuxtv.org/download/dvb/firmware/\n"); - } else + } else { printk(KERN_ERR "dvb-ttpci: cannot request firmware (error %i)\n", ret); + } return -EINVAL; } @@ -2076,9 +2077,8 @@ static int frontend_init(struct av7110 *av7110) case 0x0000: // Fujitsu/Siemens DVB-Cable (ves1820/Philips CD1516(??)) av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, &av7110->i2c_adap, read_pwm(av7110)); - if (av7110->fe) { + if (av7110->fe) av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params; - } break; } @@ -2129,17 +2129,15 @@ static int frontend_init(struct av7110 *av7110) /* Siemens DVB-C (full-length card) VES1820/Philips CD1516 */ av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, &av7110->i2c_adap, read_pwm(av7110)); - if (av7110->fe) { + if (av7110->fe) av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params; - } break; case 0x0003: /* Hauppauge DVB-C 2.1 VES1820/ALPS TDBE2 */ av7110->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); - if (av7110->fe) { + if (av7110->fe) av7110->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; - } break; } break; @@ -2168,9 +2166,8 @@ static int frontend_init(struct av7110 *av7110) case 0x0002: // Hauppauge/TT DVB-C premium rev2.X av7110->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); - if (av7110->fe) { + if (av7110->fe) av7110->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; - } break; case 0x0004: // Galaxis DVB-S rev1.3 diff --git a/drivers/staging/media/av7110/av7110_av.c b/drivers/staging/media/av7110/av7110_av.c index f5200aa3dd17..35af0cd6dd55 100644 --- a/drivers/staging/media/av7110/av7110_av.c +++ b/drivers/staging/media/av7110/av7110_av.c @@ -412,8 +412,9 @@ static void play_video_cb(u8 *buf, int count, void *priv) if ((buf[3] & 0xe0) == 0xe0) { get_video_format(av7110, buf, count); aux_ring_buffer_write(&av7110->avout, buf, count); - } else + } else { aux_ring_buffer_write(&av7110->aout, buf, count); + } } static void play_audio_cb(u8 *buf, int count, void *priv) @@ -610,8 +611,9 @@ static int find_pes_header(u8 const *buf, long int length, int *frags) c++; break; } - } else + } else { c++; + } } if (c == length - 3 && !found) { if (buf[length - 1] == 0x00) @@ -709,8 +711,9 @@ void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t c = c2; clear_p2t(p); add = 0; - } else + } else { add = 1; + } } else { l = length - c; rest = l % (TS_SIZE - 4); @@ -981,8 +984,10 @@ static __poll_t dvb_audio_poll(struct file *file, poll_table *wait) if (av7110->playing) { if (dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024) mask |= (EPOLLOUT | EPOLLWRNORM); - } else /* if not playing: may play if asked for */ + } else { + /* if not playing: may play if asked for */ mask = (EPOLLOUT | EPOLLWRNORM); + } return mask; } @@ -1532,9 +1537,8 @@ static int dvb_video_release(struct inode *inode, struct file *file) dprintk(2, "av7110:%p, \n", av7110); - if ((file->f_flags & O_ACCMODE) != O_RDONLY) { + if ((file->f_flags & O_ACCMODE) != O_RDONLY) av7110_av_stop(av7110, RP_VIDEO); - } return dvb_generic_release(inode, file); } diff --git a/drivers/staging/media/av7110/av7110_hw.c b/drivers/staging/media/av7110/av7110_hw.c index c7ea72128899..d775dd632d5b 100644 --- a/drivers/staging/media/av7110/av7110_hw.c +++ b/drivers/staging/media/av7110/av7110_hw.c @@ -1076,9 +1076,9 @@ int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc) ret = OSDSetColor(av7110, dc->color, dc->x0, dc->y0, dc->x1, dc->y1); break; case OSD_SetPalette: - if (FW_VERSION(av7110->arm_app) >= 0x2618) + if (FW_VERSION(av7110->arm_app) >= 0x2618) { ret = OSDSetPalette(av7110, dc->data, dc->color, dc->x0); - else { + } else { int i, len = dc->x0 - dc->color + 1; u8 __user *colors = (u8 __user *)dc->data; u8 r, g = 0, b = 0, blend = 0; @@ -1140,9 +1140,9 @@ int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc) break; } case OSD_SetWindow: - if (dc->x0 < 1 || dc->x0 > 7) + if (dc->x0 < 1 || dc->x0 > 7) { ret = -EINVAL; - else { + } else { av7110->osdwin = dc->x0; ret = 0; } diff --git a/drivers/staging/media/av7110/av7110_ipack.c b/drivers/staging/media/av7110/av7110_ipack.c index 9fe18f4707f5..c18a9f586db1 100644 --- a/drivers/staging/media/av7110/av7110_ipack.c +++ b/drivers/staging/media/av7110/av7110_ipack.c @@ -215,9 +215,9 @@ int av7110_ipack_instant_repack(const u8 *buf, int count, struct ipack *p) p->flag1 = buf[c]; c++; p->found++; - if ((p->flag1 & 0xc0) == 0x80) + if ((p->flag1 & 0xc0) == 0x80) { p->mpeg = 2; - else { + } else { p->hlength = 0; p->which = 0; p->mpeg = 1; diff --git a/drivers/staging/media/av7110/av7110_v4l.c b/drivers/staging/media/av7110/av7110_v4l.c index 633dbb697943..f19e505370dd 100644 --- a/drivers/staging/media/av7110/av7110_v4l.c +++ b/drivers/staging/media/av7110/av7110_v4l.c @@ -330,8 +330,10 @@ static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t) /* bilingual */ t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; t->audmode = V4L2_TUNER_MODE_LANG1; - } else /* mono */ + } else { + /* mono */ t->rxsubchans = V4L2_TUNER_SUB_MONO; + } return 0; } @@ -709,8 +711,9 @@ int av7110_init_analog_module(struct av7110 *av7110) pr_info("DVB-C analog module @ card %d detected, initializing MSP3415\n", av7110->dvb_adapter.num); av7110->adac_type = DVB_ADAC_MSP34x5; - } else + } else { return -ENODEV; + } msleep(100); // the probing above resets the msp... msp_readreg(av7110, MSP_RD_DSP, 0x001e, &version1); @@ -922,9 +925,9 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std) } else if (std->id & V4L2_STD_NTSC) { av7110->vidmode = AV7110_VIDEO_MODE_NTSC; av7110_set_vidmode(av7110, av7110->vidmode); - } - else + } else { return -1; + } return 0; } diff --git a/drivers/staging/media/av7110/dvb_filter.c b/drivers/staging/media/av7110/dvb_filter.c index 515b4288b213..f92faae446ac 100644 --- a/drivers/staging/media/av7110/dvb_filter.c +++ b/drivers/staging/media/av7110/dvb_filter.c @@ -33,9 +33,8 @@ int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int p if (b[0] == 0x0b && b[1] == 0x77) found = 1; - else { + else c++; - } } if (!found) -- cgit v1.2.3 From e198cabc5d49f18f17c5cc2e9e4c7203e58e2dc6 Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:24:53 +0200 Subject: media: av7110: coding style fixes: assignments This patch fixes the following checkpatch errors: ERROR:ASSIGN_IN_IF: do not use assignment in if condition CHECK:MULTIPLE_ASSIGNMENTS: multiple assignments should be avoided Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 30 ++++++++++++++++++++++------- drivers/staging/media/av7110/av7110_av.c | 18 +++++++++++------ drivers/staging/media/av7110/av7110_hw.c | 6 ++++-- drivers/staging/media/av7110/av7110_ipack.c | 3 ++- drivers/staging/media/av7110/dvb_filter.c | 3 ++- 5 files changed, 43 insertions(+), 17 deletions(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index 78d708ffc8f3..fac774dc6eab 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -709,7 +709,11 @@ static inline int SetPIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, if (vpid == 0x1fff || apid == 0x1fff || ttpid == 0x1fff || subpid == 0x1fff || pcrpid == 0x1fff) { - vpid = apid = ttpid = subpid = pcrpid = 0; + vpid = 0; + apid = 0; + ttpid = 0; + subpid = 0; + pcrpid = 0; av7110->pids[DMX_PES_VIDEO] = 0; av7110->pids[DMX_PES_AUDIO] = 0; av7110->pids[DMX_PES_TELETEXT] = 0; @@ -855,7 +859,11 @@ static int dvb_feed_start_pid(struct dvb_demux_feed *dvbdmxfeed) dprintk(4, "%p\n", av7110); - npids[0] = npids[1] = npids[2] = npids[3] = npids[4] = 0xffff; + npids[0] = 0xffff; + npids[1] = 0xffff; + npids[2] = 0xffff; + npids[3] = 0xffff; + npids[4] = 0xffff; i = dvbdmxfeed->pes_type; npids[i] = (pid[i] & 0x8000) ? 0 : pid[i]; if ((i == 2) && npids[i] && (dvbdmxfeed->ts_type & TS_PACKET)) { @@ -907,7 +915,11 @@ static int dvb_feed_stop_pid(struct dvb_demux_feed *dvbdmxfeed) if (!av7110->playing) dvbdmx->playing = 0; } - npids[0] = npids[1] = npids[2] = npids[3] = npids[4] = 0xffff; + npids[0] = 0xffff; + npids[1] = 0xffff; + npids[2] = 0xffff; + npids[3] = 0xffff; + npids[4] = 0xffff; i = dvbdmxfeed->pes_type; switch (i) { case 2: //teletext @@ -1407,10 +1419,13 @@ u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg) msgs[0].flags = 0; msgs[1].flags = I2C_M_RD; - msgs[0].addr = msgs[1].addr = id / 2; + msgs[0].addr = id / 2; + msgs[1].addr = id / 2; mm1[0] = reg; - msgs[0].len = 1; msgs[1].len = 1; - msgs[0].buf = mm1; msgs[1].buf = mm2; + msgs[0].len = 1; + msgs[1].len = 1; + msgs[0].buf = mm1; + msgs[1].buf = mm2; i2c_transfer(&av7110->i2c_adap, msgs, 2); return mm2[0]; @@ -1511,7 +1526,8 @@ static int get_firmware(struct av7110 *av7110) memcpy(av7110->bin_fw, fw->data, fw->size); av7110->size_fw = fw->size; - if ((ret = check_firmware(av7110))) + ret = check_firmware(av7110); + if (ret) vfree(av7110->bin_fw); release_firmware(fw); diff --git a/drivers/staging/media/av7110/av7110_av.c b/drivers/staging/media/av7110/av7110_av.c index 35af0cd6dd55..3e0db8e9a8c2 100644 --- a/drivers/staging/media/av7110/av7110_av.c +++ b/drivers/staging/media/av7110/av7110_av.c @@ -81,8 +81,10 @@ int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len) if (!(dvbdmxfeed->ts_type & TS_PACKET)) return 0; - if (buf[3] == 0xe0) // video PES do not have a length in TS - buf[4] = buf[5] = 0; + if (buf[3] == 0xe0) { // video PES do not have a length in TS + buf[4] = 0; + buf[5] = 0; + } if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY) return dvbdmxfeed->cb.ts(buf, len, NULL, 0, &dvbdmxfeed->feed.ts, NULL); @@ -282,7 +284,8 @@ int av7110_set_volume(struct av7110 *av7110, unsigned int volleft, volleft = 0x3f; if (volright > 0x3f) volright = 0x3f; - if ((err = SendDAC(av7110, 3, 0x80 + volleft))) + err = SendDAC(av7110, 3, 0x80 + volleft); + if (err) return err; return SendDAC(av7110, 4, volright); @@ -1513,7 +1516,8 @@ static int dvb_video_open(struct inode *inode, struct file *file) dprintk(2, "av7110:%p, \n", av7110); - if ((err = dvb_generic_open(inode, file)) < 0) + err = dvb_generic_open(inode, file); + if (err < 0) return err; if ((file->f_flags & O_ACCMODE) != O_RDONLY) { @@ -1524,7 +1528,8 @@ static int dvb_video_open(struct inode *inode, struct file *file) av7110->videostate.stream_source = VIDEO_SOURCE_DEMUX; /* empty event queue */ - av7110->video_events.eventr = av7110->video_events.eventw = 0; + av7110->video_events.eventr = 0; + av7110->video_events.eventw = 0; } return 0; @@ -1631,7 +1636,8 @@ int av7110_av_register(struct av7110 *av7110) init_waitqueue_head(&av7110->video_events.wait_queue); spin_lock_init(&av7110->video_events.lock); - av7110->video_events.eventw = av7110->video_events.eventr = 0; + av7110->video_events.eventw = 0; + av7110->video_events.eventr = 0; av7110->video_events.overflow = 0; memset(&av7110->video_size, 0, sizeof(video_size_t)); diff --git a/drivers/staging/media/av7110/av7110_hw.c b/drivers/staging/media/av7110/av7110_hw.c index d775dd632d5b..52290a50074d 100644 --- a/drivers/staging/media/av7110/av7110_hw.c +++ b/drivers/staging/media/av7110/av7110_hw.c @@ -219,7 +219,8 @@ int av7110_bootarm(struct av7110 *av7110) /* FIXME: Why does Nexus CA require 2x iwdebi for first init? */ iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4); - if ((ret=irdebi(av7110, DEBINOSWAP, DPRAM_BASE, 0, 4)) != 0x10325476) { + ret = irdebi(av7110, DEBINOSWAP, DPRAM_BASE, 0, 4); + if (ret != 0x10325476) { printk(KERN_ERR "dvb-ttpci: debi test in av7110_bootarm() failed: %08x != %08x (check your BIOS 'Plug&Play OS' settings)\n", ret, 0x10325476); return -1; @@ -547,7 +548,8 @@ int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, if (mutex_lock_interruptible(&av7110->dcomlock)) return -ERESTARTSYS; - if ((err = __av7110_send_fw_cmd(av7110, request_buf, request_buf_len)) < 0) { + err = __av7110_send_fw_cmd(av7110, request_buf, request_buf_len); + if (err < 0) { mutex_unlock(&av7110->dcomlock); printk(KERN_ERR "dvb-ttpci: av7110_fw_request error %d\n", err); return err; diff --git a/drivers/staging/media/av7110/av7110_ipack.c b/drivers/staging/media/av7110/av7110_ipack.c index c18a9f586db1..c41a233e178c 100644 --- a/drivers/staging/media/av7110/av7110_ipack.c +++ b/drivers/staging/media/av7110/av7110_ipack.c @@ -22,7 +22,8 @@ void av7110_ipack_reset(struct ipack *p) int av7110_ipack_init(struct ipack *p, int size, void (*func)(u8 *buf, int size, void *priv)) { - if (!(p->buf = vmalloc(size))) { + p->buf = vmalloc(size); + if (!p->buf) { printk(KERN_WARNING "Couldn't allocate memory for ipack\n"); return -ENOMEM; } diff --git a/drivers/staging/media/av7110/dvb_filter.c b/drivers/staging/media/av7110/dvb_filter.c index f92faae446ac..71c1d9629e8d 100644 --- a/drivers/staging/media/av7110/dvb_filter.c +++ b/drivers/staging/media/av7110/dvb_filter.c @@ -99,7 +99,8 @@ int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes, while (len >= 184) { buf[3] = 0x10 | ((p2ts->cc++) & 0x0f); memcpy(buf + 4, pes, 184); - if ((ret=p2ts->cb(p2ts->priv, buf))) + ret = p2ts->cb(p2ts->priv, buf); + if (ret) return ret; len -= 184; pes += 184; buf[1] &= ~0x40; -- cgit v1.2.3 From 3c08dc38e3cf018a74c64e75cebcc803d973196c Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:24:54 +0200 Subject: media: av7110: coding style fixes: comparsations This patch fixes the following checkpatch warnings: WARNING:CONSTANT_COMPARISON: Comparisons should place the constant on the right side of the test CHECK:COMPARISON_TO_NULL: Comparison to NULL could be written ... Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 10 +++++----- drivers/staging/media/av7110/av7110_hw.c | 2 +- drivers/staging/media/av7110/av7110_v4l.c | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index fac774dc6eab..de9476154075 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -104,7 +104,7 @@ static int av7110_num; #define FE_FUNC_OVERRIDE(fe_func, av7110_copy, av7110_func) \ {\ - if (fe_func != NULL) { \ + if (fe_func) { \ av7110_copy = fe_func; \ fe_func = av7110_func; \ } \ @@ -159,7 +159,7 @@ static void init_av7110_av(struct av7110 *av7110) /** * some special handling for the Siemens DVB-C cards... */ - } else if (0 == av7110_init_analog_module(av7110)) { + } else if (av7110_init_analog_module(av7110) == 0) { /* done. */ } else if (dev->pci->subsystem_vendor == 0x110a) { printk("dvb-ttpci: DVB-C w/o analog module @ card %d detected\n", @@ -1386,7 +1386,7 @@ static void dvb_unregister(struct av7110 *av7110) dvb_dmxdev_release(&av7110->dmxdev); dvb_dmx_release(&av7110->demux); - if (av7110->fe != NULL) { + if (av7110->fe) { dvb_unregister_frontend(av7110->fe); dvb_frontend_detach(av7110->fe); } @@ -1518,7 +1518,7 @@ static int get_firmware(struct av7110 *av7110) /* check if the firmware is available */ av7110->bin_fw = vmalloc(fw->size); - if (NULL == av7110->bin_fw) { + if (!av7110->bin_fw) { dprintk(1, "out of memory\n"); release_firmware(fw); return -ENOMEM; @@ -2233,7 +2233,7 @@ static int frontend_init(struct av7110 *av7110) av7110->fe->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; av7110->fe->tuner_priv = &av7110->i2c_adap; - if (dvb_attach(lnbp21_attach, av7110->fe, &av7110->i2c_adap, 0, 0) == NULL) { + if (!dvb_attach(lnbp21_attach, av7110->fe, &av7110->i2c_adap, 0, 0)) { printk("dvb-ttpci: LNBP21 not found!\n"); if (av7110->fe->ops.release) av7110->fe->ops.release(av7110->fe); diff --git a/drivers/staging/media/av7110/av7110_hw.c b/drivers/staging/media/av7110/av7110_hw.c index 52290a50074d..5a4de3e3e967 100644 --- a/drivers/staging/media/av7110/av7110_hw.c +++ b/drivers/staging/media/av7110/av7110_hw.c @@ -390,7 +390,7 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16 *buf, int length) break; } - if (type != NULL) { + if (type) { /* non-immediate COMMAND type */ start = jiffies; for (;;) { diff --git a/drivers/staging/media/av7110/av7110_v4l.c b/drivers/staging/media/av7110/av7110_v4l.c index f19e505370dd..0ccc34f3d19c 100644 --- a/drivers/staging/media/av7110/av7110_v4l.c +++ b/drivers/staging/media/av7110/av7110_v4l.c @@ -128,7 +128,7 @@ static int ves1820_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data) dprintk(4, "dev: %p\n", dev); - if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1)) + if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) return -1; return 0; } @@ -140,7 +140,7 @@ static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data[4]) dprintk(4, "dev: %p\n", dev); - if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1)) + if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) return -1; return 0; } @@ -221,7 +221,7 @@ static int av7110_dvb_c_switch(struct saa7146_dev *dev) dprintk(4, "%p\n", av7110); - if (0 != av7110->current_input) { + if (av7110->current_input != 0) { dprintk(1, "switching to analog TV:\n"); adswitch = 1; source = SAA7146_HPS_SOURCE_PORT_B; @@ -409,7 +409,7 @@ static int vidioc_s_frequency(struct file *file, void *fh, const struct v4l2_fre if (!av7110->analog_tuner_flags || av7110->current_input != 1) return -EINVAL; - if (V4L2_TUNER_ANALOG_TV != f->type) + if (f->type != V4L2_TUNER_ANALOG_TV) return -EINVAL; msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0xffe0); /* fast mute */ -- cgit v1.2.3 From e7b9b556aa799fd7a917801a6afc86f4317532fc Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:24:55 +0200 Subject: media: av7110: coding style fixes: sizeof This patch fixes the following checkpatch warnings: WARNING:SIZEOF_PARENTHESIS: sizeof d should be sizeof(d) WARNING:SIZEOF_PARENTHESIS: sizeof f->fmt.sliced should be sizeof(f->fmt.sliced) WARNING:CONST_STRUCT: struct dvb_frontend_ops should normally be const CHECK:ALLOC_SIZEOF_STRUCT: Prefer kzalloc(sizeof(*av7110)...) over kzalloc(sizeof(struct av7110)...) Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 2 +- drivers/staging/media/av7110/av7110_v4l.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index de9476154075..42b5d049ec12 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -2437,7 +2437,7 @@ static int av7110_attach(struct saa7146_dev *dev, } /* prepare the av7110 device struct */ - av7110 = kzalloc(sizeof(struct av7110), GFP_KERNEL); + av7110 = kzalloc(sizeof(*av7110), GFP_KERNEL); if (!av7110) { dprintk(1, "out of memory\n"); return -ENOMEM; diff --git a/drivers/staging/media/av7110/av7110_v4l.c b/drivers/staging/media/av7110/av7110_v4l.c index 0ccc34f3d19c..d13f1afec84d 100644 --- a/drivers/staging/media/av7110/av7110_v4l.c +++ b/drivers/staging/media/av7110/av7110_v4l.c @@ -557,7 +557,7 @@ static int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh, dprintk(2, "VIDIOC_G_FMT:\n"); if (FW_VERSION(av7110->arm_app) < 0x2623) return -EINVAL; - memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced); + memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced)); if (av7110->wssMode) { f->fmt.sliced.service_set = V4L2_SLICED_WSS_625; f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; @@ -618,7 +618,7 @@ static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size int rc; dprintk(2, "%s\n", __func__); - if (FW_VERSION(av7110->arm_app) < 0x2623 || !av7110->wssMode || count != sizeof d) + if (FW_VERSION(av7110->arm_app) < 0x2623 || !av7110->wssMode || count != sizeof(d)) return -EINVAL; if (copy_from_user(&d, data, count)) return -EFAULT; -- cgit v1.2.3 From a6257cc79aed1de04a6e84c789a1e8609411346b Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:24:56 +0200 Subject: media: av7110: coding style fixes: variable types This patch fixes the following checkpatch warnings: CHECK:PREFER_KERNEL_TYPES: Prefer kernel type 's32' over 'int32_t' CHECK:PREFER_KERNEL_TYPES: Prefer kernel type 'u64' over 'uint64_t' WARNING:UNNECESSARY_INT: Prefer 'long' over 'long int' as the int is unnecessary WARNING:UNSPECIFIED_INT: Prefer 'unsigned int' to bare use of 'unsigned' Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 4 ++-- drivers/staging/media/av7110/av7110.h | 2 +- drivers/staging/media/av7110/av7110_av.c | 12 ++++++------ drivers/staging/media/av7110/av7110_av.h | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index 42b5d049ec12..14c512fdfa47 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -1094,7 +1094,7 @@ static void restart_feeds(struct av7110 *av7110) } static int dvb_get_stc(struct dmx_demux *demux, unsigned int num, - uint64_t *stc, unsigned int *base) + u64 *stc, unsigned int *base) { int ret; u16 fwstc[4]; @@ -1183,7 +1183,7 @@ static int stop_ts_capture(struct av7110 *budget) static int start_ts_capture(struct av7110 *budget) { - unsigned y; + unsigned int y; dprintk(2, "budget: %p\n", budget); diff --git a/drivers/staging/media/av7110/av7110.h b/drivers/staging/media/av7110/av7110.h index 1cfe13df67f9..3f6b88f0f329 100644 --- a/drivers/staging/media/av7110/av7110.h +++ b/drivers/staging/media/av7110/av7110.h @@ -58,7 +58,7 @@ enum av7110_video_mode { struct av7110_p2t { u8 pes[TS_SIZE]; u8 counter; - long int pos; + long pos; int frags; struct dvb_demux_feed *feed; }; diff --git a/drivers/staging/media/av7110/av7110_av.c b/drivers/staging/media/av7110/av7110_av.c index 3e0db8e9a8c2..a408e10fb41a 100644 --- a/drivers/staging/media/av7110/av7110_av.c +++ b/drivers/staging/media/av7110/av7110_av.c @@ -71,7 +71,7 @@ #define PIECE_RATE 0x40 #define SEAM_SPLICE 0x20 -static void p_to_t(u8 const *buf, long int length, u16 pid, +static void p_to_t(u8 const *buf, long length, u16 pid, u8 *counter, struct dvb_demux_feed *feed); static int write_ts_to_decoder(struct av7110 *av7110, int type, const u8 *buf, size_t len); @@ -585,7 +585,7 @@ static void clear_p2t(struct av7110_p2t *p) p->frags = 0; } -static int find_pes_header(u8 const *buf, long int length, int *frags) +static int find_pes_header(u8 const *buf, long length, int *frags) { int c = 0; int found = 0; @@ -634,7 +634,7 @@ static int find_pes_header(u8 const *buf, long int length, int *frags) return c; } -void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t *p) +void av7110_p2t_write(u8 const *buf, long length, u16 pid, struct av7110_p2t *p) { int c, c2, l, add; int check, rest; @@ -763,7 +763,7 @@ static int write_ts_header2(u16 pid, u8 *counter, int pes_start, u8 *buf, u8 len return c; } -static void p_to_t(u8 const *buf, long int length, u16 pid, u8 *counter, +static void p_to_t(u8 const *buf, long length, u16 pid, u8 *counter, struct dvb_demux_feed *feed) { int l, pes_start; @@ -1023,7 +1023,7 @@ static u8 iframe_header[] = { 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x static int play_iframe(struct av7110 *av7110, char __user *buf, unsigned int len, int nonblock) { - unsigned i, n; + unsigned int i, n; int progressive = 0; int match = 0; @@ -1090,7 +1090,7 @@ static int play_iframe(struct av7110 *av7110, char __user *buf, unsigned int len #ifdef CONFIG_COMPAT struct compat_video_still_picture { compat_uptr_t iFrame; - int32_t size; + s32 size; }; #define VIDEO_STILLPICTURE32 _IOW('o', 30, struct compat_video_still_picture) diff --git a/drivers/staging/media/av7110/av7110_av.h b/drivers/staging/media/av7110/av7110_av.h index ce0f9945dbcf..f1c5c26c668b 100644 --- a/drivers/staging/media/av7110/av7110_av.h +++ b/drivers/staging/media/av7110/av7110_av.h @@ -21,7 +21,7 @@ extern int av7110_av_start_play(struct av7110 *av7110, int av); extern void dvb_video_add_event(struct av7110 *av7110, struct video_event *event); extern void av7110_p2t_init(struct av7110_p2t *p, struct dvb_demux_feed *feed); -extern void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t *p); +extern void av7110_p2t_write(u8 const *buf, long length, u16 pid, struct av7110_p2t *p); extern int av7110_av_register(struct av7110 *av7110); extern void av7110_av_unregister(struct av7110 *av7110); -- cgit v1.2.3 From 64b84024314e69b9c588e8fa38e565ac4dc3c1f8 Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:24:57 +0200 Subject: media: av7110: coding style fixes: miscellaneous This patch fixes the following checkpatch warnings: WARNING:OOM_MESSAGE: Possible unnecessary 'out of memory' message CHECK:MACRO_ARG_REUSE: Macro argument reuse 'fe_func' - possible side-effects? The MACRO_ARG_REUSE isn't really fixed but marked as intentional. The comment is visible in the checkpatch warning. Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 2 +- drivers/staging/media/av7110/av7110_ipack.c | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index 14c512fdfa47..30e2e6e65148 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -108,7 +108,7 @@ static int av7110_num; av7110_copy = fe_func; \ fe_func = av7110_func; \ } \ -} +} /* Macro argument reuse of 'fe_func' is intentional! */ static void init_av7110_av(struct av7110 *av7110) { diff --git a/drivers/staging/media/av7110/av7110_ipack.c b/drivers/staging/media/av7110/av7110_ipack.c index c41a233e178c..9631aae71fc0 100644 --- a/drivers/staging/media/av7110/av7110_ipack.c +++ b/drivers/staging/media/av7110/av7110_ipack.c @@ -23,10 +23,8 @@ int av7110_ipack_init(struct ipack *p, int size, void (*func)(u8 *buf, int size, void *priv)) { p->buf = vmalloc(size); - if (!p->buf) { - printk(KERN_WARNING "Couldn't allocate memory for ipack\n"); + if (!p->buf) return -ENOMEM; - } p->size = size; p->func = func; p->repack_subids = 0; -- cgit v1.2.3 From 908b202653cde20c4245f27a91308ce905b952a5 Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:24:58 +0200 Subject: media: av7110: coding style fixes: deep_indentation This patch fixes most the following checkpatch warnings: WARNING:DEEP_INDENTATION: Too many leading tabs - consider code refactoring Invert "if" statement to reduce indention level by 1. Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110_ipack.c | 234 ++++++++++++++-------------- 1 file changed, 117 insertions(+), 117 deletions(-) diff --git a/drivers/staging/media/av7110/av7110_ipack.c b/drivers/staging/media/av7110/av7110_ipack.c index 9631aae71fc0..4be6e225f08e 100644 --- a/drivers/staging/media/av7110/av7110_ipack.c +++ b/drivers/staging/media/av7110/av7110_ipack.c @@ -249,146 +249,146 @@ int av7110_ipack_instant_repack(const u8 *buf, int count, struct ipack *p) if (!p->plength) p->plength = MMAX_PLENGTH - 6; - if (p->done || ((p->mpeg == 2 && p->found >= 9) || - (p->mpeg == 1 && p->found >= 7))) { - switch (p->cid) { - case AUDIO_STREAM_S ... AUDIO_STREAM_E: - case VIDEO_STREAM_S ... VIDEO_STREAM_E: - case PRIVATE_STREAM1: - if (p->mpeg == 2 && p->found == 9) { - write_ipack(p, &p->flag1, 1); - write_ipack(p, &p->flag2, 1); - write_ipack(p, &p->hlength, 1); + if (!(p->done || ((p->mpeg == 2 && p->found >= 9) || + (p->mpeg == 1 && p->found >= 7)))) + return count; + + switch (p->cid) { + case AUDIO_STREAM_S ... AUDIO_STREAM_E: + case VIDEO_STREAM_S ... VIDEO_STREAM_E: + case PRIVATE_STREAM1: + if (p->mpeg == 2 && p->found == 9) { + write_ipack(p, &p->flag1, 1); + write_ipack(p, &p->flag2, 1); + write_ipack(p, &p->hlength, 1); + } + + if (p->mpeg == 1 && p->found == 7) + write_ipack(p, &p->flag1, 1); + + if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) && p->found < 14) { + while (c < count && p->found < 14) { + p->pts[p->found - 9] = buf[c]; + write_ipack(p, buf + c, 1); + c++; + p->found++; } + if (c == count) + return count; + } - if (p->mpeg == 1 && p->found == 7) - write_ipack(p, &p->flag1, 1); + if (p->mpeg == 1 && p->which < 2000) { + if (p->found == 7) { + p->check = p->flag1; + p->hlength = 1; + } - if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) && - p->found < 14) { - while (c < count && p->found < 14) { - p->pts[p->found - 9] = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - } - if (c == count) - return count; + while (!p->which && c < count && p->check == 0xff) { + p->check = buf[c]; + write_ipack(p, buf + c, 1); + c++; + p->found++; + p->hlength++; } - if (p->mpeg == 1 && p->which < 2000) { - if (p->found == 7) { - p->check = p->flag1; - p->hlength = 1; - } + if (c == count) + return count; - while (!p->which && c < count && - p->check == 0xff){ - p->check = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - p->hlength++; - } + if ((p->check & 0xc0) == 0x40 && !p->which) { + p->check = buf[c]; + write_ipack(p, buf + c, 1); + c++; + p->found++; + p->hlength++; + p->which = 1; + if (c == count) + return count; + p->check = buf[c]; + write_ipack(p, buf + c, 1); + c++; + p->found++; + p->hlength++; + p->which = 2; if (c == count) return count; + } - if ((p->check & 0xc0) == 0x40 && !p->which) { - p->check = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - p->hlength++; + if (p->which == 1) { + p->check = buf[c]; + write_ipack(p, buf + c, 1); + c++; + p->found++; + p->hlength++; + p->which = 2; + if (c == count) + return count; + } - p->which = 1; - if (c == count) - return count; - p->check = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - p->hlength++; - p->which = 2; - if (c == count) - return count; - } + if ((p->check & 0x30) && p->check != 0xff) { + p->flag2 = (p->check & 0xf0) << 2; + p->pts[0] = p->check; + p->which = 3; + } - if (p->which == 1) { - p->check = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - p->hlength++; - p->which = 2; + if (c == count) + return count; + if (p->which > 2) { + if ((p->flag2 & PTS_DTS_FLAGS) == PTS_ONLY) { + while (c < count && p->which < 7) { + p->pts[p->which - 2] = buf[c]; + write_ipack(p, buf + c, 1); + c++; + p->found++; + p->which++; + p->hlength++; + } if (c == count) return count; - } - - if ((p->check & 0x30) && p->check != 0xff) { - p->flag2 = (p->check & 0xf0) << 2; - p->pts[0] = p->check; - p->which = 3; - } - - if (c == count) - return count; - if (p->which > 2) { - if ((p->flag2 & PTS_DTS_FLAGS) == PTS_ONLY) { - while (c < count && p->which < 7) { + } else if ((p->flag2 & PTS_DTS_FLAGS) == PTS_DTS) { + while (c < count && p->which < 12) { + if (p->which < 7) p->pts[p->which - 2] = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - p->which++; - p->hlength++; - } - if (c == count) - return count; - } else if ((p->flag2 & PTS_DTS_FLAGS) == PTS_DTS) { - while (c < count && p->which < 12) { - if (p->which < 7) - p->pts[p->which - 2] = buf[c]; - write_ipack(p, buf + c, 1); - c++; - p->found++; - p->which++; - p->hlength++; - } - if (c == count) - return count; + write_ipack(p, buf + c, 1); + c++; + p->found++; + p->which++; + p->hlength++; } - p->which = 2000; + if (c == count) + return count; } + p->which = 2000; } - - while (c < count && p->found < p->plength + 6) { - l = count - c; - if (l + p->found > p->plength + 6) - l = p->plength + 6 - p->found; - write_ipack(p, buf + c, l); - p->found += l; - c += l; - } - break; } - if (p->done) { - if (p->found + count - c < p->plength + 6) { - p->found += count - c; - c = count; - } else { - c += p->plength + 6 - p->found; - p->found = p->plength + 6; - } + while (c < count && p->found < p->plength + 6) { + l = count - c; + if (l + p->found > p->plength + 6) + l = p->plength + 6 - p->found; + write_ipack(p, buf + c, l); + p->found += l; + c += l; } + break; + } - if (p->plength && p->found == p->plength + 6) { - send_ipack(p); - av7110_ipack_reset(p); - if (c < count) - av7110_ipack_instant_repack(buf + c, count - c, p); + if (p->done) { + if (p->found + count - c < p->plength + 6) { + p->found += count - c; + c = count; + } else { + c += p->plength + 6 - p->found; + p->found = p->plength + 6; } } + + if (p->plength && p->found == p->plength + 6) { + send_ipack(p); + av7110_ipack_reset(p); + if (c < count) + av7110_ipack_instant_repack(buf + c, count - c, p); + } + return count; } -- cgit v1.2.3 From f6ed8943fb061e3cb6d658555940f83bf236a81e Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:24:59 +0200 Subject: media: av7110: coding style fixes: logging This patch fixes the following checkpatch warnings: CHECK:MACRO_ARG_PRECEDENCE: Macro argument 'level' may be better as '(level)' to avoid precedence issues WARNING:EMBEDDED_FUNCTION_NAME: Prefer using '"%s...", __func__' to using 'av7110_bootarm', this function's name, in a string WARNING:EMBEDDED_FUNCTION_NAME: Prefer using '"%s...", __func__' to using 'av7110_diseqc_send', this function's name, in a string WARNING:EMBEDDED_FUNCTION_NAME: Prefer using '"%s...", __func__' to using 'av7110_fw_cmd', this function's name, in a string WARNING:EMBEDDED_FUNCTION_NAME: Prefer using '"%s...", __func__' to using 'av7110_fw_query', this function's name, in a string WARNING:EMBEDDED_FUNCTION_NAME: Prefer using '"%s...", __func__' to using 'av7110_fw_request', this function's name, in a string WARNING:EMBEDDED_FUNCTION_NAME: Prefer using '"%s...", __func__' to using 'av7110_osd_cmd', this function's name, in a string WARNING:EMBEDDED_FUNCTION_NAME: Prefer using '"%s...", __func__' to using 'av7110_send_ci_cmd', this function's name, in a string WARNING:EMBEDDED_FUNCTION_NAME: Prefer using '"%s...", __func__' to using 'av7110_send_fw_cmd', this function's name, in a string WARNING:EMBEDDED_FUNCTION_NAME: Prefer using '"%s...", __func__' to using 'gpioirq', this function's name, in a string WARNING:EMBEDDED_FUNCTION_NAME: Prefer using '"%s...", __func__' to using 'load_dram', this function's name, in a string WARNING:EMBEDDED_FUNCTION_NAME: Prefer using '"%s...", __func__' to using 'ReleaseBitmap', this function's name, in a string WARNING:EMBEDDED_FUNCTION_NAME: Prefer using '"%s...", __func__' to using 'vpeirq', this function's name, in a string WARNING:EMBEDDED_FUNCTION_NAME: Prefer using '"%s...", __func__' to using 'WriteText', this function's name, in a string WARNING:LOGGING_CONTINUATION: Avoid logging continuation uses where feasible WARNING:PREFER_PR_LEVEL: Prefer [subsystem eg: netdev]_cont([subsystem]dev, ... then dev_cont(dev, ... then pr_cont(... to printk(KERN_CONT ... WARNING:PREFER_PR_LEVEL: Prefer [subsystem eg: netdev]_dbg([subsystem]dev, ... then dev_dbg(dev, ... then pr_debug(... to printk(KERN_DEBUG ... WARNING:PREFER_PR_LEVEL: Prefer [subsystem eg: netdev]_err([subsystem]dev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... WARNING:PREFER_PR_LEVEL: Prefer [subsystem eg: netdev]_info([subsystem]dev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... WARNING:PREFER_PR_LEVEL: Prefer [subsystem eg: netdev]_warn([subsystem]dev, ... then dev_warn(dev, ... then pr_warn(... to printk(KERN_WARNING ... WARNING:PRINTK_WITHOUT_KERN_LEVEL: printk() should include KERN_ facility level WARNING:TRACING_LOGGING: Unnecessary ftrace-like logging - prefer using ftrace There where different logging styles in this drivers. Convert everything to the recommend pr_* macros. Log messages should mostly be unchanged by this patch. Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 130 +++++++++++++----------------- drivers/staging/media/av7110/av7110.h | 10 ++- drivers/staging/media/av7110/av7110_av.c | 52 ++++++------ drivers/staging/media/av7110/av7110_ca.c | 12 +-- drivers/staging/media/av7110/av7110_hw.c | 116 ++++++++++++-------------- drivers/staging/media/av7110/av7110_ir.c | 3 +- drivers/staging/media/av7110/av7110_v4l.c | 10 +-- drivers/staging/media/av7110/dvb_filter.c | 11 +-- 8 files changed, 157 insertions(+), 187 deletions(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index 30e2e6e65148..728b3892a20c 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -119,27 +119,27 @@ static void init_av7110_av(struct av7110 *av7110) av7110->adac_type = DVB_ADAC_TI; ret = av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right); if (ret < 0) - printk("dvb-ttpci:cannot set internal volume to maximum:%d\n",ret); + pr_err("cannot set internal volume to maximum:%d\n", ret); ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, 1, (u16)av7110->display_ar); if (ret < 0) - printk("dvb-ttpci: unable to set aspect ratio\n"); + pr_err("unable to set aspect ratio\n"); ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, 1, av7110->display_panscan); if (ret < 0) - printk("dvb-ttpci: unable to set pan scan\n"); + pr_err("unable to set pan scan\n"); ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 2, wss_cfg_4_3); if (ret < 0) - printk("dvb-ttpci: unable to configure 4:3 wss\n"); + pr_err("unable to configure 4:3 wss\n"); ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 3, wss_cfg_16_9); if (ret < 0) - printk("dvb-ttpci: unable to configure 16:9 wss\n"); + pr_err("unable to configure 16:9 wss\n"); ret = av7710_set_video_mode(av7110, vidmode); if (ret < 0) - printk("dvb-ttpci:cannot set video mode:%d\n",ret); + pr_err("cannot set video mode:%d\n", ret); /* handle different card types */ /* remaining inits according to card and frontend type */ @@ -148,8 +148,7 @@ static void init_av7110_av(struct av7110 *av7110) if (dev->pci->subsystem_vendor == 0x13c2 && dev->pci->subsystem_device == 0x000a) av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 0); // SPDIF on if (i2c_writereg(av7110, 0x20, 0x00, 0x00) == 1) { - printk ("dvb-ttpci: Crystal audio DAC @ card %d detected\n", - av7110->dvb_adapter.num); + pr_info("Crystal audio DAC @ card %d detected\n", av7110->dvb_adapter.num); av7110->adac_type = DVB_ADAC_CRYSTAL; i2c_writereg(av7110, 0x20, 0x01, 0xd2); i2c_writereg(av7110, 0x20, 0x02, 0x49); @@ -162,23 +161,21 @@ static void init_av7110_av(struct av7110 *av7110) } else if (av7110_init_analog_module(av7110) == 0) { /* done. */ } else if (dev->pci->subsystem_vendor == 0x110a) { - printk("dvb-ttpci: DVB-C w/o analog module @ card %d detected\n", - av7110->dvb_adapter.num); + pr_info("DVB-C w/o analog module @ card %d detected\n", av7110->dvb_adapter.num); av7110->adac_type = DVB_ADAC_NONE; } else { av7110->adac_type = adac; - printk("dvb-ttpci: adac type set to %d @ card %d\n", - av7110->adac_type, av7110->dvb_adapter.num); + pr_info("adac type set to %d @ card %d\n", av7110->adac_type, av7110->dvb_adapter.num); } if (av7110->adac_type == DVB_ADAC_NONE || av7110->adac_type == DVB_ADAC_MSP34x0) { // switch DVB SCART on ret = av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, MainSwitch, 1, 0); if (ret < 0) - printk("dvb-ttpci:cannot switch on SCART(Main):%d\n",ret); + pr_err("cannot switch on SCART(Main):%d\n", ret); ret = av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 1); if (ret < 0) - printk("dvb-ttpci:cannot switch on SCART(AD):%d\n",ret); + pr_err("cannot switch on SCART(AD):%d\n", ret); if (rgb_on && ((av7110->dev->pci->subsystem_vendor == 0x110a) || (av7110->dev->pci->subsystem_vendor == 0x13c2)) && @@ -193,12 +190,12 @@ static void init_av7110_av(struct av7110 *av7110) ret = av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right); if (ret < 0) - printk("dvb-ttpci:cannot set volume :%d\n",ret); + pr_err("cannot set volume :%d\n", ret); } static void recover_arm(struct av7110 *av7110) { - dprintk(4, "%p\n",av7110); + dprintk(4, "%p\n", av7110); av7110_bootarm(av7110); msleep(100); @@ -230,7 +227,7 @@ static int arm_thread(void *data) u16 newloops = 0; int timeout; - dprintk(4, "%p\n",av7110); + dprintk(4, "%p\n", av7110); for (;;) { timeout = wait_event_interruptible_timeout(av7110->arm_wait, @@ -250,8 +247,7 @@ static int arm_thread(void *data) mutex_unlock(&av7110->dcomlock); if (newloops == av7110->arm_loops || av7110->arm_errors > 3) { - printk(KERN_ERR "dvb-ttpci: ARM crashed @ card %d\n", - av7110->dvb_adapter.num); + pr_err("ARM crashed @ card %d\n", av7110->dvb_adapter.num); recover_arm(av7110); @@ -325,7 +321,7 @@ static inline void print_time(char *s) struct timespec64 ts; ktime_get_real_ts64(&ts); - printk("%s: %lld.%09ld\n", s, (s64)ts.tv_sec, ts.tv_nsec); + pr_info("%s(): %lld.%09ld\n", s, (s64)ts.tv_sec, ts.tv_nsec); #endif } @@ -336,7 +332,7 @@ static inline void start_debi_dma(struct av7110 *av7110, int dir, { dprintk(8, "%c %08lx %u\n", dir == DEBI_READ ? 'R' : 'W', addr, len); if (saa7146_wait_for_debi_done(av7110->dev, 0)) { - printk(KERN_ERR "%s: saa7146_wait_for_debi_done timed out\n", __func__); + pr_err("%s(): saa7146_wait_for_debi_done timed out\n", __func__); return; } @@ -361,9 +357,8 @@ static void debiirq(struct tasklet_struct *t) dprintk(4, "type 0x%04x\n", type); if (type == -1) { - printk("DEBI irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n", - jiffies, saa7146_read(av7110->dev, PSR), - saa7146_read(av7110->dev, SSR)); + pr_err("DEBI irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n", jiffies, + saa7146_read(av7110->dev, PSR), saa7146_read(av7110->dev, SSR)); goto debi_done; } av7110->debitype = -1; @@ -424,7 +419,7 @@ static void debiirq(struct tasklet_struct *t) case DATA_DEBUG_MESSAGE: ((s8 *)av7110->debi_virt)[Reserved_SIZE - 1] = 0; - printk("%s\n", (s8 *) av7110->debi_virt); + pr_info("%s\n", (s8 *)av7110->debi_virt); xfer = RX_BUFF; break; @@ -460,12 +455,11 @@ static void gpioirq(struct tasklet_struct *t) if (av7110->debitype != -1) /* we shouldn't get any irq while a debi xfer is running */ - printk("dvb-ttpci: GPIO0 irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n", - jiffies, saa7146_read(av7110->dev, PSR), - saa7146_read(av7110->dev, SSR)); + pr_err("GPIO0 irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n", jiffies, + saa7146_read(av7110->dev, PSR), saa7146_read(av7110->dev, SSR)); if (saa7146_wait_for_debi_done(av7110->dev, 0)) { - printk(KERN_ERR "%s: saa7146_wait_for_debi_done timed out\n", __func__); + pr_err("%s(): saa7146_wait_for_debi_done timed out\n", __func__); BUG(); /* maybe we should try resetting the debi? */ } @@ -657,7 +651,7 @@ static void gpioirq(struct tasklet_struct *t) break; default: - printk("dvb-ttpci: gpioirq unknown type=%d len=%d\n", + pr_err("%s(): unknown irq: type=%d len=%d\n", __func__, av7110->debitype, av7110->debilen); break; } @@ -798,9 +792,8 @@ static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter) ret = av7110_fw_request(av7110, buf, 20, &handle, 1); if (ret != 0 || handle >= 32) { - printk(KERN_ERR "dvb-ttpci: %s error buf %04x %04x %04x %04x ret %d handle %04x\n", - __func__, buf[0], buf[1], buf[2], buf[3], - ret, handle); + pr_err("%s(): error buf %04x %04x %04x %04x ret %d handle %04x\n", + __func__, buf[0], buf[1], buf[2], buf[3], ret, handle); dvbdmxfilter->hw_handle = 0xffff; if (!ret) ret = -1; @@ -828,8 +821,8 @@ static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter) handle = dvbdmxfilter->hw_handle; if (handle >= 32) { - printk("%s tried to stop invalid filter %04x, filter type = %x\n", - __func__, handle, dvbdmxfilter->type); + pr_err("%s(): tried to stop invalid filter %04x, filter type = %x\n", + __func__, handle, dvbdmxfilter->type); return -EINVAL; } @@ -840,9 +833,8 @@ static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter) buf[2] = handle; ret = av7110_fw_request(av7110, buf, 3, answ, 2); if (ret != 0 || answ[1] != handle) { - printk(KERN_ERR "dvb-ttpci: %s error cmd %04x %04x %04x ret %x resp %04x %04x pid %d\n", - __func__, buf[0], buf[1], buf[2], ret, - answ[0], answ[1], dvbdmxfilter->feed->pid); + pr_err("%s(): error cmd %04x %04x %04x ret %x resp %04x %04x pid %d\n", __func__, + buf[0], buf[1], buf[2], ret, answ[0], answ[1], dvbdmxfilter->feed->pid); if (!ret) ret = -1; } @@ -1117,11 +1109,10 @@ static int dvb_get_stc(struct dmx_demux *demux, unsigned int num, ret = av7110_fw_request(av7110, &tag, 0, fwstc, 4); if (ret) { - printk(KERN_ERR "%s: av7110_fw_request error\n", __func__); + pr_err("%s(): av7110_fw_request error\n", __func__); return ret; } - dprintk(2, "fwstc = %04hx %04hx %04hx %04hx\n", - fwstc[0], fwstc[1], fwstc[2], fwstc[3]); + dprintk(2, "fwstc = %04hx %04hx %04hx %04hx\n", fwstc[0], fwstc[1], fwstc[2], fwstc[3]); *stc = (((uint64_t)((fwstc[3] & 0x8000) >> 15)) << 32) | (((uint64_t)fwstc[1]) << 16) | ((uint64_t)fwstc[0]); @@ -1250,11 +1241,10 @@ static void vpeirq(struct tasklet_struct *t) dma_sync_sg_for_cpu(&budget->dev->pci->dev, budget->pt.slist, budget->pt.nents, DMA_FROM_DEVICE); -#if 0 +#ifdef RPS_DEBUG /* track rps1 activity */ - printk("vpeirq: %02x Event Counter 1 0x%04x\n", - mem[olddma], - saa7146_read(budget->dev, EC1R) & 0x3fff); + pr_info("%s(): %02x Event Counter 1 0x%04x\n", __func__, mem[olddma], + saa7146_read(budget->dev, EC1R) & 0x3fff); #endif if (newdma > olddma) { @@ -1355,7 +1345,7 @@ static int av7110_register(struct av7110 *av7110) dvb_dmxdev_init(&av7110->dmxdev1, &av7110->dvb_adapter); dvb_net_init(&av7110->dvb_adapter, &av7110->dvb_net1, &dvbdemux1->dmx); - printk("dvb-ttpci: additional demux1 for budget-patch registered\n"); + pr_info("additional demux1 for budget-patch registered\n"); } return 0; } @@ -1444,7 +1434,7 @@ static int check_firmware(struct av7110 *av7110) ptr = av7110->bin_fw; if (ptr[0] != 'A' || ptr[1] != 'V' || ptr[2] != 'F' || ptr[3] != 'W') { - printk("dvb-ttpci: this is not an av7110 firmware\n"); + pr_err("this is not an av7110 firmware\n"); return -EINVAL; } ptr += 4; @@ -1455,11 +1445,11 @@ static int check_firmware(struct av7110 *av7110) len = get_unaligned_be32(ptr); ptr += 4; if (len >= 512) { - printk("dvb-ttpci: dpram file is way too big.\n"); + pr_err("dpram file is way too big.\n"); return -EINVAL; } if (crc != crc32_le(0, ptr, len)) { - printk("dvb-ttpci: crc32 of dpram file does not match.\n"); + pr_err("crc32 of dpram file does not match.\n"); return -EINVAL; } av7110->bin_dpram = ptr; @@ -1474,11 +1464,11 @@ static int check_firmware(struct av7110 *av7110) if (len <= 200000 || len >= 300000 || len > ((av7110->bin_fw + av7110->size_fw) - ptr)) { - printk("dvb-ttpci: root file has strange size (%d). aborting.\n", len); + pr_err("root file has strange size (%d). aborting.\n", len); return -EINVAL; } if (crc != crc32_le(0, ptr, len)) { - printk("dvb-ttpci: crc32 of root file does not match.\n"); + pr_err("crc32 of root file does not match.\n"); return -EINVAL; } av7110->bin_root = ptr; @@ -1500,18 +1490,17 @@ static int get_firmware(struct av7110 *av7110) ret = request_firmware(&fw, "dvb-ttpci-01.fw", &av7110->dev->pci->dev); if (ret) { if (ret == -ENOENT) { - printk(KERN_ERR "dvb-ttpci: could not load firmware, file not found: dvb-ttpci-01.fw\n"); - printk(KERN_ERR "dvb-ttpci: usually this should be in /usr/lib/hotplug/firmware or /lib/firmware\n"); - printk(KERN_ERR "dvb-ttpci: and can be downloaded from https://linuxtv.org/download/dvb/firmware/\n"); + pr_err("could not load firmware, file not found: dvb-ttpci-01.fw\n"); + pr_err("usually this should be in /usr/lib/hotplug/firmware or /lib/firmware\n"); + pr_err("and can be downloaded from https://linuxtv.org/download/dvb/firmware/\n"); } else { - printk(KERN_ERR "dvb-ttpci: cannot request firmware (error %i)\n", - ret); + pr_err("cannot request firmware (error %i)\n", ret); } return -EINVAL; } if (fw->size <= 200000) { - printk("dvb-ttpci: this firmware is way too small.\n"); + pr_err("this firmware is way too small.\n"); release_firmware(fw); return -EINVAL; } @@ -1827,7 +1816,7 @@ static int nexusca_stv0297_tuner_set_params(struct dvb_frontend *fe) if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) { - printk("nexusca: pll transfer failed!\n"); + pr_err("nexusca: pll transfer failed!\n"); return -EIO; } @@ -2234,7 +2223,7 @@ static int frontend_init(struct av7110 *av7110) av7110->fe->tuner_priv = &av7110->i2c_adap; if (!dvb_attach(lnbp21_attach, av7110->fe, &av7110->i2c_adap, 0, 0)) { - printk("dvb-ttpci: LNBP21 not found!\n"); + pr_err("LNBP21 not found!\n"); if (av7110->fe->ops.release) av7110->fe->ops.release(av7110->fe); av7110->fe = NULL; @@ -2250,11 +2239,9 @@ static int frontend_init(struct av7110 *av7110) if (!av7110->fe) { /* FIXME: propagate the failure code from the lower layers */ ret = -ENOMEM; - printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", - av7110->dev->pci->vendor, - av7110->dev->pci->device, - av7110->dev->pci->subsystem_vendor, - av7110->dev->pci->subsystem_device); + pr_err("A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", + av7110->dev->pci->vendor, av7110->dev->pci->device, + av7110->dev->pci->subsystem_vendor, av7110->dev->pci->subsystem_device); } else { FE_FUNC_OVERRIDE(av7110->fe->ops.init, av7110->fe_init, av7110_fe_init); FE_FUNC_OVERRIDE(av7110->fe->ops.read_status, av7110->fe_read_status, av7110_fe_read_status); @@ -2268,7 +2255,7 @@ static int frontend_init(struct av7110 *av7110) ret = dvb_register_frontend(&av7110->dvb_adapter, av7110->fe); if (ret < 0) { - printk("av7110: Frontend registration failed!\n"); + pr_err("av7110: Frontend registration failed!\n"); dvb_frontend_detach(av7110->fe); av7110->fe = NULL; } @@ -2427,12 +2414,12 @@ static int av7110_attach(struct saa7146_dev *dev, */ if ((saa7146_read(dev, GPIO_CTRL) & 0x10000000) == 0) { budgetpatch = 1; - printk("dvb-ttpci: BUDGET-PATCH DETECTED.\n"); + pr_info("BUDGET-PATCH DETECTED.\n"); } /* Disable RPS1 */ saa7146_write(dev, MC1, (MASK_29)); #if RPS_IRQ - printk("dvb-ttpci: Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff ); + pr_info("Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff); #endif } @@ -2487,7 +2474,7 @@ static int av7110_attach(struct saa7146_dev *dev, } if (av7110->full_ts) { - printk(KERN_INFO "dvb-ttpci: full-ts mode enabled for saa7146 port B\n"); + pr_info("full-ts mode enabled for saa7146 port B\n"); spin_lock_init(&av7110->feedlock1); av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length, &av7110->pt); @@ -2663,9 +2650,8 @@ static int av7110_attach(struct saa7146_dev *dev, goto err_stop_arm_9; if (FW_VERSION(av7110->arm_app) < 0x2501) - printk(KERN_WARNING - "dvb-ttpci: Warning, firmware version 0x%04x is too old. System might be unstable!\n", - FW_VERSION(av7110->arm_app)); + pr_warn("Warning, firmware version 0x%04x is too old. System might be unstable!\n", + FW_VERSION(av7110->arm_app)); thread = kthread_run(arm_thread, (void *)av7110, "arm_mon"); if (IS_ERR(thread)) { @@ -2702,7 +2688,7 @@ static int av7110_attach(struct saa7146_dev *dev, #if IS_ENABLED(CONFIG_DVB_AV7110_IR) av7110_ir_init(av7110); #endif - printk(KERN_INFO "dvb-ttpci: found av7110-%d.\n", av7110_num); + pr_info("found av7110-%d.\n", av7110_num); av7110_num++; out: return ret; diff --git a/drivers/staging/media/av7110/av7110.h b/drivers/staging/media/av7110/av7110.h index 3f6b88f0f329..403dbbba5b5b 100644 --- a/drivers/staging/media/av7110/av7110.h +++ b/drivers/staging/media/av7110/av7110.h @@ -40,10 +40,14 @@ extern int av7110_debug; +#ifdef pr_fmt +#undef pr_fmt +#endif +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define dprintk(level, fmt, arg...) do { \ - if (level & av7110_debug) \ - printk(KERN_DEBUG KBUILD_MODNAME ": %s(): " fmt, \ - __func__, ##arg); \ + if ((level) & av7110_debug) \ + pr_info("%s(): " fmt, __func__, ##arg); \ } while (0) #define MAXFILT 32 diff --git a/drivers/staging/media/av7110/av7110_av.c b/drivers/staging/media/av7110/av7110_av.c index a408e10fb41a..2993ac43c49c 100644 --- a/drivers/staging/media/av7110/av7110_av.c +++ b/drivers/staging/media/av7110/av7110_av.c @@ -151,7 +151,7 @@ int av7110_av_start_play(struct av7110 *av7110, int av) { int ret = 0; - dprintk(2, "av7110:%p, \n", av7110); + dprintk(2, "av7110:%p\n", av7110); if (av7110->rec_mode) return -EBUSY; @@ -186,7 +186,7 @@ int av7110_av_stop(struct av7110 *av7110, int av) { int ret = 0; - dprintk(2, "av7110:%p, \n", av7110); + dprintk(2, "av7110:%p\n", av7110); if (!(av7110->playing & av) && !(av7110->rec_mode & av)) return 0; @@ -245,14 +245,14 @@ int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen) ((sync & ~0x1f) == 0x000001c0) || (sync == 0x000001bd)) break; - printk("resync\n"); + pr_info("resync\n"); DVB_RINGBUFFER_SKIP(buf, 1); } blen = DVB_RINGBUFFER_PEEK(buf, 4) << 8; blen |= DVB_RINGBUFFER_PEEK(buf, 5); blen += 6; if (len < blen || blen > dlen) { - //printk("buffer empty - avail %d blen %u dlen %d\n", len, blen, dlen); + //pr_info("buffer empty - avail %d blen %u dlen %d\n", len, blen, dlen); wake_up(&buf->queue); return -1; } @@ -271,7 +271,7 @@ int av7110_set_volume(struct av7110 *av7110, unsigned int volleft, unsigned int vol, val, balance = 0; int err; - dprintk(2, "av7110:%p, \n", av7110); + dprintk(2, "av7110:%p\n", av7110); av7110->mixer.volume_left = volleft; av7110->mixer.volume_right = volright; @@ -323,7 +323,7 @@ int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode) { int ret; - dprintk(2, "av7110:%p, \n", av7110); + dprintk(2, "av7110:%p\n", av7110); ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, LoadVidCode, 1, mode); @@ -357,7 +357,7 @@ static int get_video_format(struct av7110 *av7110, u8 *buf, int count) u8 *p; int ret = 0; - dprintk(2, "av7110:%p, \n", av7110); + dprintk(2, "av7110:%p\n", av7110); if (av7110->sinfo) return 0; @@ -410,7 +410,7 @@ static void play_video_cb(u8 *buf, int count, void *priv) { struct av7110 *av7110 = (struct av7110 *)priv; - dprintk(2, "av7110:%p, \n", av7110); + dprintk(2, "av7110:%p\n", av7110); if ((buf[3] & 0xe0) == 0xe0) { get_video_format(av7110, buf, count); @@ -424,7 +424,7 @@ static void play_audio_cb(u8 *buf, int count, void *priv) { struct av7110 *av7110 = (struct av7110 *)priv; - dprintk(2, "av7110:%p, \n", av7110); + dprintk(2, "av7110:%p\n", av7110); aux_ring_buffer_write(&av7110->aout, buf, count); } @@ -438,7 +438,7 @@ static ssize_t ts_play(struct av7110 *av7110, const char __user *buf, u8 *kb; unsigned long todo = count; - dprintk(2, "%s: type %d cnt %lu\n", __func__, type, count); + dprintk(2, "type %d cnt %lu\n", type, count); rb = (type) ? &av7110->avout : &av7110->aout; kb = av7110->kbuf[type]; @@ -474,7 +474,7 @@ static ssize_t dvb_play(struct av7110 *av7110, const char __user *buf, { unsigned long todo = count, n; - dprintk(2, "av7110:%p, \n", av7110); + dprintk(2, "av7110:%p\n", av7110); if (!av7110->kbuf[type]) return -ENOBUFS; @@ -508,7 +508,7 @@ static ssize_t dvb_play_kernel(struct av7110 *av7110, const u8 *buf, { unsigned long todo = count, n; - dprintk(2, "av7110:%p, \n", av7110); + dprintk(2, "av7110:%p\n", av7110); if (!av7110->kbuf[type]) return -ENOBUFS; @@ -539,7 +539,7 @@ static ssize_t dvb_aplay(struct av7110 *av7110, const char __user *buf, { unsigned long todo = count, n; - dprintk(2, "av7110:%p, \n", av7110); + dprintk(2, "av7110:%p\n", av7110); if (!av7110->kbuf[type]) return -ENOBUFS; @@ -841,7 +841,7 @@ int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t l struct dvb_demux *demux = feed->demux; struct av7110 *av7110 = demux->priv; - dprintk(2, "av7110:%p, \n", av7110); + dprintk(2, "av7110:%p\n", av7110); if (av7110->full_ts && demux->dmx.frontend->source != DMX_MEMORY_FE) return 0; @@ -928,7 +928,7 @@ static __poll_t dvb_video_poll(struct file *file, poll_table *wait) struct av7110 *av7110 = dvbdev->priv; __poll_t mask = 0; - dprintk(2, "av7110:%p, \n", av7110); + dprintk(2, "av7110:%p\n", av7110); if ((file->f_flags & O_ACCMODE) != O_RDONLY) poll_wait(file, &av7110->avout.queue, wait); @@ -958,7 +958,7 @@ static ssize_t dvb_video_write(struct file *file, const char __user *buf, struct av7110 *av7110 = dvbdev->priv; unsigned char c; - dprintk(2, "av7110:%p, \n", av7110); + dprintk(2, "av7110:%p\n", av7110); if ((file->f_flags & O_ACCMODE) == O_RDONLY) return -EPERM; @@ -980,7 +980,7 @@ static __poll_t dvb_audio_poll(struct file *file, poll_table *wait) struct av7110 *av7110 = dvbdev->priv; __poll_t mask = 0; - dprintk(2, "av7110:%p, \n", av7110); + dprintk(2, "av7110:%p\n", av7110); poll_wait(file, &av7110->aout.queue, wait); @@ -1002,10 +1002,10 @@ static ssize_t dvb_audio_write(struct file *file, const char __user *buf, struct av7110 *av7110 = dvbdev->priv; unsigned char c; - dprintk(2, "av7110:%p, \n", av7110); + dprintk(2, "av7110:%p\n", av7110); if (av7110->audiostate.stream_source != AUDIO_SOURCE_MEMORY) { - printk(KERN_ERR "not audio source memory\n"); + pr_err("not audio source memory\n"); return -EPERM; } @@ -1027,7 +1027,7 @@ static int play_iframe(struct av7110 *av7110, char __user *buf, unsigned int len int progressive = 0; int match = 0; - dprintk(2, "av7110:%p, \n", av7110); + dprintk(2, "av7110:%p\n", av7110); if (len == 0) return 0; @@ -1134,7 +1134,7 @@ static int dvb_video_ioctl(struct file *file, unsigned long arg = (unsigned long)parg; int ret = 0; - dprintk(1, "av7110:%p, cmd=%04x\n", av7110,cmd); + dprintk(1, "av7110:%p, cmd=%04x\n", av7110, cmd); if ((file->f_flags & O_ACCMODE) == O_RDONLY) { if (cmd != VIDEO_GET_STATUS && cmd != VIDEO_GET_EVENT && @@ -1366,7 +1366,7 @@ static int dvb_audio_ioctl(struct file *file, unsigned long arg = (unsigned long)parg; int ret = 0; - dprintk(1, "av7110:%p, cmd=%04x\n", av7110,cmd); + dprintk(1, "av7110:%p, cmd=%04x\n", av7110, cmd); if (((file->f_flags & O_ACCMODE) == O_RDONLY) && (cmd != AUDIO_GET_STATUS)) @@ -1514,7 +1514,7 @@ static int dvb_video_open(struct inode *inode, struct file *file) struct av7110 *av7110 = dvbdev->priv; int err; - dprintk(2, "av7110:%p, \n", av7110); + dprintk(2, "av7110:%p\n", av7110); err = dvb_generic_open(inode, file); if (err < 0) @@ -1540,7 +1540,7 @@ static int dvb_video_release(struct inode *inode, struct file *file) struct dvb_device *dvbdev = file->private_data; struct av7110 *av7110 = dvbdev->priv; - dprintk(2, "av7110:%p, \n", av7110); + dprintk(2, "av7110:%p\n", av7110); if ((file->f_flags & O_ACCMODE) != O_RDONLY) av7110_av_stop(av7110, RP_VIDEO); @@ -1554,7 +1554,7 @@ static int dvb_audio_open(struct inode *inode, struct file *file) struct av7110 *av7110 = dvbdev->priv; int err = dvb_generic_open(inode, file); - dprintk(2, "av7110:%p, \n", av7110); + dprintk(2, "av7110:%p\n", av7110); if (err < 0) return err; @@ -1568,7 +1568,7 @@ static int dvb_audio_release(struct inode *inode, struct file *file) struct dvb_device *dvbdev = file->private_data; struct av7110 *av7110 = dvbdev->priv; - dprintk(2, "av7110:%p, \n", av7110); + dprintk(2, "av7110:%p\n", av7110); av7110_av_stop(av7110, RP_AUDIO); return dvb_generic_release(inode, file); diff --git a/drivers/staging/media/av7110/av7110_ca.c b/drivers/staging/media/av7110/av7110_ca.c index c28e41d42ba6..6ce212c64e5d 100644 --- a/drivers/staging/media/av7110/av7110_ca.c +++ b/drivers/staging/media/av7110/av7110_ca.c @@ -26,7 +26,7 @@ void CI_handle(struct av7110 *av7110, u8 *data, u16 len) { - dprintk(8, "av7110:%p\n",av7110); + dprintk(8, "av7110:%p\n", av7110); if (len < 3) return; @@ -199,7 +199,7 @@ static int dvb_ca_open(struct inode *inode, struct file *file) struct av7110 *av7110 = dvbdev->priv; int err = dvb_generic_open(inode, file); - dprintk(8, "av7110:%p\n",av7110); + dprintk(8, "av7110:%p\n", av7110); if (err < 0) return err; @@ -215,7 +215,7 @@ static __poll_t dvb_ca_poll(struct file *file, poll_table *wait) struct dvb_ringbuffer *wbuf = &av7110->ci_wbuffer; __poll_t mask = 0; - dprintk(8, "av7110:%p\n",av7110); + dprintk(8, "av7110:%p\n", av7110); poll_wait(file, &rbuf->queue, wait); poll_wait(file, &wbuf->queue, wait); @@ -236,7 +236,7 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) unsigned long arg = (unsigned long)parg; int ret = 0; - dprintk(8, "av7110:%p\n",av7110); + dprintk(8, "av7110:%p\n", av7110); if (mutex_lock_interruptible(&av7110->ioctl_mutex)) return -ERESTARTSYS; @@ -322,7 +322,7 @@ static ssize_t dvb_ca_write(struct file *file, const char __user *buf, struct dvb_device *dvbdev = file->private_data; struct av7110 *av7110 = dvbdev->priv; - dprintk(8, "av7110:%p\n",av7110); + dprintk(8, "av7110:%p\n", av7110); return ci_ll_write(&av7110->ci_wbuffer, file, buf, count, ppos); } @@ -332,7 +332,7 @@ static ssize_t dvb_ca_read(struct file *file, char __user *buf, struct dvb_device *dvbdev = file->private_data; struct av7110 *av7110 = dvbdev->priv; - dprintk(8, "av7110:%p\n",av7110); + dprintk(8, "av7110:%p\n", av7110); return ci_ll_read(&av7110->ci_rbuffer, file, buf, count, ppos); } diff --git a/drivers/staging/media/av7110/av7110_hw.c b/drivers/staging/media/av7110/av7110_hw.c index 5a4de3e3e967..bf8e6dca40e5 100644 --- a/drivers/staging/media/av7110/av7110_hw.c +++ b/drivers/staging/media/av7110/av7110_hw.c @@ -48,11 +48,11 @@ int av7110_debiwrite(struct av7110 *av7110, u32 config, struct saa7146_dev *dev = av7110->dev; if (count > 32764) { - printk("%s: invalid count %d\n", __func__, count); + pr_err("%s(): invalid count %d\n", __func__, count); return -1; } if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { - printk("%s: wait_for_debi_done failed\n", __func__); + pr_err("%s(): wait_for_debi_done failed\n", __func__); return -1; } saa7146_write(dev, DEBI_CONFIG, config); @@ -71,11 +71,11 @@ u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, unsigned int co u32 result = 0; if (count > 32764) { - printk("%s: invalid count %d\n", __func__, count); + pr_err("%s(): invalid count %d\n", __func__, count); return 0; } if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { - printk("%s: wait_for_debi_done #1 failed\n", __func__); + pr_err("%s(): wait_for_debi_done #1 failed\n", __func__); return 0; } saa7146_write(dev, DEBI_AD, av7110->debi_bus); @@ -86,7 +86,7 @@ u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, unsigned int co if (count > 4) return count; if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { - printk("%s: wait_for_debi_done #2 failed\n", __func__); + pr_err("%s(): wait_for_debi_done #2 failed\n", __func__); return 0; } @@ -146,7 +146,7 @@ static int load_dram(struct av7110 *av7110, u32 *data, int len) for (i = 0; i < blocks; i++) { if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { - printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at block %d\n", i); + pr_err("%s(): timeout at block %d\n", __func__, i); return -ETIMEDOUT; } dprintk(4, "writing DRAM block %d\n", i); @@ -161,7 +161,7 @@ static int load_dram(struct av7110 *av7110, u32 *data, int len) if (rest > 0) { if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { - printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at last block\n"); + pr_err("%s(): timeout at last block\n", __func__); return -ETIMEDOUT; } if (rest > 4) @@ -176,13 +176,13 @@ static int load_dram(struct av7110 *av7110, u32 *data, int len) iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); } if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { - printk(KERN_ERR "dvb-ttpci: load_dram(): timeout after last block\n"); + pr_err("%s(): timeout after last block\n", __func__); return -ETIMEDOUT; } iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, 0, 2); iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_AV7110_BOOT_COMPLETE) < 0) { - printk(KERN_ERR "dvb-ttpci: load_dram(): final handshake timeout\n"); + pr_err("%s(): final handshake timeout\n", __func__); return -ETIMEDOUT; } return 0; @@ -221,8 +221,8 @@ int av7110_bootarm(struct av7110 *av7110) ret = irdebi(av7110, DEBINOSWAP, DPRAM_BASE, 0, 4); if (ret != 0x10325476) { - printk(KERN_ERR "dvb-ttpci: debi test in av7110_bootarm() failed: %08x != %08x (check your BIOS 'Plug&Play OS' settings)\n", - ret, 0x10325476); + pr_err("debi test in %s() failed: %08x != %08x (check your BIOS 'Plug&Play OS' settings)\n", + __func__, ret, 0x10325476); return -1; } for (i = 0; i < 8192; i += 4) @@ -237,8 +237,7 @@ int av7110_bootarm(struct av7110 *av7110) ret = request_firmware(&fw, fw_name, &dev->pci->dev); if (ret) { - printk(KERN_ERR "dvb-ttpci: Failed to load firmware \"%s\"\n", - fw_name); + pr_err("Failed to load firmware \"%s\"\n", fw_name); return ret; } @@ -247,7 +246,7 @@ int av7110_bootarm(struct av7110 *av7110) iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); if (saa7146_wait_for_debi_done(av7110->dev, 1)) { - printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): saa7146_wait_for_debi_done() timed out\n"); + pr_err("%s(): saa7146_wait_for_debi_done() timed out\n", __func__); return -ETIMEDOUT; } saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI); @@ -255,7 +254,7 @@ int av7110_bootarm(struct av7110 *av7110) dprintk(1, "load dram code\n"); if (load_dram(av7110, (u32 *)av7110->bin_root, av7110->size_root) < 0) { - printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): load_dram() failed\n"); + pr_err("%s(): load_dram() failed\n", __func__); return -1; } @@ -266,7 +265,7 @@ int av7110_bootarm(struct av7110 *av7110) mwdebi(av7110, DEBISWAB, DPRAM_BASE, av7110->bin_dpram, av7110->size_dpram); if (saa7146_wait_for_debi_done(av7110->dev, 1)) { - printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): saa7146_wait_for_debi_done() timed out after loading DRAM\n"); + pr_err("%s(): saa7146_wait_for_debi_done() timed out after loading DRAM\n", __func__); return -ETIMEDOUT; } saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI); @@ -310,8 +309,7 @@ int av7110_wait_msgstate(struct av7110 *av7110, u16 flags) if ((stat & flags) == 0) break; if (err) { - printk(KERN_ERR "%s: timeout waiting for MSGSTATE %04x\n", - __func__, stat & flags); + pr_err("%s(): timeout waiting for MSGSTATE %04x\n", __func__, stat & flags); return -ETIMEDOUT; } msleep(1); @@ -341,7 +339,7 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16 *buf, int length) if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) break; if (err) { - printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND idle\n", __func__); + pr_err("%s(): timeout waiting for COMMAND idle\n", __func__); av7110->arm_errors++; return -ETIMEDOUT; } @@ -358,7 +356,7 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16 *buf, int length) if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) break; if (err) { - printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for HANDSHAKE_REG\n", __func__); + pr_err("%s(): timeout waiting for HANDSHAKE_REG\n", __func__); return -ETIMEDOUT; } msleep(1); @@ -397,15 +395,13 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16 *buf, int length) err = time_after(jiffies, start + ARM_WAIT_FREE); stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); if (stat & flags[0]) { - printk(KERN_ERR "%s: %s QUEUE overflow\n", - __func__, type); + pr_err("%s(): %s QUEUE overflow\n", __func__, type); return -1; } if ((stat & flags[1]) == 0) break; if (err) { - printk(KERN_ERR "%s: timeout waiting on busy %s QUEUE\n", - __func__, type); + pr_err("%s(): timeout waiting on busy %s QUEUE\n", __func__, type); av7110->arm_errors++; return -ETIMEDOUT; } @@ -433,7 +429,7 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16 *buf, int length) if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) break; if (err) { - printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND %d to complete\n", + pr_err("%s(): timeout waiting for COMMAND %d to complete\n", __func__, (buf[0] >> 8) & 0xff); return -ETIMEDOUT; } @@ -442,10 +438,10 @@ static int __av7110_send_fw_cmd(struct av7110 *av7110, u16 *buf, int length) stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); if (stat & GPMQOver) { - printk(KERN_ERR "dvb-ttpci: %s(): GPMQOver\n", __func__); + pr_err("%s(): GPMQOver\n", __func__); return -ENOSPC; } else if (stat & OSDQOver) { - printk(KERN_ERR "dvb-ttpci: %s(): OSDQOver\n", __func__); + pr_err("%s(): OSDQOver\n", __func__); return -ENOSPC; } #endif @@ -469,8 +465,7 @@ static int av7110_send_fw_cmd(struct av7110 *av7110, u16 *buf, int length) ret = __av7110_send_fw_cmd(av7110, buf, length); mutex_unlock(&av7110->dcomlock); if (ret && ret != -ERESTARTSYS) - printk(KERN_ERR "dvb-ttpci: %s(): av7110_send_fw_cmd error %d\n", - __func__, ret); + pr_err("%s(): error %d\n", __func__, ret); return ret; } @@ -483,9 +478,7 @@ int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...) // dprintk(4, "%p\n", av7110); if (2 + num > ARRAY_SIZE(buf)) { - printk(KERN_WARNING - "%s: %s len=%d is too big!\n", - KBUILD_MODNAME, __func__, num); + pr_warn("%s(): len=%d is too big!\n", __func__, num); return -EINVAL; } @@ -501,7 +494,7 @@ int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...) ret = av7110_send_fw_cmd(av7110, buf, num + 2); if (ret && ret != -ERESTARTSYS) - printk(KERN_ERR "dvb-ttpci: av7110_fw_cmd error %d\n", ret); + pr_err("%s(): error %d\n", __func__, ret); return ret; } @@ -523,7 +516,7 @@ int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len) ret = av7110_send_fw_cmd(av7110, cmd, 18); if (ret && ret != -ERESTARTSYS) - printk(KERN_ERR "dvb-ttpci: av7110_send_ci_cmd error %d\n", ret); + pr_err("%s(): error %d\n", __func__, ret); return ret; } #endif /* 0 */ @@ -551,7 +544,7 @@ int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, err = __av7110_send_fw_cmd(av7110, request_buf, request_buf_len); if (err < 0) { mutex_unlock(&av7110->dcomlock); - printk(KERN_ERR "dvb-ttpci: av7110_fw_request error %d\n", err); + pr_err("%s(): error %d\n", __func__, err); return err; } @@ -561,7 +554,7 @@ int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) break; if (err) { - printk(KERN_ERR "%s: timeout waiting for COMMAND to complete\n", __func__); + pr_err("%s(): timeout waiting for COMMAND to complete\n", __func__); mutex_unlock(&av7110->dcomlock); return -ETIMEDOUT; } @@ -577,7 +570,7 @@ int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) break; if (err) { - printk(KERN_ERR "%s: timeout waiting for HANDSHAKE_REG\n", __func__); + pr_err("%s(): timeout waiting for HANDSHAKE_REG\n", __func__); mutex_unlock(&av7110->dcomlock); return -ETIMEDOUT; } @@ -588,11 +581,11 @@ int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, #ifdef COM_DEBUG stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); if (stat & GPMQOver) { - printk(KERN_ERR "%s: GPMQOver\n", __func__); + pr_err("%s(): GPMQOver\n", __func__); mutex_unlock(&av7110->dcomlock); return -1; } else if (stat & OSDQOver) { - printk(KERN_ERR "%s: OSDQOver\n", __func__); + pr_err("%s(): OSDQOver\n", __func__); mutex_unlock(&av7110->dcomlock); return -1; } @@ -611,7 +604,7 @@ static int av7110_fw_query(struct av7110 *av7110, u16 tag, u16 *buf, s16 length) ret = av7110_fw_request(av7110, &tag, 0, buf, length); if (ret) - printk(KERN_ERR "dvb-ttpci: av7110_fw_query error %d\n", ret); + pr_err("%s(): error %d\n", __func__, ret); return ret; } @@ -628,8 +621,7 @@ int av7110_firmversion(struct av7110 *av7110) dprintk(4, "%p\n", av7110); if (av7110_fw_query(av7110, tag, buf, 16)) { - printk("dvb-ttpci: failed to boot firmware @ card %d\n", - av7110->dvb_adapter.num); + pr_err("failed to boot firmware @ card %d\n", av7110->dvb_adapter.num); return -EIO; } @@ -639,17 +631,17 @@ int av7110_firmversion(struct av7110 *av7110) av7110->arm_app = (buf[6] << 16) + buf[7]; av7110->avtype = (buf[8] << 16) + buf[9]; - printk("dvb-ttpci: info @ card %d: firm %08x, rtsl %08x, vid %08x, app %08x\n", - av7110->dvb_adapter.num, av7110->arm_fw, - av7110->arm_rtsl, av7110->arm_vid, av7110->arm_app); + pr_info("info @ card %d: firm %08x, rtsl %08x, vid %08x, app %08x\n", + av7110->dvb_adapter.num, av7110->arm_fw, + av7110->arm_rtsl, av7110->arm_vid, av7110->arm_app); /* print firmware capabilities */ if (FW_CI_LL_SUPPORT(av7110->arm_app)) - printk("dvb-ttpci: firmware @ card %d supports CI link layer interface\n", - av7110->dvb_adapter.num); + pr_info("firmware @ card %d supports CI link layer interface\n", + av7110->dvb_adapter.num); else - printk("dvb-ttpci: no firmware support for CI link layer interface @ card %d\n", - av7110->dvb_adapter.num); + pr_info("no firmware support for CI link layer interface @ card %d\n", + av7110->dvb_adapter.num); return 0; } @@ -678,7 +670,7 @@ int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long bu ret = av7110_send_fw_cmd(av7110, buf, 18); if (ret && ret != -ERESTARTSYS) - printk(KERN_ERR "dvb-ttpci: av7110_diseqc_send error %d\n", ret); + pr_err("%s(): error %d\n", __func__, ret); return ret; } @@ -723,8 +715,7 @@ static int FlushText(struct av7110 *av7110) if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0) break; if (err) { - printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for BUFF1_BASE == 0\n", - __func__); + pr_err("%s(): timeout waiting for BUFF1_BASE == 0\n", __func__); mutex_unlock(&av7110->dcomlock); return -ETIMEDOUT; } @@ -750,8 +741,7 @@ static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, char *buf) if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0) break; if (ret) { - printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for BUFF1_BASE == 0\n", - __func__); + pr_err("%s(): timeout waiting for BUFF1_BASE == 0\n", __func__); mutex_unlock(&av7110->dcomlock); return -ETIMEDOUT; } @@ -764,8 +754,7 @@ static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, char *buf) if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) break; if (ret) { - printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for HANDSHAKE_REG\n", - __func__); + pr_err("%s(): timeout waiting for HANDSHAKE_REG\n", __func__); mutex_unlock(&av7110->dcomlock); return -ETIMEDOUT; } @@ -780,7 +769,7 @@ static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, char *buf) ret = __av7110_send_fw_cmd(av7110, cbuf, 5); mutex_unlock(&av7110->dcomlock); if (ret && ret != -ERESTARTSYS) - printk(KERN_ERR "dvb-ttpci: WriteText error %d\n", ret); + pr_err("%s(): error %d\n", __func__, ret); return ret; } @@ -839,8 +828,7 @@ static inline int WaitUntilBmpLoaded(struct av7110 *av7110) int ret = wait_event_timeout(av7110->bmpq, av7110->bmp_state != BMP_LOADING, 10 * HZ); if (ret == 0) { - printk("dvb-ttpci: warning: timeout waiting in LoadBitmap: %d, %d\n", - ret, av7110->bmp_state); + pr_warn("warning: timeout waiting in LoadBitmap: %d, %d\n", ret, av7110->bmp_state); av7110->bmp_state = BMP_NONE; return -ETIMEDOUT; } @@ -897,7 +885,7 @@ static inline int LoadBitmap(struct av7110 *av7110, } } av7110->bmplen += 1024; - dprintk(4, "av7110_fw_cmd: LoadBmp size %d\n", av7110->bmplen); + dprintk(4, "av7110_fw_cmd(): LoadBmp size %d\n", av7110->bmplen); ret = av7110_fw_cmd(av7110, COMTYPE_OSD, LoadBmp, 3, format, dx, dy); if (!ret) ret = WaitUntilBmpLoaded(av7110); @@ -918,7 +906,7 @@ static inline int ReleaseBitmap(struct av7110 *av7110) if (av7110->bmp_state != BMP_LOADED && FW_VERSION(av7110->arm_app) < 0x261e) return -1; if (av7110->bmp_state == BMP_LOADING) - dprintk(1,"ReleaseBitmap called while BMP_LOADING\n"); + dprintk(1, "%s called while BMP_LOADING\n", __func__); av7110->bmp_state = BMP_NONE; return av7110_fw_cmd(av7110, COMTYPE_OSD, ReleaseBmp, 0); } @@ -1033,7 +1021,7 @@ static int OSDSetBlock(struct av7110 *av7110, int x0, int y0, if (!rc) rc = release_rc; if (rc) - dprintk(1,"returns %d\n",rc); + dprintk(1, "returns %d\n", rc); return rc; } @@ -1180,9 +1168,9 @@ int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc) mutex_unlock(&av7110->osd_mutex); if (ret == -ERESTARTSYS) - dprintk(1, "av7110_osd_cmd(%d) returns with -ERESTARTSYS\n",dc->cmd); + dprintk(1, "%s(%d) returns with -ERESTARTSYS\n", __func__, dc->cmd); else if (ret) - dprintk(1, "av7110_osd_cmd(%d) returns with %d\n",dc->cmd,ret); + dprintk(1, "%s(%d) returns with %d\n", __func__, dc->cmd, ret); return ret; } diff --git a/drivers/staging/media/av7110/av7110_ir.c b/drivers/staging/media/av7110/av7110_ir.c index a851ba328e4a..68b3979ba5f2 100644 --- a/drivers/staging/media/av7110/av7110_ir.c +++ b/drivers/staging/media/av7110/av7110_ir.c @@ -59,8 +59,7 @@ void av7110_ir_handler(struct av7110 *av7110, u32 ircom) proto = RC_PROTO_RC5; break; default: - dprintk(2, "unknown ir config %d\n", - av7110->ir.ir_config); + dprintk(2, "unknown ir config %d\n", av7110->ir.ir_config); return; } diff --git a/drivers/staging/media/av7110/av7110_v4l.c b/drivers/staging/media/av7110/av7110_v4l.c index d13f1afec84d..04e659243f02 100644 --- a/drivers/staging/media/av7110/av7110_v4l.c +++ b/drivers/staging/media/av7110/av7110_v4l.c @@ -42,8 +42,7 @@ int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val) } if (i2c_transfer(&av7110->i2c_adap, &msgs, 1) != 1) { - dprintk(1, "dvb-ttpci: failed @ card %d, %u = %u\n", - av7110->dvb_adapter.num, reg, val); + dprintk(1, "failed @ card %d, %u = %u\n", av7110->dvb_adapter.num, reg, val); return -EIO; } return 0; @@ -72,8 +71,7 @@ static int msp_readreg(struct av7110 *av7110, u8 dev, u16 reg, u16 *val) } if (i2c_transfer(&av7110->i2c_adap, &msgs[0], 2) != 2) { - dprintk(1, "dvb-ttpci: failed @ card %d, %u\n", - av7110->dvb_adapter.num, reg); + dprintk(1, "failed @ card %d, %u\n", av7110->dvb_adapter.num, reg); return -EIO; } *val = (msg2[0] << 8) | msg2[1]; @@ -617,7 +615,7 @@ static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size struct v4l2_sliced_vbi_data d; int rc; - dprintk(2, "%s\n", __func__); + dprintk(2, "\n"); if (FW_VERSION(av7110->arm_app) < 0x2623 || !av7110->wssMode || count != sizeof(d)) return -EINVAL; if (copy_from_user(&d, data, count)) @@ -718,7 +716,7 @@ int av7110_init_analog_module(struct av7110 *av7110) msleep(100); // the probing above resets the msp... msp_readreg(av7110, MSP_RD_DSP, 0x001e, &version1); msp_readreg(av7110, MSP_RD_DSP, 0x001f, &version2); - dprintk(1, "dvb-ttpci: @ card %d MSP34xx version 0x%04x 0x%04x\n", + dprintk(1, "@ card %d MSP34xx version 0x%04x 0x%04x\n", av7110->dvb_adapter.num, version1, version2); msp_writereg(av7110, MSP_WR_DSP, 0x0013, 0x0c00); msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone diff --git a/drivers/staging/media/av7110/dvb_filter.c b/drivers/staging/media/av7110/dvb_filter.c index 71c1d9629e8d..9eafbb82bf42 100644 --- a/drivers/staging/media/av7110/dvb_filter.c +++ b/drivers/staging/media/av7110/dvb_filter.c @@ -39,8 +39,6 @@ int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int p if (!found) return -1; - if (pr) - printk(KERN_DEBUG "Audiostream: AC3"); ai->off = c; if (c + 5 >= count) @@ -52,21 +50,18 @@ int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int p frame = (headr[2] & 0x3f); ai->bit_rate = ac3_bitrates[frame >> 1] * 1000; - if (pr) - printk(KERN_CONT " BRate: %d kb/s", (int) ai->bit_rate/1000); - ai->frequency = (headr[2] & 0xc0) >> 6; fr = (headr[2] & 0xc0) >> 6; ai->frequency = freq[fr] * 100; - if (pr) - printk(KERN_CONT " Freq: %d Hz\n", (int) ai->frequency); ai->framesize = ac3_frames[fr][frame >> 1]; if ((frame & 1) && (fr == 1)) ai->framesize++; ai->framesize = ai->framesize << 1; + if (pr) - printk(KERN_DEBUG " Framesize %d\n", (int) ai->framesize); + pr_info("Audiostream: AC3, BRate: %d kb/s, Freq: %d Hz, Framesize %d\n", + (int)ai->bit_rate / 1000, (int)ai->frequency, (int)ai->framesize); return 0; } -- cgit v1.2.3 From 1aea3d1d4a21e3e7895663b848ffae79ee82e065 Mon Sep 17 00:00:00 2001 From: Stefan Herdler Date: Tue, 7 May 2024 02:25:00 +0200 Subject: media: av7110: coding style fixes: avoid_externs This patch fixes the following checkpatch warnings: CHECK:AVOID_EXTERNS: extern prototypes should be avoided in .h files Removing unnecessary 'extern' declaration of function prototypes in header files. Signed-off-by: Stefan Herdler Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.h | 16 ++++++------- drivers/staging/media/av7110/av7110_av.h | 36 ++++++++++++++--------------- drivers/staging/media/av7110/av7110_ca.h | 12 +++++----- drivers/staging/media/av7110/av7110_hw.h | 26 ++++++++++----------- drivers/staging/media/av7110/av7110_ipack.h | 12 +++++----- 5 files changed, 51 insertions(+), 51 deletions(-) diff --git a/drivers/staging/media/av7110/av7110.h b/drivers/staging/media/av7110/av7110.h index 403dbbba5b5b..ec461fd187af 100644 --- a/drivers/staging/media/av7110/av7110.h +++ b/drivers/staging/media/av7110/av7110.h @@ -284,8 +284,8 @@ struct av7110 { int (*fe_set_frontend)(struct dvb_frontend *fe); }; -extern int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, - u16 subpid, u16 pcrpid); +int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, + u16 subpid, u16 pcrpid); void av7110_ir_handler(struct av7110 *av7110, u32 ircom); int av7110_set_ir_config(struct av7110 *av7110); @@ -298,12 +298,12 @@ void av7110_ir_exit(struct av7110 *av7110); #define MSP_WR_DSP 0x12 #define MSP_RD_DSP 0x13 -extern int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val); -extern u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg); -extern int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val); +int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val); +u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg); +int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val); -extern int av7110_init_analog_module(struct av7110 *av7110); -extern int av7110_init_v4l(struct av7110 *av7110); -extern int av7110_exit_v4l(struct av7110 *av7110); +int av7110_init_analog_module(struct av7110 *av7110); +int av7110_init_v4l(struct av7110 *av7110); +int av7110_exit_v4l(struct av7110 *av7110); #endif /* _AV7110_H_ */ diff --git a/drivers/staging/media/av7110/av7110_av.h b/drivers/staging/media/av7110/av7110_av.h index f1c5c26c668b..eebaf59c7585 100644 --- a/drivers/staging/media/av7110/av7110_av.h +++ b/drivers/staging/media/av7110/av7110_av.h @@ -4,28 +4,28 @@ struct av7110; -extern int av7110_set_vidmode(struct av7110 *av7110, - enum av7110_video_mode mode); +int av7110_set_vidmode(struct av7110 *av7110, + enum av7110_video_mode mode); -extern int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len); -extern int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen); -extern int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len); +int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len); +int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen); +int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len); -extern int av7110_set_volume(struct av7110 *av7110, unsigned int volleft, - unsigned int volright); -extern int av7110_av_stop(struct av7110 *av7110, int av); -extern int av7110_av_start_record(struct av7110 *av7110, int av, - struct dvb_demux_feed *dvbdmxfeed); -extern int av7110_av_start_play(struct av7110 *av7110, int av); +int av7110_set_volume(struct av7110 *av7110, unsigned int volleft, + unsigned int volright); +int av7110_av_stop(struct av7110 *av7110, int av); +int av7110_av_start_record(struct av7110 *av7110, int av, + struct dvb_demux_feed *dvbdmxfeed); +int av7110_av_start_play(struct av7110 *av7110, int av); -extern void dvb_video_add_event(struct av7110 *av7110, struct video_event *event); +void dvb_video_add_event(struct av7110 *av7110, struct video_event *event); -extern void av7110_p2t_init(struct av7110_p2t *p, struct dvb_demux_feed *feed); -extern void av7110_p2t_write(u8 const *buf, long length, u16 pid, struct av7110_p2t *p); +void av7110_p2t_init(struct av7110_p2t *p, struct dvb_demux_feed *feed); +void av7110_p2t_write(u8 const *buf, long length, u16 pid, struct av7110_p2t *p); -extern int av7110_av_register(struct av7110 *av7110); -extern void av7110_av_unregister(struct av7110 *av7110); -extern int av7110_av_init(struct av7110 *av7110); -extern void av7110_av_exit(struct av7110 *av7110); +int av7110_av_register(struct av7110 *av7110); +void av7110_av_unregister(struct av7110 *av7110); +int av7110_av_init(struct av7110 *av7110); +void av7110_av_exit(struct av7110 *av7110); #endif /* _AV7110_AV_H_ */ diff --git a/drivers/staging/media/av7110/av7110_ca.h b/drivers/staging/media/av7110/av7110_ca.h index a6e3f2955730..d3521944b97c 100644 --- a/drivers/staging/media/av7110/av7110_ca.h +++ b/drivers/staging/media/av7110/av7110_ca.h @@ -4,12 +4,12 @@ struct av7110; -extern void CI_handle(struct av7110 *av7110, u8 *data, u16 len); -extern void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len); +void CI_handle(struct av7110 *av7110, u8 *data, u16 len); +void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len); -extern int av7110_ca_register(struct av7110 *av7110); -extern void av7110_ca_unregister(struct av7110 *av7110); -extern int av7110_ca_init(struct av7110* av7110); -extern void av7110_ca_exit(struct av7110* av7110); +int av7110_ca_register(struct av7110 *av7110); +void av7110_ca_unregister(struct av7110 *av7110); +int av7110_ca_init(struct av7110 *av7110); +void av7110_ca_exit(struct av7110 *av7110); #endif /* _AV7110_CA_H_ */ diff --git a/drivers/staging/media/av7110/av7110_hw.h b/drivers/staging/media/av7110/av7110_hw.h index c13b8c479eb7..d4579f411c56 100644 --- a/drivers/staging/media/av7110/av7110_hw.h +++ b/drivers/staging/media/av7110/av7110_hw.h @@ -357,22 +357,22 @@ enum av7110_command_type { #define DEBI_DONE_LINE 1 #define ARM_IRQ_LINE 0 -extern int av7110_bootarm(struct av7110 *av7110); -extern int av7110_firmversion(struct av7110 *av7110); +int av7110_bootarm(struct av7110 *av7110); +int av7110_firmversion(struct av7110 *av7110); #define FW_CI_LL_SUPPORT(arm_app) ((arm_app) & 0x80000000) #define FW_4M_SDRAM(arm_app) ((arm_app) & 0x40000000) #define FW_VERSION(arm_app) ((arm_app) & 0x0000FFFF) -extern int av7110_wait_msgstate(struct av7110 *av7110, u16 flags); -extern int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...); -extern int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, - int request_buf_len, u16 *reply_buf, int reply_buf_len); +int av7110_wait_msgstate(struct av7110 *av7110, u16 flags); +int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...); +int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, + int request_buf_len, u16 *reply_buf, int reply_buf_len); /* DEBI (saa7146 data extension bus interface) access */ -extern int av7110_debiwrite(struct av7110 *av7110, u32 config, - int addr, u32 val, unsigned int count); -extern u32 av7110_debiread(struct av7110 *av7110, u32 config, - int addr, unsigned int count); +int av7110_debiwrite(struct av7110 *av7110, u32 config, + int addr, u32 val, unsigned int count); +u32 av7110_debiread(struct av7110 *av7110, u32 config, + int addr, unsigned int count); /* DEBI during interrupt */ /* single word writes */ @@ -473,11 +473,11 @@ static inline int Set22K(struct av7110 *av7110, int state) return av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, (state ? ON22K : OFF22K), 0); } -extern int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst); +int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst); #ifdef CONFIG_DVB_AV7110_OSD -extern int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc); -extern int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap); +int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc); +int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap); #endif /* CONFIG_DVB_AV7110_OSD */ #endif /* _AV7110_HW_H_ */ diff --git a/drivers/staging/media/av7110/av7110_ipack.h b/drivers/staging/media/av7110/av7110_ipack.h index 943ec899bb93..55296421d52f 100644 --- a/drivers/staging/media/av7110/av7110_ipack.h +++ b/drivers/staging/media/av7110/av7110_ipack.h @@ -2,12 +2,12 @@ #ifndef _AV7110_IPACK_H_ #define _AV7110_IPACK_H_ -extern int av7110_ipack_init(struct ipack *p, int size, - void (*func)(u8 *buf, int size, void *priv)); +int av7110_ipack_init(struct ipack *p, int size, + void (*func)(u8 *buf, int size, void *priv)); -extern void av7110_ipack_reset(struct ipack *p); -extern int av7110_ipack_instant_repack(const u8 *buf, int count, struct ipack *p); -extern void av7110_ipack_free(struct ipack * p); -extern void av7110_ipack_flush(struct ipack *p); +void av7110_ipack_reset(struct ipack *p); +int av7110_ipack_instant_repack(const u8 *buf, int count, struct ipack *p); +void av7110_ipack_free(struct ipack *p); +void av7110_ipack_flush(struct ipack *p); #endif -- cgit v1.2.3 From 792196fd34b1811d1491000c76e73805f5f827ef Mon Sep 17 00:00:00 2001 From: Yunfei Dong Date: Sat, 16 Mar 2024 16:13:44 +0800 Subject: media: mediatek: vcodec: add decoder command to support stateless decoder The supported decoder commands are different for stateless and stateful architectures. Add stateless decoder commands to fix the v4l2-compliance test error below. Codec ioctls: VIDIOC_ENCODER_CMD returned -1 (Inappropriate ioctl for device) VIDIOC_TRY_ENCODER_CMD returned -1 (Inappropriate ioctl for device) test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported) VIDIOC_G_ENC_INDEX returned -1 (Inappropriate ioctl for device) test VIDIOC_G_ENC_INDEX: OK (Not Supported) VIDIOC_DECODER_CMD returned -1 (Invalid argument) VIDIOC_TRY_DECODER_CMD returned -1 (Invalid argument) VIDIOC_TRY_DECODER_CMD returned -1 (Invalid argument) fail: v4l2-test-codecs.cpp(126): ret test VIDIOC_(TRY_)DECODER_CMD: FAIL Signed-off-by: Yunfei Dong Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- .../mediatek/vcodec/decoder/mtk_vcodec_dec.c | 60 +++++++++++++++++++--- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c index 9107707de6c4..98838217b97d 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c @@ -80,21 +80,18 @@ static struct mtk_q_data *mtk_vdec_get_q_data(struct mtk_vcodec_dec_ctx *ctx, return &ctx->q_data[MTK_Q_DATA_DST]; } -static int vidioc_try_decoder_cmd(struct file *file, void *priv, - struct v4l2_decoder_cmd *cmd) +static int stateful_try_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) { return v4l2_m2m_ioctl_try_decoder_cmd(file, priv, cmd); } - -static int vidioc_decoder_cmd(struct file *file, void *priv, - struct v4l2_decoder_cmd *cmd) +static int stateful_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) { struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); struct vb2_queue *src_vq, *dst_vq; int ret; - ret = vidioc_try_decoder_cmd(file, priv, cmd); + ret = stateful_try_decoder_cmd(file, priv, cmd); if (ret) return ret; @@ -128,6 +125,57 @@ static int vidioc_decoder_cmd(struct file *file, void *priv, return 0; } +static int stateless_try_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) +{ + return v4l2_m2m_ioctl_stateless_try_decoder_cmd(file, priv, cmd); +} + +static int stateless_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) +{ + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); + int ret; + + ret = v4l2_m2m_ioctl_stateless_try_decoder_cmd(file, priv, cmd); + if (ret) + return ret; + + mtk_v4l2_vdec_dbg(3, ctx, "decoder cmd=%u", cmd->cmd); + switch (cmd->cmd) { + case V4L2_DEC_CMD_FLUSH: + /* + * If the flag of the output buffer is equals V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF, + * this command will prevent dequeueing the capture buffer containing the last + * decoded frame. Or do nothing + */ + break; + default: + mtk_v4l2_vdec_err(ctx, "invalid stateless decoder cmd=%u", cmd->cmd); + return -EINVAL; + } + + return 0; +} + +static int vidioc_try_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) +{ + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); + + if (ctx->dev->vdec_pdata->uses_stateless_api) + return stateless_try_decoder_cmd(file, priv, cmd); + + return stateful_try_decoder_cmd(file, priv, cmd); +} + +static int vidioc_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd) +{ + struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv); + + if (ctx->dev->vdec_pdata->uses_stateless_api) + return stateless_decoder_cmd(file, priv, cmd); + + return stateful_decoder_cmd(file, priv, cmd); +} + void mtk_vdec_unlock(struct mtk_vcodec_dec_ctx *ctx) { mutex_unlock(&ctx->dev->dec_mutex[ctx->hw_id]); -- cgit v1.2.3 From 59d438f8e02ca641c58d77e1feffa000ff809e9f Mon Sep 17 00:00:00 2001 From: Irui Wang Date: Thu, 21 Mar 2024 09:47:54 +0800 Subject: media: mediatek: vcodec: Handle invalid decoder vsi Handle an invalid decoder vsi in vpu_dec_init to ensure the decoder vsi is valid for future use. Fixes: 590577a4e525 ("[media] vcodec: mediatek: Add Mediatek V4L2 Video Decoder Driver") Signed-off-by: Irui Wang Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c index da6be556727b..145958206e38 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c @@ -233,6 +233,12 @@ int vpu_dec_init(struct vdec_vpu_inst *vpu) mtk_vdec_debug(vpu->ctx, "vdec_inst=%p", vpu); err = vcodec_vpu_send_msg(vpu, (void *)&msg, sizeof(msg)); + + if (IS_ERR_OR_NULL(vpu->vsi)) { + mtk_vdec_err(vpu->ctx, "invalid vdec vsi, status=%d", err); + return -EINVAL; + } + mtk_vdec_debug(vpu->ctx, "- ret=%d", err); return err; } -- cgit v1.2.3 From 48d85de244047eabe07c5040af12dfa736d61d6c Mon Sep 17 00:00:00 2001 From: Yunfei Dong Date: Fri, 12 Apr 2024 11:57:31 +0800 Subject: media: mediatek: vcodec: Fix unreasonable data conversion Both 'bs_dma' and 'dma_addr' are integers. No need to convert the type from dma_addr_t to uint64_t again. Fixes: d353c3c34af0 ("media: mediatek: vcodec: support 36 bits physical address") Signed-off-by: Yunfei Dong Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c index 4bc89c8644fe..5f848691cea4 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c @@ -449,7 +449,7 @@ static int vdec_vp8_decode(void *h_vdec, struct mtk_vcodec_mem *bs, inst->frm_cnt, y_fb_dma, c_fb_dma, fb); inst->cur_fb = fb; - dec->bs_dma = (uint64_t)bs->dma_addr; + dec->bs_dma = bs->dma_addr; dec->bs_sz = bs->size; dec->cur_y_fb_dma = y_fb_dma; dec->cur_c_fb_dma = c_fb_dma; -- cgit v1.2.3 From b785ea5b16c8b33bad45fdb5ce469d9236401dc8 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 22 Apr 2024 10:03:59 -0700 Subject: media: mediatek: vcodec: Alloc DMA memory with DMA_ATTR_ALLOC_SINGLE_PAGES As talked about in commit 14d3ae2efeed ("ARM: 8507/1: dma-mapping: Use DMA_ATTR_ALLOC_SINGLE_PAGES hint to optimize alloc"), it doesn't really make sense to try to allocate contiguous chunks of memory for video encoding/decoding. Let's switch the Mediatek vcodec driver to pass DMA_ATTR_ALLOC_SINGLE_PAGES and take some of the stress off the memory subsystem. Signed-off-by: Douglas Anderson Tested-by: Fei Shao Reviewed-by: Fei Shao Reviewed-by: Nicolas Dufresne Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c index c60e4c193b25..fc4e34c29192 100644 --- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c +++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c @@ -63,7 +63,8 @@ int mtk_vcodec_mem_alloc(void *priv, struct mtk_vcodec_mem *mem) id = dec_ctx->id; } - mem->va = dma_alloc_coherent(&plat_dev->dev, mem->size, &mem->dma_addr, GFP_KERNEL); + mem->va = dma_alloc_attrs(&plat_dev->dev, mem->size, &mem->dma_addr, + GFP_KERNEL, DMA_ATTR_ALLOC_SINGLE_PAGES); if (!mem->va) { mtk_v4l2_err(plat_dev, "%s dma_alloc size=0x%zx failed!", __func__, mem->size); -- cgit v1.2.3 From 62096c48394b49e326056a62780fb67b60edde91 Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Mon, 6 May 2024 17:49:16 +0900 Subject: media: v4l2-ctrls: Add average QP control Add a control V4L2_CID_MPEG_VIDEO_AVERAGE_QP to report the average QP value of the current encoded frame. The value applies to the last dequeued capture buffer. Signed-off-by: Ming Qian Reviewed-by: Hans Verkuil Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst | 14 ++++++++++++++ drivers/media/v4l2-core/v4l2-ctrls-defs.c | 5 +++++ include/uapi/linux/v4l2-controls.h | 2 ++ 3 files changed, 21 insertions(+) diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst index 2a165ae063fb..4a379bd9e3fb 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst @@ -1653,6 +1653,20 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type - Quantization parameter for a P frame for FWHT. Valid range: from 1 to 31. +``V4L2_CID_MPEG_VIDEO_AVERAGE_QP (integer)`` + This read-only control returns the average QP value of the currently + encoded frame. The value applies to the last dequeued capture buffer + (VIDIOC_DQBUF). Its valid range depends on the encoding format and parameters. + For H264, its valid range is from 0 to 51. + For HEVC, its valid range is from 0 to 51 for 8 bit and + from 0 to 63 for 10 bit. + For H263 and MPEG4, its valid range is from 1 to 31. + For VP8, its valid range is from 0 to 127. + For VP9, its valid range is from 0 to 255. + If the codec's MIN_QP and MAX_QP are set, then the QP will meet both requirements. + Codecs need to always use the specified range, rather then a HW custom range. + Applicable to encoders + .. raw:: latex \normalsize diff --git a/drivers/media/v4l2-core/v4l2-ctrls-defs.c b/drivers/media/v4l2-core/v4l2-ctrls-defs.c index 8696eb1cdd61..1ea52011247a 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-defs.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-defs.c @@ -970,6 +970,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_LTR_COUNT: return "LTR Count"; case V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX: return "Frame LTR Index"; case V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES: return "Use LTR Frames"; + case V4L2_CID_MPEG_VIDEO_AVERAGE_QP: return "Average QP Value"; case V4L2_CID_FWHT_I_FRAME_QP: return "FWHT I-Frame QP Value"; case V4L2_CID_FWHT_P_FRAME_QP: return "FWHT P-Frame QP Value"; @@ -1507,6 +1508,10 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *max = 0xffffffffffffLL; *step = 1; break; + case V4L2_CID_MPEG_VIDEO_AVERAGE_QP: + *type = V4L2_CTRL_TYPE_INTEGER; + *flags |= V4L2_CTRL_FLAG_READ_ONLY; + break; case V4L2_CID_PIXEL_RATE: *type = V4L2_CTRL_TYPE_INTEGER64; *flags |= V4L2_CTRL_FLAG_READ_ONLY; diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 99c3f5e99da7..974fd254e573 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -898,6 +898,8 @@ enum v4l2_mpeg_video_av1_level { V4L2_MPEG_VIDEO_AV1_LEVEL_7_3 = 23 }; +#define V4L2_CID_MPEG_VIDEO_AVERAGE_QP (V4L2_CID_CODEC_BASE + 657) + /* MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */ #define V4L2_CID_CODEC_CX2341X_BASE (V4L2_CTRL_CLASS_CODEC | 0x1000) #define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE (V4L2_CID_CODEC_CX2341X_BASE+0) -- cgit v1.2.3 From 065927b51eb1f042c3e026cebfd55e72ccc26093 Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Mon, 6 May 2024 17:49:17 +0900 Subject: media: amphion: Remove lock in s_ctrl callback There is no need to add a lock in s_ctrl callback, it has been synchronized by the ctrl_handler's lock, otherwise it may led to a deadlock if the driver calls v4l2_ctrl_s_ctrl(). Signed-off-by: Ming Qian Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/amphion/vdec.c | 2 -- drivers/media/platform/amphion/venc.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c index a57f9f4f3b87..6a38a0fa0e2d 100644 --- a/drivers/media/platform/amphion/vdec.c +++ b/drivers/media/platform/amphion/vdec.c @@ -195,7 +195,6 @@ static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) struct vdec_t *vdec = inst->priv; int ret = 0; - vpu_inst_lock(inst); switch (ctrl->id) { case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE: vdec->params.display_delay_enable = ctrl->val; @@ -207,7 +206,6 @@ static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) ret = -EINVAL; break; } - vpu_inst_unlock(inst); return ret; } diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c index 4eb57d793a9c..16ed4d21519c 100644 --- a/drivers/media/platform/amphion/venc.c +++ b/drivers/media/platform/amphion/venc.c @@ -518,7 +518,6 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) struct venc_t *venc = inst->priv; int ret = 0; - vpu_inst_lock(inst); switch (ctrl->id) { case V4L2_CID_MPEG_VIDEO_H264_PROFILE: venc->params.profile = ctrl->val; @@ -579,7 +578,6 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) ret = -EINVAL; break; } - vpu_inst_unlock(inst); return ret; } -- cgit v1.2.3 From 1034ead1a7333e65e516c583f757717f461eba43 Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Mon, 6 May 2024 17:49:18 +0900 Subject: media: amphion: Report the average QP of current encoded frame Report the average QP value of the current encoded frame via the V4L2_CID_MPEG_VIDEO_AVERAGE_QP control, the value applies to the last dequeued capture buffer. Signed-off-by: Ming Qian Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/amphion/venc.c | 4 ++++ drivers/media/platform/amphion/vpu.h | 1 + drivers/media/platform/amphion/vpu_defs.h | 1 + drivers/media/platform/amphion/vpu_v4l2.c | 16 ++++++++++++++++ drivers/media/platform/amphion/vpu_v4l2.h | 1 + drivers/media/platform/amphion/vpu_windsor.c | 2 ++ 6 files changed, 25 insertions(+) diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c index 16ed4d21519c..351b4edc8742 100644 --- a/drivers/media/platform/amphion/venc.c +++ b/drivers/media/platform/amphion/venc.c @@ -678,6 +678,9 @@ static int venc_ctrl_init(struct vpu_inst *inst) ~(1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME), V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME); + v4l2_ctrl_new_std(&inst->ctrl_handler, NULL, + V4L2_CID_MPEG_VIDEO_AVERAGE_QP, 0, 51, 1, 0); + if (inst->ctrl_handler.error) { ret = inst->ctrl_handler.error; v4l2_ctrl_handler_free(&inst->ctrl_handler); @@ -817,6 +820,7 @@ static int venc_get_one_encoded_frame(struct vpu_inst *inst, vbuf->field = inst->cap_format.field; vbuf->flags |= frame->info.pic_type; vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE); + vpu_set_buffer_average_qp(vbuf, frame->info.average_qp); dev_dbg(inst->dev, "[%d][OUTPUT TS]%32lld\n", inst->id, vbuf->vb2_buf.timestamp); v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE); venc->ready_count++; diff --git a/drivers/media/platform/amphion/vpu.h b/drivers/media/platform/amphion/vpu.h index 0246cf0ac3a8..22f0da26ccec 100644 --- a/drivers/media/platform/amphion/vpu.h +++ b/drivers/media/platform/amphion/vpu.h @@ -306,6 +306,7 @@ struct vpu_vb2_buffer { dma_addr_t chroma_v; unsigned int state; u32 tag; + u32 average_qp; }; void vpu_writel(struct vpu_dev *vpu, u32 reg, u32 val); diff --git a/drivers/media/platform/amphion/vpu_defs.h b/drivers/media/platform/amphion/vpu_defs.h index 7320852668d6..428d988cf2f7 100644 --- a/drivers/media/platform/amphion/vpu_defs.h +++ b/drivers/media/platform/amphion/vpu_defs.h @@ -114,6 +114,7 @@ struct vpu_enc_pic_info { u32 wptr; u32 crc; s64 timestamp; + u32 average_qp; }; struct vpu_dec_codec_info { diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c index c88738e8fff7..83db57bc80b7 100644 --- a/drivers/media/platform/amphion/vpu_v4l2.c +++ b/drivers/media/platform/amphion/vpu_v4l2.c @@ -63,6 +63,13 @@ unsigned int vpu_get_buffer_state(struct vb2_v4l2_buffer *vbuf) return vpu_buf->state; } +void vpu_set_buffer_average_qp(struct vb2_v4l2_buffer *vbuf, u32 qp) +{ + struct vpu_vb2_buffer *vpu_buf = to_vpu_vb2_buffer(vbuf); + + vpu_buf->average_qp = qp; +} + void vpu_v4l2_set_error(struct vpu_inst *inst) { vpu_inst_lock(inst); @@ -539,6 +546,15 @@ static void vpu_vb2_buf_finish(struct vb2_buffer *vb) struct vpu_inst *inst = vb2_get_drv_priv(vb->vb2_queue); struct vb2_queue *q = vb->vb2_queue; + if (V4L2_TYPE_IS_CAPTURE(vb->type)) { + struct vpu_vb2_buffer *vpu_buf = to_vpu_vb2_buffer(vbuf); + struct v4l2_ctrl *ctrl = v4l2_ctrl_find(&inst->ctrl_handler, + V4L2_CID_MPEG_VIDEO_AVERAGE_QP); + + if (ctrl) + v4l2_ctrl_s_ctrl(ctrl, vpu_buf->average_qp); + } + if (vbuf->flags & V4L2_BUF_FLAG_LAST) vpu_notify_eos(inst); diff --git a/drivers/media/platform/amphion/vpu_v4l2.h b/drivers/media/platform/amphion/vpu_v4l2.h index 60f43056a7a2..56f2939fa84d 100644 --- a/drivers/media/platform/amphion/vpu_v4l2.h +++ b/drivers/media/platform/amphion/vpu_v4l2.h @@ -12,6 +12,7 @@ void vpu_inst_lock(struct vpu_inst *inst); void vpu_inst_unlock(struct vpu_inst *inst); void vpu_set_buffer_state(struct vb2_v4l2_buffer *vbuf, unsigned int state); unsigned int vpu_get_buffer_state(struct vb2_v4l2_buffer *vbuf); +void vpu_set_buffer_average_qp(struct vb2_v4l2_buffer *vbuf, u32 qp); int vpu_v4l2_open(struct file *file, struct vpu_inst *inst); int vpu_v4l2_close(struct file *file); diff --git a/drivers/media/platform/amphion/vpu_windsor.c b/drivers/media/platform/amphion/vpu_windsor.c index 5f1101d7cf9e..e7d37aa4b826 100644 --- a/drivers/media/platform/amphion/vpu_windsor.c +++ b/drivers/media/platform/amphion/vpu_windsor.c @@ -499,6 +499,7 @@ struct windsor_pic_info { u32 proc_dacc_rng_wr_cnt; s32 tv_s; u32 tv_ns; + u32 average_qp; }; u32 vpu_windsor_get_data_size(void) @@ -734,6 +735,7 @@ static void vpu_windsor_unpack_pic_info(struct vpu_rpc_event *pkt, void *data) info->wptr = get_ptr(windsor->str_buff_wptr); info->crc = windsor->frame_crc; info->timestamp = timespec64_to_ns(&ts); + info->average_qp = windsor->average_qp; } static void vpu_windsor_unpack_mem_req(struct vpu_rpc_event *pkt, void *data) -- cgit v1.2.3 From dce30a66120fa092d28e2741f0890a5738b32fda Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Fri, 9 Feb 2024 09:56:16 +0100 Subject: dt-bindings: media: convert Mediatek consumer IR to the json-schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This helps validating DTS files. Introduced changes: 1. Reworded title 2. Made "bus" clock required on MT7623 as well 3. Added required #include-s and adjusted "reg" & clocks in example Signed-off-by: Rafał Miłecki Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Conor Dooley Signed-off-by: Sean Young Signed-off-by: Hans Verkuil --- .../bindings/media/mediatek,mt7622-cir.yaml | 55 ++++++++++++++++++++++ .../devicetree/bindings/media/mtk-cir.txt | 28 ----------- 2 files changed, 55 insertions(+), 28 deletions(-) create mode 100644 Documentation/devicetree/bindings/media/mediatek,mt7622-cir.yaml delete mode 100644 Documentation/devicetree/bindings/media/mtk-cir.txt diff --git a/Documentation/devicetree/bindings/media/mediatek,mt7622-cir.yaml b/Documentation/devicetree/bindings/media/mediatek,mt7622-cir.yaml new file mode 100644 index 000000000000..c01210e053f9 --- /dev/null +++ b/Documentation/devicetree/bindings/media/mediatek,mt7622-cir.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/mediatek,mt7622-cir.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek Consumer Infrared Receiver on-SoC Controller + +maintainers: + - Sean Wang + +allOf: + - $ref: rc.yaml# + +properties: + compatible: + enum: + - mediatek,mt7622-cir + - mediatek,mt7623-cir + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: clk + - const: bus + +required: + - reg + - interrupts + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include + #include + + ir@10013000 { + compatible = "mediatek,mt7623-cir"; + reg = <0x10013000 0x1000>; + interrupts = ; + clocks = <&infracfg CLK_INFRA_IRRX>, <&topckgen CLK_TOP_AXI_SEL>; + clock-names = "clk", "bus"; + linux,rc-map-name = "rc-rc6-mce"; + }; diff --git a/Documentation/devicetree/bindings/media/mtk-cir.txt b/Documentation/devicetree/bindings/media/mtk-cir.txt deleted file mode 100644 index 5e18087ce11f..000000000000 --- a/Documentation/devicetree/bindings/media/mtk-cir.txt +++ /dev/null @@ -1,28 +0,0 @@ -Device-Tree bindings for Mediatek consumer IR controller -found in Mediatek SoC family - -Required properties: -- compatible : Should be - "mediatek,mt7623-cir": for MT7623 SoC - "mediatek,mt7622-cir": for MT7622 SoC -- clocks : list of clock specifiers, corresponding to - entries in clock-names property; -- clock-names : should contain - - "clk" entries: for MT7623 SoC - - "clk", "bus" entries: for MT7622 SoC -- interrupts : should contain IR IRQ number; -- reg : should contain IO map address for IR. - -Optional properties: -- linux,rc-map-name : see rc.txt file in the same directory. - -Example: - -cir: cir@10013000 { - compatible = "mediatek,mt7623-cir"; - reg = <0 0x10013000 0 0x1000>; - interrupts = ; - clocks = <&infracfg CLK_INFRA_IRRX>; - clock-names = "clk"; - linux,rc-map-name = "rc-rc6-mce"; -}; -- cgit v1.2.3 From 156922faabcef2979cb2ddc2fbaa659b5ea37f54 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Tue, 11 Jun 2024 10:34:06 -0700 Subject: media: atomisp: Switch to new Intel CPU model defines New CPU #defines encode vendor and family as well as model. Signed-off-by: Tony Luck Reviewed-by: Andy Shevchenko Acked-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- .../media/atomisp/include/linux/atomisp_platform.h | 27 +++++++++------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/drivers/staging/media/atomisp/include/linux/atomisp_platform.h b/drivers/staging/media/atomisp/include/linux/atomisp_platform.h index 0e3f6fb78483..fdeb247036b0 100644 --- a/drivers/staging/media/atomisp/include/linux/atomisp_platform.h +++ b/drivers/staging/media/atomisp/include/linux/atomisp_platform.h @@ -18,7 +18,7 @@ #ifndef ATOMISP_PLATFORM_H_ #define ATOMISP_PLATFORM_H_ -#include +#include #include #include @@ -178,22 +178,17 @@ void atomisp_unregister_subdev(struct v4l2_subdev *subdev); int v4l2_get_acpi_sensor_info(struct device *dev, char **module_id_str); /* API from old platform_camera.h, new CPUID implementation */ -#define __IS_SOC(x) (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && \ - boot_cpu_data.x86 == 6 && \ - boot_cpu_data.x86_model == (x)) -#define __IS_SOCS(x,y) (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && \ - boot_cpu_data.x86 == 6 && \ - (boot_cpu_data.x86_model == (x) || \ - boot_cpu_data.x86_model == (y))) - -#define IS_MFLD __IS_SOC(INTEL_FAM6_ATOM_SALTWELL_MID) -#define IS_BYT __IS_SOC(INTEL_FAM6_ATOM_SILVERMONT) -#define IS_CHT __IS_SOC(INTEL_FAM6_ATOM_AIRMONT) -#define IS_MRFD __IS_SOC(INTEL_FAM6_ATOM_SILVERMONT_MID) -#define IS_MOFD __IS_SOC(INTEL_FAM6_ATOM_AIRMONT_MID) +#define __IS_SOC(x) (boot_cpu_data.x86_vfm == x) +#define __IS_SOCS(x, y) (boot_cpu_data.x86_vfm == x || boot_cpu_data.x86_vfm == y) + +#define IS_MFLD __IS_SOC(INTEL_ATOM_SALTWELL_MID) +#define IS_BYT __IS_SOC(INTEL_ATOM_SILVERMONT) +#define IS_CHT __IS_SOC(INTEL_ATOM_AIRMONT) +#define IS_MRFD __IS_SOC(INTEL_ATOM_SILVERMONT_MID) +#define IS_MOFD __IS_SOC(INTEL_ATOM_AIRMONT_MID) /* Both CHT and MOFD come with ISP2401 */ -#define IS_ISP2401 __IS_SOCS(INTEL_FAM6_ATOM_AIRMONT, \ - INTEL_FAM6_ATOM_AIRMONT_MID) +#define IS_ISP2401 __IS_SOCS(INTEL_ATOM_AIRMONT, \ + INTEL_ATOM_AIRMONT_MID) #endif /* ATOMISP_PLATFORM_H_ */ -- cgit v1.2.3 From 5bc4a0132d7acf0cad739342584a249f748a6c61 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 13 Jun 2024 17:19:08 +0200 Subject: media: imx283: drop CENTERED_RECTANGLE due to clang failure The CENTERED_RECTANGLE define fails to compile on clang and old gcc versions. Just drop it and fill in the crop rectangles explicitly. Signed-off-by: Hans Verkuil Reviewed-by: Kieran Bingham Acked-by: Sakari Ailus --- drivers/media/i2c/imx283.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/media/i2c/imx283.c b/drivers/media/i2c/imx283.c index 6428eb5394a9..8490618c5071 100644 --- a/drivers/media/i2c/imx283.c +++ b/drivers/media/i2c/imx283.c @@ -407,14 +407,6 @@ static const struct imx283_reg_list link_freq_reglist[] = { }, }; -#define CENTERED_RECTANGLE(rect, _width, _height) \ - { \ - .left = rect.left + ((rect.width - (_width)) / 2), \ - .top = rect.top + ((rect.height - (_height)) / 2), \ - .width = (_width), \ - .height = (_height), \ - } - /* Mode configs */ static const struct imx283_mode supported_modes_12bit[] = { { @@ -440,7 +432,12 @@ static const struct imx283_mode supported_modes_12bit[] = { .min_shr = 11, .horizontal_ob = 96, .vertical_ob = 16, - .crop = CENTERED_RECTANGLE(imx283_active_area, 5472, 3648), + .crop = { + .top = 40, + .left = 108, + .width = 5472, + .height = 3648, + }, }, { /* @@ -468,7 +465,12 @@ static const struct imx283_mode supported_modes_12bit[] = { .horizontal_ob = 48, .vertical_ob = 4, - .crop = CENTERED_RECTANGLE(imx283_active_area, 5472, 3648), + .crop = { + .top = 40, + .left = 108, + .width = 5472, + .height = 3648, + }, }, }; @@ -489,7 +491,12 @@ static const struct imx283_mode supported_modes_10bit[] = { .min_shr = 10, .horizontal_ob = 96, .vertical_ob = 16, - .crop = CENTERED_RECTANGLE(imx283_active_area, 5472, 3648), + .crop = { + .top = 40, + .left = 108, + .width = 5472, + .height = 3648, + }, }, }; -- cgit v1.2.3 From 9d8683b3fd93f0e378f24dc3d9604e5d7d3e0a17 Mon Sep 17 00:00:00 2001 From: Aleksandr Burakov Date: Fri, 16 Feb 2024 15:40:06 +0300 Subject: saa7134: Unchecked i2c_transfer function result fixed Return value of function 'i2c_transfer' is not checked that may cause undefined behaviour. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 2cf36ac44730 ("[PATCH] v4l: 656: added support for the following cards") Signed-off-by: Aleksandr Burakov Signed-off-by: Hans Verkuil --- drivers/media/pci/saa7134/saa7134-dvb.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/saa7134/saa7134-dvb.c b/drivers/media/pci/saa7134/saa7134-dvb.c index 9c6cfef03331..a66df6adfaad 100644 --- a/drivers/media/pci/saa7134/saa7134-dvb.c +++ b/drivers/media/pci/saa7134/saa7134-dvb.c @@ -466,7 +466,9 @@ static int philips_europa_tuner_sleep(struct dvb_frontend *fe) /* switch the board to analog mode */ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(&dev->i2c_adap, &analog_msg, 1); + if (i2c_transfer(&dev->i2c_adap, &analog_msg, 1) != 1) + return -EIO; + return 0; } @@ -1018,7 +1020,9 @@ static int md8800_set_voltage2(struct dvb_frontend *fe, else wbuf[1] = rbuf & 0xef; msg[0].len = 2; - i2c_transfer(&dev->i2c_adap, msg, 1); + if (i2c_transfer(&dev->i2c_adap, msg, 1) != 1) + return -EIO; + return 0; } -- cgit v1.2.3 From f2ac2f36c938fd64c6e9a269508045f798923f8f Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Sun, 9 Jun 2024 14:18:55 -0700 Subject: media: marvell: add missing MODULE_DESCRIPTION() macros make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/platform/marvell/mcam-core.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/platform/marvell/mmp_camera.o Add the missing invocations of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Signed-off-by: Hans Verkuil --- drivers/media/platform/marvell/mcam-core.c | 1 + drivers/media/platform/marvell/mmp-driver.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/media/platform/marvell/mcam-core.c b/drivers/media/platform/marvell/mcam-core.c index 66688b4aece5..9dfaea56a46d 100644 --- a/drivers/media/platform/marvell/mcam-core.c +++ b/drivers/media/platform/marvell/mcam-core.c @@ -1981,5 +1981,6 @@ int mccic_resume(struct mcam_camera *cam) } EXPORT_SYMBOL_GPL(mccic_resume); +MODULE_DESCRIPTION("Marvell camera core driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Jonathan Corbet "); diff --git a/drivers/media/platform/marvell/mmp-driver.c b/drivers/media/platform/marvell/mmp-driver.c index 170907cc1885..ff9d151121d5 100644 --- a/drivers/media/platform/marvell/mmp-driver.c +++ b/drivers/media/platform/marvell/mmp-driver.c @@ -30,6 +30,7 @@ MODULE_ALIAS("platform:mmp-camera"); MODULE_AUTHOR("Jonathan Corbet "); +MODULE_DESCRIPTION("Support for the camera device found on Marvell MMP processors"); MODULE_LICENSE("GPL"); static char *mcam_clks[] = {"axi", "func", "phy"}; -- cgit v1.2.3 From c22010cee935a433359a9671f402792528ba21c8 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Sun, 9 Jun 2024 14:58:06 -0700 Subject: media: saa7134: add missing MODULE_DESCRIPTION() macros make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/pci/saa7134/saa7134-empress.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/pci/saa7134/saa7134-go7007.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/pci/saa7134/saa7134-alsa.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/pci/saa7134/saa7134-dvb.o Add the missing invocations of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Signed-off-by: Hans Verkuil --- drivers/media/pci/saa7134/saa7134-alsa.c | 1 + drivers/media/pci/saa7134/saa7134-dvb.c | 1 + drivers/media/pci/saa7134/saa7134-empress.c | 1 + drivers/media/pci/saa7134/saa7134-go7007.c | 1 + 4 files changed, 4 insertions(+) diff --git a/drivers/media/pci/saa7134/saa7134-alsa.c b/drivers/media/pci/saa7134/saa7134-alsa.c index dd2236c5c4a1..f86a44dfe6e3 100644 --- a/drivers/media/pci/saa7134/saa7134-alsa.c +++ b/drivers/media/pci/saa7134/saa7134-alsa.c @@ -1254,5 +1254,6 @@ static void saa7134_alsa_exit(void) /* We initialize this late, to make sure the sound system is up and running */ late_initcall(saa7134_alsa_init); module_exit(saa7134_alsa_exit); +MODULE_DESCRIPTION("Philips SAA7134 DMA audio support"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ricardo Cerqueira"); diff --git a/drivers/media/pci/saa7134/saa7134-dvb.c b/drivers/media/pci/saa7134/saa7134-dvb.c index a66df6adfaad..6de8a02314af 100644 --- a/drivers/media/pci/saa7134/saa7134-dvb.c +++ b/drivers/media/pci/saa7134/saa7134-dvb.c @@ -52,6 +52,7 @@ #include "s5h1411.h" MODULE_AUTHOR("Gerd Knorr [SuSE Labs]"); +MODULE_DESCRIPTION("DVB/ATSC Support for saa7134 based TV cards"); MODULE_LICENSE("GPL"); static unsigned int antenna_pwr; diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c index 434fa1ee1c33..bbf480ab31ca 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -19,6 +19,7 @@ /* ------------------------------------------------------------------ */ MODULE_AUTHOR("Gerd Knorr [SuSE Labs]"); +MODULE_DESCRIPTION("Philips SAA7134 empress support"); MODULE_LICENSE("GPL"); static unsigned int empress_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; diff --git a/drivers/media/pci/saa7134/saa7134-go7007.c b/drivers/media/pci/saa7134/saa7134-go7007.c index da83893ffee9..bd37db5ce363 100644 --- a/drivers/media/pci/saa7134/saa7134-go7007.c +++ b/drivers/media/pci/saa7134/saa7134-go7007.c @@ -516,4 +516,5 @@ static void __exit saa7134_go7007_mod_cleanup(void) module_init(saa7134_go7007_mod_init); module_exit(saa7134_go7007_mod_cleanup); +MODULE_DESCRIPTION("go7007 support for saa7134 based TV cards"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 3544c75d930a0c13bbb1721307564325327a2421 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Mon, 10 Jun 2024 13:08:36 -0700 Subject: media: go7007: add missing MODULE_DESCRIPTION() macros make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/usb/go7007/go7007.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/usb/go7007/go7007-usb.o Add the missing invocations of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Signed-off-by: Hans Verkuil --- drivers/media/usb/go7007/go7007-driver.c | 1 + drivers/media/usb/go7007/go7007-usb.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/media/usb/go7007/go7007-driver.c b/drivers/media/usb/go7007/go7007-driver.c index eb03f98b2ef1..468406302cd5 100644 --- a/drivers/media/usb/go7007/go7007-driver.c +++ b/drivers/media/usb/go7007/go7007-driver.c @@ -736,4 +736,5 @@ void go7007_update_board(struct go7007 *go) } EXPORT_SYMBOL(go7007_update_board); +MODULE_DESCRIPTION("WIS GO7007 MPEG encoder support"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/usb/go7007/go7007-usb.c b/drivers/media/usb/go7007/go7007-usb.c index 762c13e49bfa..334cdde81a5c 100644 --- a/drivers/media/usb/go7007/go7007-usb.c +++ b/drivers/media/usb/go7007/go7007-usb.c @@ -1352,4 +1352,5 @@ static struct usb_driver go7007_usb_driver = { }; module_usb_driver(go7007_usb_driver); +MODULE_DESCRIPTION("WIS GO7007 USB support"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 29abda17fc86bad96163f1f5d26cae42831ecf03 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Tue, 11 Jun 2024 17:14:30 -0700 Subject: media: pci: add missing MODULE_DESCRIPTION() macros With ARCH=x86, make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/pci/ttpci/budget-core.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/pci/bt8xx/bt878.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/pci/ivtv/ivtvfb.o Add the missing invocations of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Signed-off-by: Hans Verkuil --- drivers/media/pci/bt8xx/bt878.c | 1 + drivers/media/pci/ivtv/ivtvfb.c | 1 + drivers/media/pci/ttpci/budget-core.c | 1 + 3 files changed, 3 insertions(+) diff --git a/drivers/media/pci/bt8xx/bt878.c b/drivers/media/pci/bt8xx/bt878.c index 90972d6952f1..5c9ba4bfc1be 100644 --- a/drivers/media/pci/bt8xx/bt878.c +++ b/drivers/media/pci/bt8xx/bt878.c @@ -563,4 +563,5 @@ static void __exit bt878_cleanup_module(void) module_init(bt878_init_module); module_exit(bt878_cleanup_module); +MODULE_DESCRIPTION("DVB/ATSC Support for bt878 based TV cards"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/pci/ivtv/ivtvfb.c b/drivers/media/pci/ivtv/ivtvfb.c index d1ab7fee0d05..90c584cf97c2 100644 --- a/drivers/media/pci/ivtv/ivtvfb.c +++ b/drivers/media/pci/ivtv/ivtvfb.c @@ -104,6 +104,7 @@ MODULE_PARM_DESC(osd_xres, "\t\t\tdefault 640"); MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil, John Harvey, Ian Armstrong"); +MODULE_DESCRIPTION("Conexant cx23415 framebuffer support"); MODULE_LICENSE("GPL"); /* --------------------------------------------------------------------- */ diff --git a/drivers/media/pci/ttpci/budget-core.c b/drivers/media/pci/ttpci/budget-core.c index d33adeca196f..6d41fc997655 100644 --- a/drivers/media/pci/ttpci/budget-core.c +++ b/drivers/media/pci/ttpci/budget-core.c @@ -602,4 +602,5 @@ void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port) } EXPORT_SYMBOL_GPL(ttpci_budget_set_video_port); +MODULE_DESCRIPTION("base driver for the SAA7146 based Budget DVB cards"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 92b8d9bfc338150f3061cd6fca92716d3817e9a8 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Wed, 12 Jun 2024 10:32:07 -0700 Subject: media: si470x: add missing MODULE_DESCRIPTION() macro With ARCH=x86, make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/radio/si470x/radio-si470x-common.o Add the missing invocation of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Signed-off-by: Hans Verkuil --- drivers/media/radio/si470x/radio-si470x-common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c index dc0c1d8d23f0..af0a70910099 100644 --- a/drivers/media/radio/si470x/radio-si470x-common.c +++ b/drivers/media/radio/si470x/radio-si470x-common.c @@ -790,4 +790,5 @@ const struct video_device si470x_viddev_template = { }; EXPORT_SYMBOL_GPL(si470x_viddev_template); +MODULE_DESCRIPTION("Core radio driver for Si470x FM Radio Receivers"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From d41b69a00c1b52dcab479e5acce84443758fcca6 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Wed, 12 Jun 2024 12:47:29 -0700 Subject: media: tda9887: add missing MODULE_DESCRIPTION() macro With ARCH=x86, make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/tuners/tda9887.o Add the missing invocation of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Signed-off-by: Hans Verkuil --- drivers/media/tuners/tda9887.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/tuners/tda9887.c b/drivers/media/tuners/tda9887.c index dca2fc776e44..b2f7054c1832 100644 --- a/drivers/media/tuners/tda9887.c +++ b/drivers/media/tuners/tda9887.c @@ -707,4 +707,5 @@ struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe, } EXPORT_SYMBOL_GPL(tda9887_attach); +MODULE_DESCRIPTION("NXP TDA9885/6/7 analog IF demodulator driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From abb7a73b687dc82202f0203a112ef435fb598aa6 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 6 Jun 2024 13:01:07 +0000 Subject: media: c8sectpfe: Add missing parameter names clang 19 complains about the missing parameter name. Let's add it. drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h:19:62: warning: omitting the parameter name in a function definition is a C23 extension [-Wc23-extensions] Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202406050908.1kL1C69p-lkp@intel.com/ Fixes: e22b4973ee20 ("media: c8sectpfe: Do not depend on DEBUG_FS") Signed-off-by: Ricardo Ribalda Reviewed-by: Nathan Chancellor Signed-off-by: Hans Verkuil --- drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h index 8e1bfd860524..3fe177b59b16 100644 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h @@ -16,8 +16,8 @@ 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 *) {}; +static inline void c8sectpfe_debugfs_init(struct c8sectpfei *fei) {}; +static inline void c8sectpfe_debugfs_exit(struct c8sectpfei *fei) {}; #endif #endif /* __C8SECTPFE_DEBUG_H */ -- cgit v1.2.3 From 461d6ee65d7cfbbf7ce1b9fdaaae041a5135f1cc Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 6 Jun 2024 13:16:35 +0000 Subject: media: dvb-frontend/mxl5xx: Refactor struct MBIN_FILE_T and MBIN_SEGMENT_T Replace the older style one-element array with a flexible array member. There does not seem to be any allocation for these structs in the code, so no more code changes are required. The following cocci warnings are fixed: drivers/media/dvb-frontends/mxl5xx_defs.h:182:4-8: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) drivers/media/dvb-frontends/mxl5xx_defs.h:171:4-8: WARNING use flexible-array member instead (https://www.kernel.org/doc/html/latest/process/deprecated.html#zero-length-and-one-element-arrays) Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/dvb-frontends/mxl5xx_defs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb-frontends/mxl5xx_defs.h b/drivers/media/dvb-frontends/mxl5xx_defs.h index 097271f73740..bf08d82ba2cc 100644 --- a/drivers/media/dvb-frontends/mxl5xx_defs.h +++ b/drivers/media/dvb-frontends/mxl5xx_defs.h @@ -168,7 +168,7 @@ struct MBIN_FILE_HEADER_T { struct MBIN_FILE_T { struct MBIN_FILE_HEADER_T header; - u8 data[1]; + u8 data[]; }; struct MBIN_SEGMENT_HEADER_T { @@ -179,7 +179,7 @@ struct MBIN_SEGMENT_HEADER_T { struct MBIN_SEGMENT_T { struct MBIN_SEGMENT_HEADER_T header; - u8 data[1]; + u8 data[]; }; enum MXL_CMD_TYPE_E { MXL_CMD_WRITE = 0, MXL_CMD_READ }; -- cgit v1.2.3 From c6be6471004e0e4d10d0514146d8c41550823d63 Mon Sep 17 00:00:00 2001 From: Wentong Wu Date: Fri, 7 Jun 2024 21:25:46 +0800 Subject: media: ivsc: csi: add separate lock for v4l2 control handler There're possibilities that privacy status change notification happens in the middle of the ongoing mei command which already takes the command lock, but v4l2_ctrl_s_ctrl() would also need the same lock prior to this patch, so this may results in circular locking problem. This patch adds one dedicated lock for v4l2 control handler to avoid described issue. Fixes: 29006e196a56 ("media: pci: intel: ivsc: Add CSI submodule") Cc: stable@vger.kernel.org # for 6.6 and later Reported-by: Hao Yao Signed-off-by: Wentong Wu Tested-by: Jason Chen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ivsc/mei_csi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c b/drivers/media/pci/intel/ivsc/mei_csi.c index f04a89584334..c6d8f72e4eec 100644 --- a/drivers/media/pci/intel/ivsc/mei_csi.c +++ b/drivers/media/pci/intel/ivsc/mei_csi.c @@ -126,6 +126,8 @@ struct mei_csi { struct v4l2_ctrl_handler ctrl_handler; struct v4l2_ctrl *freq_ctrl; struct v4l2_ctrl *privacy_ctrl; + /* lock for v4l2 controls */ + struct mutex ctrl_lock; unsigned int remote_pad; /* start streaming or not */ int streaming; @@ -559,11 +561,13 @@ static int mei_csi_init_controls(struct mei_csi *csi) u32 max; int ret; + mutex_init(&csi->ctrl_lock); + ret = v4l2_ctrl_handler_init(&csi->ctrl_handler, 2); if (ret) return ret; - csi->ctrl_handler.lock = &csi->lock; + csi->ctrl_handler.lock = &csi->ctrl_lock; max = ARRAY_SIZE(link_freq_menu_items) - 1; csi->freq_ctrl = v4l2_ctrl_new_int_menu(&csi->ctrl_handler, @@ -755,6 +759,7 @@ err_entity: err_ctrl_handler: v4l2_ctrl_handler_free(&csi->ctrl_handler); + mutex_destroy(&csi->ctrl_lock); v4l2_async_nf_unregister(&csi->notifier); v4l2_async_nf_cleanup(&csi->notifier); @@ -774,6 +779,7 @@ static void mei_csi_remove(struct mei_cl_device *cldev) v4l2_async_nf_unregister(&csi->notifier); v4l2_async_nf_cleanup(&csi->notifier); v4l2_ctrl_handler_free(&csi->ctrl_handler); + mutex_destroy(&csi->ctrl_lock); v4l2_async_unregister_subdev(&csi->subdev); v4l2_subdev_cleanup(&csi->subdev); media_entity_cleanup(&csi->subdev.entity); -- cgit v1.2.3 From a813f168336ec4ef725b836e598cd9dc14f76dd7 Mon Sep 17 00:00:00 2001 From: Wentong Wu Date: Fri, 7 Jun 2024 21:25:45 +0800 Subject: media: ivsc: csi: don't count privacy on as error Prior to the ongoing command privacy is on, it would return -1 to indicate the current privacy status, and the ongoing command would be well executed by firmware as well, so this is not error. This patch changes its behavior to notify privacy on directly by V4L2 privacy control instead of reporting error. Fixes: 29006e196a56 ("media: pci: intel: ivsc: Add CSI submodule") Cc: stable@vger.kernel.org # for 6.6 and later Reported-by: Hao Yao Signed-off-by: Wentong Wu Tested-by: Jason Chen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ivsc/mei_csi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c b/drivers/media/pci/intel/ivsc/mei_csi.c index c6d8f72e4eec..16791a7f4f15 100644 --- a/drivers/media/pci/intel/ivsc/mei_csi.c +++ b/drivers/media/pci/intel/ivsc/mei_csi.c @@ -192,7 +192,11 @@ static int mei_csi_send(struct mei_csi *csi, u8 *buf, size_t len) /* command response status */ ret = csi->cmd_response.status; - if (ret) { + if (ret == -1) { + /* notify privacy on instead of reporting error */ + ret = 0; + v4l2_ctrl_s_ctrl(csi->privacy_ctrl, 1); + } else if (ret) { ret = -EINVAL; goto out; } -- cgit v1.2.3 From 87990652e0b94255229bf34b38158af825843859 Mon Sep 17 00:00:00 2001 From: Wentong Wu Date: Fri, 7 Jun 2024 21:25:47 +0800 Subject: media: ivsc: csi: remove privacy status in struct mei_csi The privacy status is maintained by privacy_ctrl, on which all of the privacy status changes will go through, so there is no point in maintaining one more element any more. Reported-by: Hao Yao Signed-off-by: Wentong Wu Tested-by: Jason Chen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ivsc/mei_csi.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c b/drivers/media/pci/intel/ivsc/mei_csi.c index 16791a7f4f15..2a9c12c975ca 100644 --- a/drivers/media/pci/intel/ivsc/mei_csi.c +++ b/drivers/media/pci/intel/ivsc/mei_csi.c @@ -138,9 +138,6 @@ struct mei_csi { u32 nr_of_lanes; /* frequency of the CSI-2 link */ u64 link_freq; - - /* privacy status */ - enum ivsc_privacy_status status; }; static const struct v4l2_mbus_framefmt mei_csi_format_mbus_default = { @@ -271,10 +268,9 @@ static void mei_csi_rx(struct mei_cl_device *cldev) switch (notif.cmd_id) { case CSI_PRIVACY_NOTIF: - if (notif.cont.cont < CSI_PRIVACY_MAX) { - csi->status = notif.cont.cont; - v4l2_ctrl_s_ctrl(csi->privacy_ctrl, csi->status); - } + if (notif.cont.cont < CSI_PRIVACY_MAX) + v4l2_ctrl_s_ctrl(csi->privacy_ctrl, + notif.cont.cont == CSI_PRIVACY_ON); break; case CSI_SET_OWNER: case CSI_SET_CONF: -- cgit v1.2.3 From 44d0ed23e5028f5a46c94df5f7c1cb5f9f7afb33 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Mon, 10 Jun 2024 10:10:30 +0200 Subject: media: i2c: alvium: fix alvium_get_fw_version() Instead of reading device_fw reg as multiple regs let's read the entire 64bit reg using one i2c read and use bit masks and bit shifts to get fw info. Signed-off-by: Tommaso Merciai Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/alvium-csi2.c | 23 ++++++++++++----------- drivers/media/i2c/alvium-csi2.h | 14 ++++++++++---- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/drivers/media/i2c/alvium-csi2.c b/drivers/media/i2c/alvium-csi2.c index e65702e3f73e..7a38c424ea48 100644 --- a/drivers/media/i2c/alvium-csi2.c +++ b/drivers/media/i2c/alvium-csi2.c @@ -403,21 +403,22 @@ static int alvium_get_bcrm_vers(struct alvium_dev *alvium) static int alvium_get_fw_version(struct alvium_dev *alvium) { struct device *dev = &alvium->i2c_client->dev; - u64 spec, maj, min, pat; - int ret = 0; + u64 val; + int ret; - ret = alvium_read(alvium, REG_BCRM_DEVICE_FW_SPEC_VERSION_R, - &spec, &ret); - ret = alvium_read(alvium, REG_BCRM_DEVICE_FW_MAJOR_VERSION_R, - &maj, &ret); - ret = alvium_read(alvium, REG_BCRM_DEVICE_FW_MINOR_VERSION_R, - &min, &ret); - ret = alvium_read(alvium, REG_BCRM_DEVICE_FW_PATCH_VERSION_R, - &pat, &ret); + ret = alvium_read(alvium, REG_BCRM_DEVICE_FW, &val, NULL); if (ret) return ret; - dev_info(dev, "fw version: %llu.%llu.%llu.%llu\n", spec, maj, min, pat); + dev_info(dev, "fw version: %02u.%02u.%04u.%08x\n", + (u8)((val & BCRM_DEVICE_FW_SPEC_MASK) >> + BCRM_DEVICE_FW_SPEC_SHIFT), + (u8)((val & BCRM_DEVICE_FW_MAJOR_MASK) >> + BCRM_DEVICE_FW_MAJOR_SHIFT), + (u16)((val & BCRM_DEVICE_FW_MINOR_MASK) >> + BCRM_DEVICE_FW_MINOR_SHIFT), + (u32)((val & BCRM_DEVICE_FW_PATCH_MASK) >> + BCRM_DEVICE_FW_PATCH_SHIFT)); return 0; } diff --git a/drivers/media/i2c/alvium-csi2.h b/drivers/media/i2c/alvium-csi2.h index 9463f8604fbc..ed712ad44899 100644 --- a/drivers/media/i2c/alvium-csi2.h +++ b/drivers/media/i2c/alvium-csi2.h @@ -31,10 +31,7 @@ #define REG_BCRM_REG_ADDR_R CCI_REG16(0x0014) #define REG_BCRM_FEATURE_INQUIRY_R REG_BCRM_V4L2_64BIT(0x0008) -#define REG_BCRM_DEVICE_FW_SPEC_VERSION_R REG_BCRM_V4L2_8BIT(0x0010) -#define REG_BCRM_DEVICE_FW_MAJOR_VERSION_R REG_BCRM_V4L2_8BIT(0x0011) -#define REG_BCRM_DEVICE_FW_MINOR_VERSION_R REG_BCRM_V4L2_16BIT(0x0012) -#define REG_BCRM_DEVICE_FW_PATCH_VERSION_R REG_BCRM_V4L2_32BIT(0x0014) +#define REG_BCRM_DEVICE_FW REG_BCRM_V4L2_64BIT(0x0010) #define REG_BCRM_WRITE_HANDSHAKE_RW REG_BCRM_V4L2_8BIT(0x0018) /* Streaming Control Registers */ @@ -205,6 +202,15 @@ #define ALVIUM_LP2HS_DELAY_MS 100 +#define BCRM_DEVICE_FW_MAJOR_MASK GENMASK_ULL(15, 8) +#define BCRM_DEVICE_FW_MAJOR_SHIFT 8 +#define BCRM_DEVICE_FW_MINOR_MASK GENMASK_ULL(31, 16) +#define BCRM_DEVICE_FW_MINOR_SHIFT 16 +#define BCRM_DEVICE_FW_PATCH_MASK GENMASK_ULL(63, 32) +#define BCRM_DEVICE_FW_PATCH_SHIFT 32 +#define BCRM_DEVICE_FW_SPEC_MASK GENMASK_ULL(7, 0) +#define BCRM_DEVICE_FW_SPEC_SHIFT 0 + enum alvium_bcrm_mode { ALVIUM_BCM_MODE, ALVIUM_GENCP_MODE, -- cgit v1.2.3 From 6be70e7981a00412b3fa99ab3b3f13ce908c0718 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Mon, 10 Jun 2024 10:10:31 +0200 Subject: media: i2c: alvium: rename acquisition frame rate enable reg Acquisition frame rate enable reg have a very long name let's reduce this with an abbreviation. Signed-off-by: Tommaso Merciai [Sakari Ailus: Fix spelling of "acquisition".] Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/alvium-csi2.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/alvium-csi2.h b/drivers/media/i2c/alvium-csi2.h index ed712ad44899..978af44f76c7 100644 --- a/drivers/media/i2c/alvium-csi2.h +++ b/drivers/media/i2c/alvium-csi2.h @@ -63,7 +63,7 @@ #define REG_BCRM_ACQUISITION_FRAME_RATE_MIN_R REG_BCRM_V4L2_64BIT(0x0098) #define REG_BCRM_ACQUISITION_FRAME_RATE_MAX_R REG_BCRM_V4L2_64BIT(0x00a0) #define REG_BCRM_ACQUISITION_FRAME_RATE_INC_R REG_BCRM_V4L2_64BIT(0x00a8) -#define REG_BCRM_ACQUISITION_FRAME_RATE_ENABLE_RW REG_BCRM_V4L2_8BIT(0x00b0) +#define REG_BCRM_ACQUISITION_FRAME_RATE_EN_RW REG_BCRM_V4L2_8BIT(0x00b0) #define REG_BCRM_FRAME_START_TRIGGER_MODE_RW REG_BCRM_V4L2_8BIT(0x00b4) #define REG_BCRM_FRAME_START_TRIGGER_SOURCE_RW REG_BCRM_V4L2_8BIT(0x00b8) -- cgit v1.2.3 From 50b631c6ecb33d45fe6919645bf19fdd3ab82f26 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Mon, 10 Jun 2024 10:10:32 +0200 Subject: media: i2c: alvium: enable acquisition frame rate Alvium camera by default is in free running mode. Datasheet say that acquisition frame rate reg can only be used if frame start trigger mode is set to off. Enable r/w acquisition frame rate and turn off trigger mode. Signed-off-by: Tommaso Merciai [Sakari Ailus: Fix spelling of "acquisition".] Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/alvium-csi2.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/media/i2c/alvium-csi2.c b/drivers/media/i2c/alvium-csi2.c index 7a38c424ea48..a88809333e56 100644 --- a/drivers/media/i2c/alvium-csi2.c +++ b/drivers/media/i2c/alvium-csi2.c @@ -1189,6 +1189,20 @@ static int alvium_set_frame_rate(struct alvium_dev *alvium, u64 fr) struct device *dev = &alvium->i2c_client->dev; int ret; + ret = alvium_write_hshake(alvium, REG_BCRM_ACQUISITION_FRAME_RATE_EN_RW, + 1); + if (ret) { + dev_err(dev, "Fail to set acquisition frame rate enable reg\n"); + return ret; + } + + ret = alvium_write_hshake(alvium, REG_BCRM_FRAME_START_TRIGGER_MODE_RW, + 0); + if (ret) { + dev_err(dev, "Fail to set frame start trigger mode reg\n"); + return ret; + } + ret = alvium_write_hshake(alvium, REG_BCRM_ACQUISITION_FRAME_RATE_RW, fr); if (ret) { -- cgit v1.2.3 From b6167cce2b1c3bc5eae967977964dfdaef971398 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Mon, 10 Jun 2024 10:10:33 +0200 Subject: media: i2c: alvium: implement enum_frame_size Implement the enum_frame_size pad operation. The sensor supports a continuous size range of resolutions. Signed-off-by: Tommaso Merciai Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/alvium-csi2.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/media/i2c/alvium-csi2.c b/drivers/media/i2c/alvium-csi2.c index a88809333e56..c27c6fcaede4 100644 --- a/drivers/media/i2c/alvium-csi2.c +++ b/drivers/media/i2c/alvium-csi2.c @@ -1722,6 +1722,27 @@ alvium_code_to_pixfmt(struct alvium_dev *alvium, u32 code) return &alvium->alvium_csi2_fmt[0]; } +static int alvium_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct alvium_dev *alvium = sd_to_alvium(sd); + const struct alvium_pixfmt *alvium_csi2_fmt; + + if (fse->index) + return -EINVAL; + + alvium_csi2_fmt = alvium_code_to_pixfmt(alvium, fse->code); + if (fse->code != alvium_csi2_fmt->code) + return -EINVAL; + + fse->min_width = alvium->img_min_width; + fse->max_width = alvium->img_max_width; + fse->min_height = alvium->img_min_height; + fse->max_height = alvium->img_max_height; + return 0; +} + static int alvium_set_mode(struct alvium_dev *alvium, struct v4l2_subdev_state *state) { @@ -2229,6 +2250,7 @@ static const struct v4l2_subdev_video_ops alvium_video_ops = { static const struct v4l2_subdev_pad_ops alvium_pad_ops = { .enum_mbus_code = alvium_enum_mbus_code, + .enum_frame_size = alvium_enum_frame_size, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = alvium_set_fmt, .get_selection = alvium_get_selection, -- cgit v1.2.3 From a50a2a3242f35dd551d89a6bad17255a410ba955 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Mon, 10 Jun 2024 10:10:34 +0200 Subject: media: i2c: alvium: Move V4L2_CID_GAIN to V4L2_CID_ANALOG_GAIN Into alvium cameras REG_BCRM_GAIN_RW control the analog gain. Let's use the right V4L2_CID_ANALOGUE_GAIN ctrl. Fixes: 0a7af872915e ("media: i2c: Add support for alvium camera") Cc: stable@vger.kernel.org Signed-off-by: Tommaso Merciai Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/alvium-csi2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/alvium-csi2.c b/drivers/media/i2c/alvium-csi2.c index c27c6fcaede4..5ddfd3dcb188 100644 --- a/drivers/media/i2c/alvium-csi2.c +++ b/drivers/media/i2c/alvium-csi2.c @@ -1998,7 +1998,7 @@ static int alvium_g_volatile_ctrl(struct v4l2_ctrl *ctrl) int val; switch (ctrl->id) { - case V4L2_CID_GAIN: + case V4L2_CID_ANALOGUE_GAIN: val = alvium_get_gain(alvium); if (val < 0) return val; @@ -2030,7 +2030,7 @@ static int alvium_s_ctrl(struct v4l2_ctrl *ctrl) return 0; switch (ctrl->id) { - case V4L2_CID_GAIN: + case V4L2_CID_ANALOGUE_GAIN: ret = alvium_set_ctrl_gain(alvium, ctrl->val); break; case V4L2_CID_AUTOGAIN: @@ -2159,7 +2159,7 @@ static int alvium_ctrl_init(struct alvium_dev *alvium) if (alvium->avail_ft.gain) { ctrls->gain = v4l2_ctrl_new_std(hdl, ops, - V4L2_CID_GAIN, + V4L2_CID_ANALOGUE_GAIN, alvium->min_gain, alvium->max_gain, alvium->inc_gain, -- cgit v1.2.3 From 6fca71fac4c8be8793ccbd854ea786c22f690aa2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 10 Jun 2024 19:34:17 +0200 Subject: media: ipu-bridge: Sort ipu_supported_sensors[] array by ACPI HID Sort the sensors in ipu_supported_sensors[] by ACPI HID rather then having them in some random order. While at it also use the correct (same as datasheet) capitalization for the sensor names in the comments. Instead of sometimes writing OV#### and sometimes ov####. Signed-off-by: Hans de Goede Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu-bridge.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index 61750cc98d70..f1d0d5f4854e 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -43,28 +43,30 @@ * becoming apparent in the future. * * Do not add an entry for a sensor that is not actually supported. + * + * Please keep the list sorted by ACPI HID. */ static const struct ipu_sensor_config ipu_supported_sensors[] = { + /* GalaxyCore GC0310 */ + IPU_SENSOR_CONFIG("INT0310", 0), /* Omnivision OV5693 */ IPU_SENSOR_CONFIG("INT33BE", 1, 419200000), + /* Omnivision OV2740 */ + IPU_SENSOR_CONFIG("INT3474", 1, 180000000), /* Omnivision OV8865 */ IPU_SENSOR_CONFIG("INT347A", 1, 360000000), /* Omnivision OV7251 */ IPU_SENSOR_CONFIG("INT347E", 1, 319200000), + /* Hynix Hi-556 */ + IPU_SENSOR_CONFIG("INT3537", 1, 437000000), + /* Omnivision OV01A10 */ + IPU_SENSOR_CONFIG("OVTI01A0", 1, 400000000), /* Omnivision OV2680 */ IPU_SENSOR_CONFIG("OVTI2680", 1, 331200000), - /* Omnivision ov8856 */ + /* Omnivision OV8856 */ IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000), - /* Omnivision ov2740 */ - IPU_SENSOR_CONFIG("INT3474", 1, 180000000), - /* Hynix hi556 */ - IPU_SENSOR_CONFIG("INT3537", 1, 437000000), - /* Omnivision ov13b10 */ + /* Omnivision OV13B10 */ IPU_SENSOR_CONFIG("OVTIDB10", 1, 560000000), - /* GalaxyCore GC0310 */ - IPU_SENSOR_CONFIG("INT0310", 0), - /* Omnivision ov01a10 */ - IPU_SENSOR_CONFIG("OVTI01A0", 1, 400000000), }; static const struct ipu_property_names prop_names = { -- cgit v1.2.3 From 440de616e76e225feb1182e2ca34cc0d6f00555c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 10 Jun 2024 19:34:18 +0200 Subject: media: ipu-bridge: Add HIDs from out of tree IPU6 driver ipu-bridge copy The out of tree IPU6 driver comes with its own copy of the ipu-bridge code. It also comes bundled with standard v4l2 sensor drivers. Many of these have been mainlined and the rest is being mainlined but not all are upstream yet. The latest version of the out of tree code now will use the mainline kernel ipu-bridge version when build against new enough kernels. This however breaks support for (bundled) sensor drivers which do not (yet) have an entry in the mainline ipu-bridge code. Add the missing entries to the mainline ipu-bridge code to make the transition to having everything in mainline easier. The alternative HID for the OV13B10 and the OV08x40 entries both are for sensors already supported in mainline which were missing. The downside of adding these HIDs is that this will cause the IPU3 / IPU6 drivers to delay registering there /dev/video# nodes until a sensor driver has bound, which for the non mainline drivers may never happen. This is not really an issue because almost all IPU designs only have front (user) facing sensors and all the added HIDs are for the main RGB (not IR) sensor. So if the sensor driver is missing then the user can already not use the camera and adding these HIDs does not really change that. Signed-off-by: Hans de Goede Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/pci/intel/ipu-bridge.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index f1d0d5f4854e..a0e9a71580b5 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -47,6 +47,12 @@ * Please keep the list sorted by ACPI HID. */ static const struct ipu_sensor_config ipu_supported_sensors[] = { + /* Himax HM11B1 */ + IPU_SENSOR_CONFIG("HIMX11B1", 1, 384000000), + /* Himax HM2170 */ + IPU_SENSOR_CONFIG("HIMX2170", 1, 384000000), + /* Himax HM2172 */ + IPU_SENSOR_CONFIG("HIMX2172", 1, 384000000), /* GalaxyCore GC0310 */ IPU_SENSOR_CONFIG("INT0310", 0), /* Omnivision OV5693 */ @@ -59,14 +65,24 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { IPU_SENSOR_CONFIG("INT347E", 1, 319200000), /* Hynix Hi-556 */ IPU_SENSOR_CONFIG("INT3537", 1, 437000000), - /* Omnivision OV01A10 */ + /* Omnivision OV01A10 / OV01A1S */ IPU_SENSOR_CONFIG("OVTI01A0", 1, 400000000), + IPU_SENSOR_CONFIG("OVTI01AS", 1, 400000000), + /* Omnivision OV02C10 */ + IPU_SENSOR_CONFIG("OVTI02C1", 1, 400000000), + /* Omnivision OV02E10 */ + IPU_SENSOR_CONFIG("OVTI02E1", 1, 360000000), + /* Omnivision OV08A10 */ + IPU_SENSOR_CONFIG("OVTI08A1", 1, 500000000), + /* Omnivision OV08x40 */ + IPU_SENSOR_CONFIG("OVTI08F4", 1, 400000000), + /* Omnivision OV13B10 */ + IPU_SENSOR_CONFIG("OVTI13B1", 1, 560000000), + IPU_SENSOR_CONFIG("OVTIDB10", 1, 560000000), /* Omnivision OV2680 */ IPU_SENSOR_CONFIG("OVTI2680", 1, 331200000), /* Omnivision OV8856 */ IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000), - /* Omnivision OV13B10 */ - IPU_SENSOR_CONFIG("OVTIDB10", 1, 560000000), }; static const struct ipu_property_names prop_names = { -- cgit v1.2.3 From e0b66a6e471f628d4029e317a4a5701960b83dcd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 8 Jun 2024 16:38:15 +0200 Subject: media: ov2680: Pass correct number of controls to v4l2_ctrl_handler_init() The ov2680 driver has 9 controls now and the call to v4l2_ctrl_new_fwnode_properties() adds 2 more. Tell v4l2_ctrl_handler_init() to pre-allocate space for 11 controls to match this. Signed-off-by: Hans de Goede Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ov2680.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c index 3ae0ea58668d..7237fb27ecd0 100644 --- a/drivers/media/i2c/ov2680.c +++ b/drivers/media/i2c/ov2680.c @@ -971,7 +971,7 @@ static int ov2680_v4l2_register(struct ov2680_dev *sensor) if (ret < 0) return ret; - v4l2_ctrl_handler_init(hdl, 5); + v4l2_ctrl_handler_init(hdl, 11); hdl->lock = &sensor->lock; -- cgit v1.2.3 From 3cdc776e0a5f1784c3ae5d3371b64215c228bf1f Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Fri, 7 Jun 2024 16:50:23 +0100 Subject: media: i2c: imx219: fix msr access command sequence It was reported to me that the imx219 didn't work on one of our development kits partly because the access sequence is incorrect. The datasheet I could find [1] for this camera has the access sequence: Seq. No. Address (Hex) data 1 30EB 05 2 30EB 0C 3 300A FF 4 300B FF 5 30EB 05 6 30EB 09 but the driver swaps the first two elements. Laurent pointed out on IRC that the original code used the correct sequence for 1920x1080 but the current sequence for 3280x2464 and 1640x1232. During refactoring of the init sequence the current order was used for all formats. Switch to using the documented sequence. Link: https://www.opensourceinstruments.com/Electronics/Data/IMX219PQ.pdf [1] Fixes: 8508455961d5 ("media: i2c: imx219: Split common registers from mode tables") Fixes: 1283b3b8f82b ("media: i2c: Add driver for Sony IMX219 sensor") Signed-off-by: Conor Dooley Reviewed-by: Dave Stevenson Tested-by: Dave Stevenson Reviewed-by: Laurent Pinchart Tested-by: Adam Ford #imx8mp-beacon-kit Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/imx219.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 51ebf5453fce..e78a80b2bb2e 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -162,8 +162,8 @@ static const struct cci_reg_sequence imx219_common_regs[] = { { IMX219_REG_MODE_SELECT, 0x00 }, /* Mode Select */ /* To Access Addresses 3000-5fff, send the following commands */ - { CCI_REG8(0x30eb), 0x0c }, { CCI_REG8(0x30eb), 0x05 }, + { CCI_REG8(0x30eb), 0x0c }, { CCI_REG8(0x300a), 0xff }, { CCI_REG8(0x300b), 0xff }, { CCI_REG8(0x30eb), 0x05 }, -- cgit v1.2.3 From 15765ff7147e9ca45079c1a5e37c35052d57381a Mon Sep 17 00:00:00 2001 From: Benjamin Mugnier Date: Wed, 12 Jun 2024 10:21:14 +0200 Subject: media: i2c: vgxy61: Fix device name Rename 'st-vgxy61' to 'vgxy61', dropping the vendor prefix to follow the same naming scheme as the vast majority of device drivers. The device tree binding does not fall into binding rename exceptions and therefore must not be changed. Keep its legacy name. Signed-off-by: Benjamin Mugnier Acked-by: Krzysztof Kozlowski Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .../userspace-api/media/drivers/index.rst | 2 +- .../userspace-api/media/drivers/st-vgxy61.rst | 25 - .../userspace-api/media/drivers/vgxy61.rst | 25 + MAINTAINERS | 6 +- drivers/media/i2c/Kconfig | 2 +- drivers/media/i2c/Makefile | 2 +- drivers/media/i2c/st-vgxy61.c | 1895 -------------------- drivers/media/i2c/vgxy61.c | 1895 ++++++++++++++++++++ 8 files changed, 1926 insertions(+), 1926 deletions(-) delete mode 100644 Documentation/userspace-api/media/drivers/st-vgxy61.rst create mode 100644 Documentation/userspace-api/media/drivers/vgxy61.rst delete mode 100644 drivers/media/i2c/st-vgxy61.c create mode 100644 drivers/media/i2c/vgxy61.c diff --git a/Documentation/userspace-api/media/drivers/index.rst b/Documentation/userspace-api/media/drivers/index.rst index 2252063593bf..d706cb47b112 100644 --- a/Documentation/userspace-api/media/drivers/index.rst +++ b/Documentation/userspace-api/media/drivers/index.rst @@ -35,6 +35,6 @@ For more details see the file COPYING in the source distribution of Linux. max2175 npcm-video omap3isp-uapi - st-vgxy61 thp7312 uvcvideo + vgxy61 diff --git a/Documentation/userspace-api/media/drivers/st-vgxy61.rst b/Documentation/userspace-api/media/drivers/st-vgxy61.rst deleted file mode 100644 index 17ac15afa77c..000000000000 --- a/Documentation/userspace-api/media/drivers/st-vgxy61.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0 - -ST VGXY61 camera sensor driver -============================== - -The ST VGXY61 driver implements the following controls: - -``V4L2_CID_HDR_SENSOR_MODE`` -------------------------------- - Change the sensor HDR mode. A HDR picture is obtained by merging two - captures of the same scene using two different exposure periods. - -.. flat-table:: - :header-rows: 0 - :stub-columns: 0 - :widths: 1 4 - - * - HDR linearize - - The merger outputs a long exposure capture as long as it is not - saturated. - * - HDR subtraction - - This involves subtracting the short exposure frame from the long - exposure frame. - * - No HDR - - This mode is used for standard dynamic range (SDR) exposures. diff --git a/Documentation/userspace-api/media/drivers/vgxy61.rst b/Documentation/userspace-api/media/drivers/vgxy61.rst new file mode 100644 index 000000000000..17ac15afa77c --- /dev/null +++ b/Documentation/userspace-api/media/drivers/vgxy61.rst @@ -0,0 +1,25 @@ +.. SPDX-License-Identifier: GPL-2.0 + +ST VGXY61 camera sensor driver +============================== + +The ST VGXY61 driver implements the following controls: + +``V4L2_CID_HDR_SENSOR_MODE`` +------------------------------- + Change the sensor HDR mode. A HDR picture is obtained by merging two + captures of the same scene using two different exposure periods. + +.. flat-table:: + :header-rows: 0 + :stub-columns: 0 + :widths: 1 4 + + * - HDR linearize + - The merger outputs a long exposure capture as long as it is not + saturated. + * - HDR subtraction + - This involves subtracting the short exposure frame from the long + exposure frame. + * - No HDR + - This mode is used for standard dynamic range (SDR) exposures. diff --git a/MAINTAINERS b/MAINTAINERS index 9caf669234f2..313624f0bd7e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21212,8 +21212,8 @@ L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/st,st-vgxy61.yaml -F: Documentation/userspace-api/media/drivers/st-vgxy61.rst -F: drivers/media/i2c/st-vgxy61.c +F: Documentation/userspace-api/media/drivers/vgxy61.rst +F: drivers/media/i2c/vgxy61.c ST VL53L0X ToF RANGER(I2C) IIO DRIVER M: Song Qiang @@ -23549,7 +23549,7 @@ F: drivers/media/i2c/mt* F: drivers/media/i2c/og* F: drivers/media/i2c/ov* F: drivers/media/i2c/s5* -F: drivers/media/i2c/st-vgxy61.c +F: drivers/media/i2c/vgxy61.c VF610 NAND DRIVER M: Stefan Agner diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 5498128773c7..f336b622e5b8 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -670,7 +670,7 @@ config VIDEO_S5K6A3 This is a V4L2 sensor driver for Samsung S5K6A3 raw camera sensor. -config VIDEO_ST_VGXY61 +config VIDEO_VGXY61 tristate "ST VGXY61 sensor support" select V4L2_CCI_I2C depends on OF && GPIOLIB diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index d1403e3273ca..c36f5d1d7f18 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -127,7 +127,6 @@ obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o -obj-$(CONFIG_VIDEO_ST_VGXY61) += st-vgxy61.o obj-$(CONFIG_VIDEO_TC358743) += tc358743.o obj-$(CONFIG_VIDEO_TC358746) += tc358746.o obj-$(CONFIG_VIDEO_TDA1997X) += tda1997x.o @@ -151,6 +150,7 @@ obj-$(CONFIG_VIDEO_TW9910) += tw9910.o obj-$(CONFIG_VIDEO_UDA1342) += uda1342.o obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o +obj-$(CONFIG_VIDEO_VGXY61) += vgxy61.o obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o obj-$(CONFIG_VIDEO_WM8739) += wm8739.o diff --git a/drivers/media/i2c/st-vgxy61.c b/drivers/media/i2c/st-vgxy61.c deleted file mode 100644 index b9e7c57027b1..000000000000 --- a/drivers/media/i2c/st-vgxy61.c +++ /dev/null @@ -1,1895 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Driver for VGXY61 global shutter sensor family driver - * - * Copyright (C) 2022 STMicroelectronics SA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#define VGXY61_REG_MODEL_ID CCI_REG16_LE(0x0000) -#define VG5661_MODEL_ID 0x5661 -#define VG5761_MODEL_ID 0x5761 -#define VGXY61_REG_REVISION CCI_REG16_LE(0x0002) -#define VGXY61_REG_FWPATCH_REVISION CCI_REG16_LE(0x0014) -#define VGXY61_REG_FWPATCH_START_ADDR CCI_REG8(0x2000) -#define VGXY61_REG_SYSTEM_FSM CCI_REG8(0x0020) -#define VGXY61_SYSTEM_FSM_SW_STBY 0x03 -#define VGXY61_SYSTEM_FSM_STREAMING 0x04 -#define VGXY61_REG_NVM CCI_REG8(0x0023) -#define VGXY61_NVM_OK 0x04 -#define VGXY61_REG_STBY CCI_REG8(0x0201) -#define VGXY61_STBY_NO_REQ 0 -#define VGXY61_STBY_REQ_TMP_READ BIT(2) -#define VGXY61_REG_STREAMING CCI_REG8(0x0202) -#define VGXY61_STREAMING_NO_REQ 0 -#define VGXY61_STREAMING_REQ_STOP BIT(0) -#define VGXY61_STREAMING_REQ_START BIT(1) -#define VGXY61_REG_EXT_CLOCK CCI_REG32_LE(0x0220) -#define VGXY61_REG_CLK_PLL_PREDIV CCI_REG8(0x0224) -#define VGXY61_REG_CLK_SYS_PLL_MULT CCI_REG8(0x0225) -#define VGXY61_REG_GPIO_0_CTRL CCI_REG8(0x0236) -#define VGXY61_REG_GPIO_1_CTRL CCI_REG8(0x0237) -#define VGXY61_REG_GPIO_2_CTRL CCI_REG8(0x0238) -#define VGXY61_REG_GPIO_3_CTRL CCI_REG8(0x0239) -#define VGXY61_REG_SIGNALS_POLARITY_CTRL CCI_REG8(0x023b) -#define VGXY61_REG_LINE_LENGTH CCI_REG16_LE(0x0300) -#define VGXY61_REG_ORIENTATION CCI_REG8(0x0302) -#define VGXY61_REG_VT_CTRL CCI_REG8(0x0304) -#define VGXY61_REG_FORMAT_CTRL CCI_REG8(0x0305) -#define VGXY61_REG_OIF_CTRL CCI_REG16_LE(0x0306) -#define VGXY61_REG_OIF_ROI0_CTRL CCI_REG8(0x030a) -#define VGXY61_REG_ROI0_START_H CCI_REG16_LE(0x0400) -#define VGXY61_REG_ROI0_START_V CCI_REG16_LE(0x0402) -#define VGXY61_REG_ROI0_END_H CCI_REG16_LE(0x0404) -#define VGXY61_REG_ROI0_END_V CCI_REG16_LE(0x0406) -#define VGXY61_REG_PATGEN_CTRL CCI_REG32_LE(0x0440) -#define VGXY61_PATGEN_LONG_ENABLE BIT(16) -#define VGXY61_PATGEN_SHORT_ENABLE BIT(0) -#define VGXY61_PATGEN_LONG_TYPE_SHIFT 18 -#define VGXY61_PATGEN_SHORT_TYPE_SHIFT 4 -#define VGXY61_REG_FRAME_CONTENT_CTRL CCI_REG8(0x0478) -#define VGXY61_REG_COARSE_EXPOSURE_LONG CCI_REG16_LE(0x0500) -#define VGXY61_REG_COARSE_EXPOSURE_SHORT CCI_REG16_LE(0x0504) -#define VGXY61_REG_ANALOG_GAIN CCI_REG8(0x0508) -#define VGXY61_REG_DIGITAL_GAIN_LONG CCI_REG16_LE(0x050a) -#define VGXY61_REG_DIGITAL_GAIN_SHORT CCI_REG16_LE(0x0512) -#define VGXY61_REG_FRAME_LENGTH CCI_REG16_LE(0x051a) -#define VGXY61_REG_SIGNALS_CTRL CCI_REG16_LE(0x0522) -#define VGXY61_SIGNALS_GPIO_ID_SHIFT 4 -#define VGXY61_REG_READOUT_CTRL CCI_REG8(0x0530) -#define VGXY61_REG_HDR_CTRL CCI_REG8(0x0532) -#define VGXY61_REG_PATGEN_LONG_DATA_GR CCI_REG16_LE(0x092c) -#define VGXY61_REG_PATGEN_LONG_DATA_R CCI_REG16_LE(0x092e) -#define VGXY61_REG_PATGEN_LONG_DATA_B CCI_REG16_LE(0x0930) -#define VGXY61_REG_PATGEN_LONG_DATA_GB CCI_REG16_LE(0x0932) -#define VGXY61_REG_PATGEN_SHORT_DATA_GR CCI_REG16_LE(0x0950) -#define VGXY61_REG_PATGEN_SHORT_DATA_R CCI_REG16_LE(0x0952) -#define VGXY61_REG_PATGEN_SHORT_DATA_B CCI_REG16_LE(0x0954) -#define VGXY61_REG_PATGEN_SHORT_DATA_GB CCI_REG16_LE(0x0956) -#define VGXY61_REG_BYPASS_CTRL CCI_REG8(0x0a60) - -#define VGX661_WIDTH 1464 -#define VGX661_HEIGHT 1104 -#define VGX761_WIDTH 1944 -#define VGX761_HEIGHT 1204 -#define VGX661_DEFAULT_MODE 1 -#define VGX761_DEFAULT_MODE 1 -#define VGX661_SHORT_ROT_TERM 93 -#define VGX761_SHORT_ROT_TERM 90 -#define VGXY61_EXPOS_ROT_TERM 66 -#define VGXY61_WRITE_MULTIPLE_CHUNK_MAX 16 -#define VGXY61_NB_GPIOS 4 -#define VGXY61_NB_POLARITIES 5 -#define VGXY61_FRAME_LENGTH_DEF 1313 -#define VGXY61_MIN_FRAME_LENGTH 1288 -#define VGXY61_MIN_EXPOSURE 10 -#define VGXY61_HDR_LINEAR_RATIO 10 -#define VGXY61_TIMEOUT_MS 500 -#define VGXY61_MEDIA_BUS_FMT_DEF MEDIA_BUS_FMT_Y8_1X8 - -#define VGXY61_FWPATCH_REVISION_MAJOR 2 -#define VGXY61_FWPATCH_REVISION_MINOR 0 -#define VGXY61_FWPATCH_REVISION_MICRO 5 - -static const u8 patch_array[] = { - 0xbf, 0x00, 0x05, 0x20, 0x06, 0x01, 0xe0, 0xe0, 0x04, 0x80, 0xe6, 0x45, - 0xed, 0x6f, 0xfe, 0xff, 0x14, 0x80, 0x1f, 0x84, 0x10, 0x42, 0x05, 0x7c, - 0x01, 0xc4, 0x1e, 0x80, 0xb6, 0x42, 0x00, 0xe0, 0x1e, 0x82, 0x1e, 0xc0, - 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x00, 0xfa, 0x86, 0x0d, 0x70, 0xe1, - 0x04, 0x98, 0x15, 0x00, 0x28, 0xe0, 0x14, 0x02, 0x08, 0xfc, 0x15, 0x40, - 0x28, 0xe0, 0x98, 0x58, 0xe0, 0xef, 0x04, 0x98, 0x0e, 0x04, 0x00, 0xf0, - 0x15, 0x00, 0x28, 0xe0, 0x19, 0xc8, 0x15, 0x40, 0x28, 0xe0, 0xc6, 0x41, - 0xfc, 0xe0, 0x14, 0x80, 0x1f, 0x84, 0x14, 0x02, 0xa0, 0xfc, 0x1e, 0x80, - 0x14, 0x80, 0x14, 0x02, 0x80, 0xfb, 0x14, 0x02, 0xe0, 0xfc, 0x1e, 0x80, - 0x14, 0xc0, 0x1f, 0x84, 0x14, 0x02, 0xa4, 0xfc, 0x1e, 0xc0, 0x14, 0xc0, - 0x14, 0x02, 0x80, 0xfb, 0x14, 0x02, 0xe4, 0xfc, 0x1e, 0xc0, 0x0c, 0x0c, - 0x00, 0xf2, 0x93, 0xdd, 0x86, 0x00, 0xf8, 0xe0, 0x04, 0x80, 0xc6, 0x03, - 0x70, 0xe1, 0x0e, 0x84, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x00, 0xfa, - 0x6b, 0x80, 0x06, 0x40, 0x6c, 0xe1, 0x04, 0x80, 0x09, 0x00, 0xe0, 0xe0, - 0x0b, 0xa1, 0x95, 0x84, 0x05, 0x0c, 0x1c, 0xe0, 0x86, 0x02, 0xf9, 0x60, - 0xe0, 0xcf, 0x78, 0x6e, 0x80, 0xef, 0x25, 0x0c, 0x18, 0xe0, 0x05, 0x4c, - 0x1c, 0xe0, 0x86, 0x02, 0xf9, 0x60, 0xe0, 0xcf, 0x0b, 0x84, 0xd8, 0x6d, - 0x80, 0xef, 0x05, 0x4c, 0x18, 0xe0, 0x04, 0xd8, 0x0b, 0xa5, 0x95, 0x84, - 0x05, 0x0c, 0x2c, 0xe0, 0x06, 0x02, 0x01, 0x60, 0xe0, 0xce, 0x18, 0x6d, - 0x80, 0xef, 0x25, 0x0c, 0x30, 0xe0, 0x05, 0x4c, 0x2c, 0xe0, 0x06, 0x02, - 0x01, 0x60, 0xe0, 0xce, 0x0b, 0x84, 0x78, 0x6c, 0x80, 0xef, 0x05, 0x4c, - 0x30, 0xe0, 0x0c, 0x0c, 0x00, 0xf2, 0x93, 0xdd, 0x46, 0x01, 0x70, 0xe1, - 0x08, 0x80, 0x0b, 0xa1, 0x08, 0x5c, 0x00, 0xda, 0x06, 0x01, 0x68, 0xe1, - 0x04, 0x80, 0x4a, 0x40, 0x84, 0xe0, 0x08, 0x5c, 0x00, 0x9a, 0x06, 0x01, - 0xe0, 0xe0, 0x04, 0x80, 0x15, 0x00, 0x60, 0xe0, 0x19, 0xc4, 0x15, 0x40, - 0x60, 0xe0, 0x15, 0x00, 0x78, 0xe0, 0x19, 0xc4, 0x15, 0x40, 0x78, 0xe0, - 0x93, 0xdd, 0xc3, 0xc1, 0x46, 0x01, 0x70, 0xe1, 0x08, 0x80, 0x0b, 0xa1, - 0x08, 0x5c, 0x00, 0xda, 0x06, 0x01, 0x68, 0xe1, 0x04, 0x80, 0x4a, 0x40, - 0x84, 0xe0, 0x08, 0x5c, 0x00, 0x9a, 0x06, 0x01, 0xe0, 0xe0, 0x14, 0x80, - 0x25, 0x02, 0x54, 0xe0, 0x29, 0xc4, 0x25, 0x42, 0x54, 0xe0, 0x24, 0x80, - 0x35, 0x04, 0x6c, 0xe0, 0x39, 0xc4, 0x35, 0x44, 0x6c, 0xe0, 0x25, 0x02, - 0x64, 0xe0, 0x29, 0xc4, 0x25, 0x42, 0x64, 0xe0, 0x04, 0x80, 0x15, 0x00, - 0x7c, 0xe0, 0x19, 0xc4, 0x15, 0x40, 0x7c, 0xe0, 0x93, 0xdd, 0xc3, 0xc1, - 0x4c, 0x04, 0x7c, 0xfa, 0x86, 0x40, 0x98, 0xe0, 0x14, 0x80, 0x1b, 0xa1, - 0x06, 0x00, 0x00, 0xc0, 0x08, 0x42, 0x38, 0xdc, 0x08, 0x64, 0xa0, 0xef, - 0x86, 0x42, 0x3c, 0xe0, 0x68, 0x49, 0x80, 0xef, 0x6b, 0x80, 0x78, 0x53, - 0xc8, 0xef, 0xc6, 0x54, 0x6c, 0xe1, 0x7b, 0x80, 0xb5, 0x14, 0x0c, 0xf8, - 0x05, 0x14, 0x14, 0xf8, 0x1a, 0xac, 0x8a, 0x80, 0x0b, 0x90, 0x38, 0x55, - 0x80, 0xef, 0x1a, 0xae, 0x17, 0xc2, 0x03, 0x82, 0x88, 0x65, 0x80, 0xef, - 0x1b, 0x80, 0x0b, 0x8e, 0x68, 0x65, 0x80, 0xef, 0x9b, 0x80, 0x0b, 0x8c, - 0x08, 0x65, 0x80, 0xef, 0x6b, 0x80, 0x0b, 0x92, 0x1b, 0x8c, 0x98, 0x64, - 0x80, 0xef, 0x1a, 0xec, 0x9b, 0x80, 0x0b, 0x90, 0x95, 0x54, 0x10, 0xe0, - 0xa8, 0x53, 0x80, 0xef, 0x1a, 0xee, 0x17, 0xc2, 0x03, 0x82, 0xf8, 0x63, - 0x80, 0xef, 0x1b, 0x80, 0x0b, 0x8e, 0xd8, 0x63, 0x80, 0xef, 0x1b, 0x8c, - 0x68, 0x63, 0x80, 0xef, 0x6b, 0x80, 0x0b, 0x92, 0x65, 0x54, 0x14, 0xe0, - 0x08, 0x65, 0x84, 0xef, 0x68, 0x63, 0x80, 0xef, 0x7b, 0x80, 0x0b, 0x8c, - 0xa8, 0x64, 0x84, 0xef, 0x08, 0x63, 0x80, 0xef, 0x14, 0xe8, 0x46, 0x44, - 0x94, 0xe1, 0x24, 0x88, 0x4a, 0x4e, 0x04, 0xe0, 0x14, 0xea, 0x1a, 0x04, - 0x08, 0xe0, 0x0a, 0x40, 0x84, 0xed, 0x0c, 0x04, 0x00, 0xe2, 0x4a, 0x40, - 0x04, 0xe0, 0x19, 0x16, 0xc0, 0xe0, 0x0a, 0x40, 0x84, 0xed, 0x21, 0x54, - 0x60, 0xe0, 0x0c, 0x04, 0x00, 0xe2, 0x1b, 0xa5, 0x0e, 0xea, 0x01, 0x89, - 0x21, 0x54, 0x64, 0xe0, 0x7e, 0xe8, 0x65, 0x82, 0x1b, 0xa7, 0x26, 0x00, - 0x00, 0x80, 0xa5, 0x82, 0x1b, 0xa9, 0x65, 0x82, 0x1b, 0xa3, 0x01, 0x85, - 0x16, 0x00, 0x00, 0xc0, 0x01, 0x54, 0x04, 0xf8, 0x06, 0xaa, 0x01, 0x83, - 0x06, 0xa8, 0x65, 0x81, 0x06, 0xa8, 0x01, 0x54, 0x04, 0xf8, 0x01, 0x83, - 0x06, 0xaa, 0x09, 0x14, 0x18, 0xf8, 0x0b, 0xa1, 0x05, 0x84, 0xc6, 0x42, - 0xd4, 0xe0, 0x14, 0x84, 0x01, 0x83, 0x01, 0x54, 0x60, 0xe0, 0x01, 0x54, - 0x64, 0xe0, 0x0b, 0x02, 0x90, 0xe0, 0x10, 0x02, 0x90, 0xe5, 0x01, 0x54, - 0x88, 0xe0, 0xb5, 0x81, 0xc6, 0x40, 0xd4, 0xe0, 0x14, 0x80, 0x0b, 0x02, - 0xe0, 0xe4, 0x10, 0x02, 0x31, 0x66, 0x02, 0xc0, 0x01, 0x54, 0x88, 0xe0, - 0x1a, 0x84, 0x29, 0x14, 0x10, 0xe0, 0x1c, 0xaa, 0x2b, 0xa1, 0xf5, 0x82, - 0x25, 0x14, 0x10, 0xf8, 0x2b, 0x04, 0xa8, 0xe0, 0x20, 0x44, 0x0d, 0x70, - 0x03, 0xc0, 0x2b, 0xa1, 0x04, 0x00, 0x80, 0x9a, 0x02, 0x40, 0x84, 0x90, - 0x03, 0x54, 0x04, 0x80, 0x4c, 0x0c, 0x7c, 0xf2, 0x93, 0xdd, 0x00, 0x00, - 0x02, 0xa9, 0x00, 0x00, 0x64, 0x4a, 0x40, 0x00, 0x08, 0x2d, 0x58, 0xe0, - 0xa8, 0x98, 0x40, 0x00, 0x28, 0x07, 0x34, 0xe0, 0x05, 0xb9, 0x00, 0x00, - 0x28, 0x00, 0x41, 0x05, 0x88, 0x00, 0x41, 0x3c, 0x98, 0x00, 0x41, 0x52, - 0x04, 0x01, 0x41, 0x79, 0x3c, 0x01, 0x41, 0x6a, 0x3d, 0xfe, 0x00, 0x00, -}; - -static const char * const vgxy61_test_pattern_menu[] = { - "Disabled", - "Solid", - "Colorbar", - "Gradbar", - "Hgrey", - "Vgrey", - "Dgrey", - "PN28", -}; - -static const char * const vgxy61_hdr_mode_menu[] = { - "HDR linearize", - "HDR substraction", - "No HDR", -}; - -static const char * const vgxy61_supply_name[] = { - "VCORE", - "VDDIO", - "VANA", -}; - -static const s64 link_freq[] = { - /* - * MIPI output freq is 804Mhz / 2, as it uses both rising edge and - * falling edges to send data - */ - 402000000ULL -}; - -enum vgxy61_bin_mode { - VGXY61_BIN_MODE_NORMAL, - VGXY61_BIN_MODE_DIGITAL_X2, - VGXY61_BIN_MODE_DIGITAL_X4, -}; - -enum vgxy61_hdr_mode { - VGXY61_HDR_LINEAR, - VGXY61_HDR_SUB, - VGXY61_NO_HDR, -}; - -enum vgxy61_strobe_mode { - VGXY61_STROBE_DISABLED, - VGXY61_STROBE_LONG, - VGXY61_STROBE_ENABLED, -}; - -struct vgxy61_mode_info { - u32 width; - u32 height; - enum vgxy61_bin_mode bin_mode; - struct v4l2_rect crop; -}; - -struct vgxy61_fmt_desc { - u32 code; - u8 bpp; - u8 data_type; -}; - -static const struct vgxy61_fmt_desc vgxy61_supported_codes[] = { - { - .code = MEDIA_BUS_FMT_Y8_1X8, - .bpp = 8, - .data_type = MIPI_CSI2_DT_RAW8, - }, - { - .code = MEDIA_BUS_FMT_Y10_1X10, - .bpp = 10, - .data_type = MIPI_CSI2_DT_RAW10, - }, - { - .code = MEDIA_BUS_FMT_Y12_1X12, - .bpp = 12, - .data_type = MIPI_CSI2_DT_RAW12, - }, - { - .code = MEDIA_BUS_FMT_Y14_1X14, - .bpp = 14, - .data_type = MIPI_CSI2_DT_RAW14, - }, - { - .code = MEDIA_BUS_FMT_Y16_1X16, - .bpp = 16, - .data_type = MIPI_CSI2_DT_RAW16, - }, -}; - -static const struct vgxy61_mode_info vgx661_mode_data[] = { - { - .width = VGX661_WIDTH, - .height = VGX661_HEIGHT, - .bin_mode = VGXY61_BIN_MODE_NORMAL, - .crop = { - .left = 0, - .top = 0, - .width = VGX661_WIDTH, - .height = VGX661_HEIGHT, - }, - }, - { - .width = 1280, - .height = 720, - .bin_mode = VGXY61_BIN_MODE_NORMAL, - .crop = { - .left = 92, - .top = 192, - .width = 1280, - .height = 720, - }, - }, - { - .width = 640, - .height = 480, - .bin_mode = VGXY61_BIN_MODE_DIGITAL_X2, - .crop = { - .left = 92, - .top = 72, - .width = 1280, - .height = 960, - }, - }, - { - .width = 320, - .height = 240, - .bin_mode = VGXY61_BIN_MODE_DIGITAL_X4, - .crop = { - .left = 92, - .top = 72, - .width = 1280, - .height = 960, - }, - }, -}; - -static const struct vgxy61_mode_info vgx761_mode_data[] = { - { - .width = VGX761_WIDTH, - .height = VGX761_HEIGHT, - .bin_mode = VGXY61_BIN_MODE_NORMAL, - .crop = { - .left = 0, - .top = 0, - .width = VGX761_WIDTH, - .height = VGX761_HEIGHT, - }, - }, - { - .width = 1920, - .height = 1080, - .bin_mode = VGXY61_BIN_MODE_NORMAL, - .crop = { - .left = 12, - .top = 62, - .width = 1920, - .height = 1080, - }, - }, - { - .width = 1280, - .height = 720, - .bin_mode = VGXY61_BIN_MODE_NORMAL, - .crop = { - .left = 332, - .top = 242, - .width = 1280, - .height = 720, - }, - }, - { - .width = 640, - .height = 480, - .bin_mode = VGXY61_BIN_MODE_DIGITAL_X2, - .crop = { - .left = 332, - .top = 122, - .width = 1280, - .height = 960, - }, - }, - { - .width = 320, - .height = 240, - .bin_mode = VGXY61_BIN_MODE_DIGITAL_X4, - .crop = { - .left = 332, - .top = 122, - .width = 1280, - .height = 960, - }, - }, -}; - -struct vgxy61_dev { - struct i2c_client *i2c_client; - struct regmap *regmap; - struct v4l2_subdev sd; - struct media_pad pad; - struct regulator_bulk_data supplies[ARRAY_SIZE(vgxy61_supply_name)]; - struct gpio_desc *reset_gpio; - struct clk *xclk; - u32 clk_freq; - u16 id; - u16 sensor_width; - u16 sensor_height; - u16 oif_ctrl; - unsigned int nb_of_lane; - u32 data_rate_in_mbps; - u32 pclk; - u16 line_length; - u16 rot_term; - bool gpios_polarity; - /* Lock to protect all members below */ - struct mutex lock; - struct v4l2_ctrl_handler ctrl_handler; - struct v4l2_ctrl *pixel_rate_ctrl; - struct v4l2_ctrl *expo_ctrl; - struct v4l2_ctrl *vblank_ctrl; - struct v4l2_ctrl *vflip_ctrl; - struct v4l2_ctrl *hflip_ctrl; - bool streaming; - struct v4l2_mbus_framefmt fmt; - const struct vgxy61_mode_info *sensor_modes; - unsigned int sensor_modes_nb; - const struct vgxy61_mode_info *default_mode; - const struct vgxy61_mode_info *current_mode; - bool hflip; - bool vflip; - enum vgxy61_hdr_mode hdr; - u16 expo_long; - u16 expo_short; - u16 expo_max; - u16 expo_min; - u16 vblank; - u16 vblank_min; - u16 frame_length; - u16 digital_gain; - u8 analog_gain; - enum vgxy61_strobe_mode strobe_mode; - u32 pattern; -}; - -static u8 get_bpp_by_code(__u32 code) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(vgxy61_supported_codes); i++) { - if (vgxy61_supported_codes[i].code == code) - return vgxy61_supported_codes[i].bpp; - } - /* Should never happen */ - WARN(1, "Unsupported code %d. default to 8 bpp", code); - return 8; -} - -static u8 get_data_type_by_code(__u32 code) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(vgxy61_supported_codes); i++) { - if (vgxy61_supported_codes[i].code == code) - return vgxy61_supported_codes[i].data_type; - } - /* Should never happen */ - WARN(1, "Unsupported code %d. default to MIPI_CSI2_DT_RAW8 data type", - code); - return MIPI_CSI2_DT_RAW8; -} - -static void compute_pll_parameters_by_freq(u32 freq, u8 *prediv, u8 *mult) -{ - const unsigned int predivs[] = {1, 2, 4}; - unsigned int i; - - /* - * Freq range is [6Mhz-27Mhz] already checked. - * Output of divider should be in [6Mhz-12Mhz[. - */ - for (i = 0; i < ARRAY_SIZE(predivs); i++) { - *prediv = predivs[i]; - if (freq / *prediv < 12 * HZ_PER_MHZ) - break; - } - WARN_ON(i == ARRAY_SIZE(predivs)); - - /* - * Target freq is 804Mhz. Don't change this as it will impact image - * quality. - */ - *mult = ((804 * HZ_PER_MHZ) * (*prediv) + freq / 2) / freq; -} - -static s32 get_pixel_rate(struct vgxy61_dev *sensor) -{ - return div64_u64((u64)sensor->data_rate_in_mbps * sensor->nb_of_lane, - get_bpp_by_code(sensor->fmt.code)); -} - -static inline struct vgxy61_dev *to_vgxy61_dev(struct v4l2_subdev *sd) -{ - return container_of(sd, struct vgxy61_dev, sd); -} - -static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct vgxy61_dev, - ctrl_handler)->sd; -} - -static unsigned int get_chunk_size(struct vgxy61_dev *sensor) -{ - struct i2c_adapter *adapter = sensor->i2c_client->adapter; - int max_write_len = VGXY61_WRITE_MULTIPLE_CHUNK_MAX; - - if (adapter->quirks && adapter->quirks->max_write_len) - max_write_len = adapter->quirks->max_write_len - 2; - - max_write_len = min(max_write_len, VGXY61_WRITE_MULTIPLE_CHUNK_MAX); - - return max(max_write_len, 1); -} - -static int vgxy61_write_array(struct vgxy61_dev *sensor, u32 reg, - unsigned int nb, const u8 *array) -{ - const unsigned int chunk_size = get_chunk_size(sensor); - int ret; - unsigned int sz; - - while (nb) { - sz = min(nb, chunk_size); - ret = regmap_bulk_write(sensor->regmap, CCI_REG_ADDR(reg), - array, sz); - if (ret < 0) - return ret; - nb -= sz; - reg += sz; - array += sz; - } - - return 0; -} - -static int vgxy61_poll_reg(struct vgxy61_dev *sensor, u32 reg, u8 poll_val, - unsigned int timeout_ms) -{ - const unsigned int loop_delay_ms = 10; - u64 val; - int ret; - - return read_poll_timeout(cci_read, ret, - ((ret < 0) || (val == poll_val)), - loop_delay_ms * 1000, timeout_ms * 1000, - false, sensor->regmap, reg, &val, NULL); -} - -static int vgxy61_wait_state(struct vgxy61_dev *sensor, int state, - unsigned int timeout_ms) -{ - return vgxy61_poll_reg(sensor, VGXY61_REG_SYSTEM_FSM, state, - timeout_ms); -} - -static int vgxy61_check_bw(struct vgxy61_dev *sensor) -{ - /* - * Simplification of time needed to send short packets and for the MIPI - * to add transition times (EoT, LPS, and SoT packet delimiters) needed - * by the protocol to go in low power between 2 packets of data. This - * is a mipi IP constant for the sensor. - */ - const unsigned int mipi_margin = 1056; - unsigned int binning_scale = sensor->current_mode->crop.height / - sensor->current_mode->height; - u8 bpp = get_bpp_by_code(sensor->fmt.code); - unsigned int max_bit_per_line; - unsigned int bit_per_line; - u64 line_rate; - - line_rate = sensor->nb_of_lane * (u64)sensor->data_rate_in_mbps * - sensor->line_length; - max_bit_per_line = div64_u64(line_rate, sensor->pclk) - mipi_margin; - bit_per_line = (bpp * sensor->current_mode->width) / binning_scale; - - return bit_per_line > max_bit_per_line ? -EINVAL : 0; -} - -static int vgxy61_apply_exposure(struct vgxy61_dev *sensor) -{ - int ret = 0; - - /* We first set expo to zero to avoid forbidden parameters couple */ - cci_write(sensor->regmap, VGXY61_REG_COARSE_EXPOSURE_SHORT, 0, &ret); - cci_write(sensor->regmap, VGXY61_REG_COARSE_EXPOSURE_LONG, - sensor->expo_long, &ret); - cci_write(sensor->regmap, VGXY61_REG_COARSE_EXPOSURE_SHORT, - sensor->expo_short, &ret); - - return ret; -} - -static int vgxy61_get_regulators(struct vgxy61_dev *sensor) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(vgxy61_supply_name); i++) - sensor->supplies[i].supply = vgxy61_supply_name[i]; - - return devm_regulator_bulk_get(&sensor->i2c_client->dev, - ARRAY_SIZE(vgxy61_supply_name), - sensor->supplies); -} - -static int vgxy61_apply_reset(struct vgxy61_dev *sensor) -{ - gpiod_set_value_cansleep(sensor->reset_gpio, 0); - usleep_range(5000, 10000); - gpiod_set_value_cansleep(sensor->reset_gpio, 1); - usleep_range(5000, 10000); - gpiod_set_value_cansleep(sensor->reset_gpio, 0); - usleep_range(40000, 100000); - return vgxy61_wait_state(sensor, VGXY61_SYSTEM_FSM_SW_STBY, - VGXY61_TIMEOUT_MS); -} - -static void vgxy61_fill_framefmt(struct vgxy61_dev *sensor, - const struct vgxy61_mode_info *mode, - struct v4l2_mbus_framefmt *fmt, u32 code) -{ - fmt->code = code; - fmt->width = mode->width; - fmt->height = mode->height; - fmt->colorspace = V4L2_COLORSPACE_RAW; - fmt->field = V4L2_FIELD_NONE; - fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; - fmt->quantization = V4L2_QUANTIZATION_DEFAULT; - fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; -} - -static int vgxy61_try_fmt_internal(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt, - const struct vgxy61_mode_info **new_mode) -{ - struct vgxy61_dev *sensor = to_vgxy61_dev(sd); - const struct vgxy61_mode_info *mode; - unsigned int index; - - for (index = 0; index < ARRAY_SIZE(vgxy61_supported_codes); index++) { - if (vgxy61_supported_codes[index].code == fmt->code) - break; - } - if (index == ARRAY_SIZE(vgxy61_supported_codes)) - index = 0; - - mode = v4l2_find_nearest_size(sensor->sensor_modes, - sensor->sensor_modes_nb, width, height, - fmt->width, fmt->height); - if (new_mode) - *new_mode = mode; - - vgxy61_fill_framefmt(sensor, mode, fmt, - vgxy61_supported_codes[index].code); - - return 0; -} - -static int vgxy61_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_selection *sel) -{ - struct vgxy61_dev *sensor = to_vgxy61_dev(sd); - - switch (sel->target) { - case V4L2_SEL_TGT_CROP: - sel->r = sensor->current_mode->crop; - return 0; - case V4L2_SEL_TGT_NATIVE_SIZE: - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP_BOUNDS: - sel->r.top = 0; - sel->r.left = 0; - sel->r.width = sensor->sensor_width; - sel->r.height = sensor->sensor_height; - return 0; - } - - return -EINVAL; -} - -static int vgxy61_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->index >= ARRAY_SIZE(vgxy61_supported_codes)) - return -EINVAL; - - code->code = vgxy61_supported_codes[code->index].code; - - return 0; -} - -static int vgxy61_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct vgxy61_dev *sensor = to_vgxy61_dev(sd); - struct v4l2_mbus_framefmt *fmt; - - mutex_lock(&sensor->lock); - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) - fmt = v4l2_subdev_state_get_format(sd_state, format->pad); - else - fmt = &sensor->fmt; - - format->format = *fmt; - - mutex_unlock(&sensor->lock); - - return 0; -} - -static u16 vgxy61_get_vblank_min(struct vgxy61_dev *sensor, - enum vgxy61_hdr_mode hdr) -{ - u16 min_vblank = VGXY61_MIN_FRAME_LENGTH - - sensor->current_mode->crop.height; - /* Ensure the first rule of thumb can't be negative */ - u16 min_vblank_hdr = VGXY61_MIN_EXPOSURE + sensor->rot_term + 1; - - if (hdr != VGXY61_NO_HDR) - return max(min_vblank, min_vblank_hdr); - return min_vblank; -} - -static int vgxy61_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) -{ - struct vgxy61_dev *sensor = to_vgxy61_dev(sd); - - if (fse->index >= sensor->sensor_modes_nb) - return -EINVAL; - - fse->min_width = sensor->sensor_modes[fse->index].width; - fse->max_width = fse->min_width; - fse->min_height = sensor->sensor_modes[fse->index].height; - fse->max_height = fse->min_height; - - return 0; -} - -static int vgxy61_update_analog_gain(struct vgxy61_dev *sensor, u32 target) -{ - sensor->analog_gain = target; - - if (sensor->streaming) - return cci_write(sensor->regmap, VGXY61_REG_ANALOG_GAIN, target, - NULL); - return 0; -} - -static int vgxy61_apply_digital_gain(struct vgxy61_dev *sensor, - u32 digital_gain) -{ - int ret = 0; - - /* - * For a monochrome version, configuring DIGITAL_GAIN_LONG_CH0 and - * DIGITAL_GAIN_SHORT_CH0 is enough to configure the gain of all - * four sub pixels. - */ - cci_write(sensor->regmap, VGXY61_REG_DIGITAL_GAIN_LONG, digital_gain, - &ret); - cci_write(sensor->regmap, VGXY61_REG_DIGITAL_GAIN_SHORT, digital_gain, - &ret); - - return ret; -} - -static int vgxy61_update_digital_gain(struct vgxy61_dev *sensor, u32 target) -{ - sensor->digital_gain = target; - - if (sensor->streaming) - return vgxy61_apply_digital_gain(sensor, sensor->digital_gain); - return 0; -} - -static int vgxy61_apply_patgen(struct vgxy61_dev *sensor, u32 index) -{ - static const u8 index2val[] = { - 0x0, 0x1, 0x2, 0x3, 0x10, 0x11, 0x12, 0x13 - }; - u32 pattern = index2val[index]; - u32 reg = (pattern << VGXY61_PATGEN_LONG_TYPE_SHIFT) | - (pattern << VGXY61_PATGEN_SHORT_TYPE_SHIFT); - - if (pattern) - reg |= VGXY61_PATGEN_LONG_ENABLE | VGXY61_PATGEN_SHORT_ENABLE; - return cci_write(sensor->regmap, VGXY61_REG_PATGEN_CTRL, reg, NULL); -} - -static int vgxy61_update_patgen(struct vgxy61_dev *sensor, u32 pattern) -{ - sensor->pattern = pattern; - - if (sensor->streaming) - return vgxy61_apply_patgen(sensor, sensor->pattern); - return 0; -} - -static int vgxy61_apply_gpiox_strobe_mode(struct vgxy61_dev *sensor, - enum vgxy61_strobe_mode mode, - unsigned int idx) -{ - static const u8 index2val[] = {0x0, 0x1, 0x3}; - u16 mask, val; - - mask = 0xf << (idx * VGXY61_SIGNALS_GPIO_ID_SHIFT); - val = index2val[mode] << (idx * VGXY61_SIGNALS_GPIO_ID_SHIFT); - - return cci_update_bits(sensor->regmap, VGXY61_REG_SIGNALS_CTRL, - mask, val, NULL); -} - -static int vgxy61_update_gpios_strobe_mode(struct vgxy61_dev *sensor, - enum vgxy61_hdr_mode hdr) -{ - unsigned int i; - int ret; - - switch (hdr) { - case VGXY61_HDR_LINEAR: - sensor->strobe_mode = VGXY61_STROBE_ENABLED; - break; - case VGXY61_HDR_SUB: - case VGXY61_NO_HDR: - sensor->strobe_mode = VGXY61_STROBE_LONG; - break; - default: - /* Should never happen */ - WARN_ON(true); - break; - } - - if (!sensor->streaming) - return 0; - - for (i = 0; i < VGXY61_NB_GPIOS; i++) { - ret = vgxy61_apply_gpiox_strobe_mode(sensor, - sensor->strobe_mode, - i); - if (ret) - return ret; - } - - return 0; -} - -static int vgxy61_update_gpios_strobe_polarity(struct vgxy61_dev *sensor, - bool polarity) -{ - int ret = 0; - - if (sensor->streaming) - return -EBUSY; - - cci_write(sensor->regmap, VGXY61_REG_GPIO_0_CTRL, polarity << 1, &ret); - cci_write(sensor->regmap, VGXY61_REG_GPIO_1_CTRL, polarity << 1, &ret); - cci_write(sensor->regmap, VGXY61_REG_GPIO_2_CTRL, polarity << 1, &ret); - cci_write(sensor->regmap, VGXY61_REG_GPIO_3_CTRL, polarity << 1, &ret); - cci_write(sensor->regmap, VGXY61_REG_SIGNALS_POLARITY_CTRL, polarity, - &ret); - - return ret; -} - -static u32 vgxy61_get_expo_long_max(struct vgxy61_dev *sensor, - unsigned int short_expo_ratio) -{ - u32 first_rot_max_expo, second_rot_max_expo, third_rot_max_expo; - - /* Apply sensor's rules of thumb */ - /* - * Short exposure + height must be less than frame length to avoid bad - * pixel line at the botom of the image - */ - first_rot_max_expo = - ((sensor->frame_length - sensor->current_mode->crop.height - - sensor->rot_term) * short_expo_ratio) - 1; - - /* - * Total exposition time must be less than frame length to avoid sensor - * crash - */ - second_rot_max_expo = - (((sensor->frame_length - VGXY61_EXPOS_ROT_TERM) * - short_expo_ratio) / (short_expo_ratio + 1)) - 1; - - /* - * Short exposure times 71 must be less than frame length to avoid - * sensor crash - */ - third_rot_max_expo = (sensor->frame_length / 71) * short_expo_ratio; - - /* Take the minimum from all rules */ - return min(min(first_rot_max_expo, second_rot_max_expo), - third_rot_max_expo); -} - -static int vgxy61_update_exposure(struct vgxy61_dev *sensor, u16 new_expo_long, - enum vgxy61_hdr_mode hdr) -{ - struct i2c_client *client = sensor->i2c_client; - u16 new_expo_short = 0; - u16 expo_short_max = 0; - u16 expo_long_min = VGXY61_MIN_EXPOSURE; - u16 expo_long_max = 0; - - /* Compute short exposure according to hdr mode and long exposure */ - switch (hdr) { - case VGXY61_HDR_LINEAR: - /* - * Take ratio into account for minimal exposures in - * VGXY61_HDR_LINEAR - */ - expo_long_min = VGXY61_MIN_EXPOSURE * VGXY61_HDR_LINEAR_RATIO; - new_expo_long = max(expo_long_min, new_expo_long); - - expo_long_max = - vgxy61_get_expo_long_max(sensor, - VGXY61_HDR_LINEAR_RATIO); - expo_short_max = (expo_long_max + - (VGXY61_HDR_LINEAR_RATIO / 2)) / - VGXY61_HDR_LINEAR_RATIO; - new_expo_short = (new_expo_long + - (VGXY61_HDR_LINEAR_RATIO / 2)) / - VGXY61_HDR_LINEAR_RATIO; - break; - case VGXY61_HDR_SUB: - new_expo_long = max(expo_long_min, new_expo_long); - - expo_long_max = vgxy61_get_expo_long_max(sensor, 1); - /* Short and long are the same in VGXY61_HDR_SUB */ - expo_short_max = expo_long_max; - new_expo_short = new_expo_long; - break; - case VGXY61_NO_HDR: - new_expo_long = max(expo_long_min, new_expo_long); - - /* - * As short expo is 0 here, only the second rule of thumb - * applies, see vgxy61_get_expo_long_max for more - */ - expo_long_max = sensor->frame_length - VGXY61_EXPOS_ROT_TERM; - break; - default: - /* Should never happen */ - WARN_ON(true); - break; - } - - /* If this happens, something is wrong with formulas */ - WARN_ON(expo_long_min > expo_long_max); - - if (new_expo_long > expo_long_max) { - dev_warn(&client->dev, "Exposure %d too high, clamping to %d\n", - new_expo_long, expo_long_max); - new_expo_long = expo_long_max; - new_expo_short = expo_short_max; - } - - sensor->expo_long = new_expo_long; - sensor->expo_short = new_expo_short; - sensor->expo_max = expo_long_max; - sensor->expo_min = expo_long_min; - - if (sensor->streaming) - return vgxy61_apply_exposure(sensor); - return 0; -} - -static int vgxy61_apply_framelength(struct vgxy61_dev *sensor) -{ - return cci_write(sensor->regmap, VGXY61_REG_FRAME_LENGTH, - sensor->frame_length, NULL); -} - -static int vgxy61_update_vblank(struct vgxy61_dev *sensor, u16 vblank, - enum vgxy61_hdr_mode hdr) -{ - int ret; - - sensor->vblank_min = vgxy61_get_vblank_min(sensor, hdr); - sensor->vblank = max(sensor->vblank_min, vblank); - sensor->frame_length = sensor->current_mode->crop.height + - sensor->vblank; - - /* Update exposure according to vblank */ - ret = vgxy61_update_exposure(sensor, sensor->expo_long, hdr); - if (ret) - return ret; - - if (sensor->streaming) - return vgxy61_apply_framelength(sensor); - return 0; -} - -static int vgxy61_apply_hdr(struct vgxy61_dev *sensor, - enum vgxy61_hdr_mode index) -{ - static const u8 index2val[] = {0x1, 0x4, 0xa}; - - return cci_write(sensor->regmap, VGXY61_REG_HDR_CTRL, index2val[index], - NULL); -} - -static int vgxy61_update_hdr(struct vgxy61_dev *sensor, - enum vgxy61_hdr_mode index) -{ - int ret; - - /* - * vblank and short exposure change according to HDR mode, do it first - * as it can violate sensors 'rule of thumbs' and therefore will require - * to change the long exposure. - */ - ret = vgxy61_update_vblank(sensor, sensor->vblank, index); - if (ret) - return ret; - - /* Update strobe mode according to HDR */ - ret = vgxy61_update_gpios_strobe_mode(sensor, index); - if (ret) - return ret; - - sensor->hdr = index; - - if (sensor->streaming) - return vgxy61_apply_hdr(sensor, sensor->hdr); - return 0; -} - -static int vgxy61_apply_settings(struct vgxy61_dev *sensor) -{ - int ret; - unsigned int i; - - ret = vgxy61_apply_hdr(sensor, sensor->hdr); - if (ret) - return ret; - - ret = vgxy61_apply_framelength(sensor); - if (ret) - return ret; - - ret = vgxy61_apply_exposure(sensor); - if (ret) - return ret; - - ret = cci_write(sensor->regmap, VGXY61_REG_ANALOG_GAIN, - sensor->analog_gain, NULL); - if (ret) - return ret; - ret = vgxy61_apply_digital_gain(sensor, sensor->digital_gain); - if (ret) - return ret; - - ret = cci_write(sensor->regmap, VGXY61_REG_ORIENTATION, - sensor->hflip | (sensor->vflip << 1), NULL); - if (ret) - return ret; - - ret = vgxy61_apply_patgen(sensor, sensor->pattern); - if (ret) - return ret; - - for (i = 0; i < VGXY61_NB_GPIOS; i++) { - ret = vgxy61_apply_gpiox_strobe_mode(sensor, - sensor->strobe_mode, i); - if (ret) - return ret; - } - - return 0; -} - -static int vgxy61_stream_enable(struct vgxy61_dev *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); - const struct v4l2_rect *crop = &sensor->current_mode->crop; - int ret = 0; - - ret = vgxy61_check_bw(sensor); - if (ret) - return ret; - - ret = pm_runtime_resume_and_get(&client->dev); - if (ret) - return ret; - - cci_write(sensor->regmap, VGXY61_REG_FORMAT_CTRL, - get_bpp_by_code(sensor->fmt.code), &ret); - cci_write(sensor->regmap, VGXY61_REG_OIF_ROI0_CTRL, - get_data_type_by_code(sensor->fmt.code), &ret); - - cci_write(sensor->regmap, VGXY61_REG_READOUT_CTRL, - sensor->current_mode->bin_mode, &ret); - cci_write(sensor->regmap, VGXY61_REG_ROI0_START_H, crop->left, &ret); - cci_write(sensor->regmap, VGXY61_REG_ROI0_END_H, - crop->left + crop->width - 1, &ret); - cci_write(sensor->regmap, VGXY61_REG_ROI0_START_V, crop->top, &ret); - cci_write(sensor->regmap, VGXY61_REG_ROI0_END_V, - crop->top + crop->height - 1, &ret); - if (ret) - goto err_rpm_put; - - ret = vgxy61_apply_settings(sensor); - if (ret) - goto err_rpm_put; - - ret = cci_write(sensor->regmap, VGXY61_REG_STREAMING, - VGXY61_STREAMING_REQ_START, NULL); - if (ret) - goto err_rpm_put; - - ret = vgxy61_poll_reg(sensor, VGXY61_REG_STREAMING, - VGXY61_STREAMING_NO_REQ, VGXY61_TIMEOUT_MS); - if (ret) - goto err_rpm_put; - - ret = vgxy61_wait_state(sensor, VGXY61_SYSTEM_FSM_STREAMING, - VGXY61_TIMEOUT_MS); - if (ret) - goto err_rpm_put; - - /* vflip and hflip cannot change during streaming */ - __v4l2_ctrl_grab(sensor->vflip_ctrl, true); - __v4l2_ctrl_grab(sensor->hflip_ctrl, true); - - return 0; - -err_rpm_put: - pm_runtime_put(&client->dev); - return ret; -} - -static int vgxy61_stream_disable(struct vgxy61_dev *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); - int ret; - - ret = cci_write(sensor->regmap, VGXY61_REG_STREAMING, - VGXY61_STREAMING_REQ_STOP, NULL); - if (ret) - goto err_str_dis; - - ret = vgxy61_poll_reg(sensor, VGXY61_REG_STREAMING, - VGXY61_STREAMING_NO_REQ, 2000); - if (ret) - goto err_str_dis; - - ret = vgxy61_wait_state(sensor, VGXY61_SYSTEM_FSM_SW_STBY, - VGXY61_TIMEOUT_MS); - if (ret) - goto err_str_dis; - - __v4l2_ctrl_grab(sensor->vflip_ctrl, false); - __v4l2_ctrl_grab(sensor->hflip_ctrl, false); - -err_str_dis: - if (ret) - WARN(1, "Can't disable stream"); - pm_runtime_put(&client->dev); - - return ret; -} - -static int vgxy61_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct vgxy61_dev *sensor = to_vgxy61_dev(sd); - int ret = 0; - - mutex_lock(&sensor->lock); - - ret = enable ? vgxy61_stream_enable(sensor) : - vgxy61_stream_disable(sensor); - if (!ret) - sensor->streaming = enable; - - mutex_unlock(&sensor->lock); - - return ret; -} - -static int vgxy61_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct vgxy61_dev *sensor = to_vgxy61_dev(sd); - const struct vgxy61_mode_info *new_mode; - struct v4l2_mbus_framefmt *fmt; - int ret; - - mutex_lock(&sensor->lock); - - if (sensor->streaming) { - ret = -EBUSY; - goto out; - } - - ret = vgxy61_try_fmt_internal(sd, &format->format, &new_mode); - if (ret) - goto out; - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - fmt = v4l2_subdev_state_get_format(sd_state, 0); - *fmt = format->format; - } else if (sensor->current_mode != new_mode || - sensor->fmt.code != format->format.code) { - fmt = &sensor->fmt; - *fmt = format->format; - - sensor->current_mode = new_mode; - - /* Reset vblank and framelength to default */ - ret = vgxy61_update_vblank(sensor, - VGXY61_FRAME_LENGTH_DEF - - new_mode->crop.height, - sensor->hdr); - - /* Update controls to reflect new mode */ - __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_ctrl, - get_pixel_rate(sensor)); - __v4l2_ctrl_modify_range(sensor->vblank_ctrl, - sensor->vblank_min, - 0xffff - new_mode->crop.height, - 1, sensor->vblank); - __v4l2_ctrl_s_ctrl(sensor->vblank_ctrl, sensor->vblank); - __v4l2_ctrl_modify_range(sensor->expo_ctrl, sensor->expo_min, - sensor->expo_max, 1, - sensor->expo_long); - } - -out: - mutex_unlock(&sensor->lock); - - return ret; -} - -static int vgxy61_init_state(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state) -{ - struct vgxy61_dev *sensor = to_vgxy61_dev(sd); - struct v4l2_subdev_format fmt = { 0 }; - - vgxy61_fill_framefmt(sensor, sensor->current_mode, &fmt.format, - VGXY61_MEDIA_BUS_FMT_DEF); - - return vgxy61_set_fmt(sd, sd_state, &fmt); -} - -static int vgxy61_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = ctrl_to_sd(ctrl); - struct vgxy61_dev *sensor = to_vgxy61_dev(sd); - const struct vgxy61_mode_info *cur_mode = sensor->current_mode; - int ret; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - ret = vgxy61_update_exposure(sensor, ctrl->val, sensor->hdr); - ctrl->val = sensor->expo_long; - break; - case V4L2_CID_ANALOGUE_GAIN: - ret = vgxy61_update_analog_gain(sensor, ctrl->val); - break; - case V4L2_CID_DIGITAL_GAIN: - ret = vgxy61_update_digital_gain(sensor, ctrl->val); - break; - case V4L2_CID_VFLIP: - case V4L2_CID_HFLIP: - if (sensor->streaming) { - ret = -EBUSY; - break; - } - if (ctrl->id == V4L2_CID_VFLIP) - sensor->vflip = ctrl->val; - if (ctrl->id == V4L2_CID_HFLIP) - sensor->hflip = ctrl->val; - ret = 0; - break; - case V4L2_CID_TEST_PATTERN: - ret = vgxy61_update_patgen(sensor, ctrl->val); - break; - case V4L2_CID_HDR_SENSOR_MODE: - ret = vgxy61_update_hdr(sensor, ctrl->val); - /* Update vblank and exposure controls to match new hdr */ - __v4l2_ctrl_modify_range(sensor->vblank_ctrl, - sensor->vblank_min, - 0xffff - cur_mode->crop.height, - 1, sensor->vblank); - __v4l2_ctrl_modify_range(sensor->expo_ctrl, sensor->expo_min, - sensor->expo_max, 1, - sensor->expo_long); - break; - case V4L2_CID_VBLANK: - ret = vgxy61_update_vblank(sensor, ctrl->val, sensor->hdr); - /* Update exposure control to match new vblank */ - __v4l2_ctrl_modify_range(sensor->expo_ctrl, sensor->expo_min, - sensor->expo_max, 1, - sensor->expo_long); - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - -static const struct v4l2_ctrl_ops vgxy61_ctrl_ops = { - .s_ctrl = vgxy61_s_ctrl, -}; - -static int vgxy61_init_controls(struct vgxy61_dev *sensor) -{ - const struct v4l2_ctrl_ops *ops = &vgxy61_ctrl_ops; - struct v4l2_ctrl_handler *hdl = &sensor->ctrl_handler; - const struct vgxy61_mode_info *cur_mode = sensor->current_mode; - struct v4l2_fwnode_device_properties props; - struct v4l2_ctrl *ctrl; - int ret; - - v4l2_ctrl_handler_init(hdl, 16); - /* We can use our own mutex for the ctrl lock */ - hdl->lock = &sensor->lock; - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, 0, 0x1c, 1, - sensor->analog_gain); - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN, 0, 0xfff, 1, - sensor->digital_gain); - v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(vgxy61_test_pattern_menu) - 1, - 0, 0, vgxy61_test_pattern_menu); - ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, 0, - sensor->line_length, 1, - sensor->line_length - cur_mode->width); - if (ctrl) - ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - ctrl = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ, - ARRAY_SIZE(link_freq) - 1, 0, link_freq); - if (ctrl) - ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_HDR_SENSOR_MODE, - ARRAY_SIZE(vgxy61_hdr_mode_menu) - 1, 0, - VGXY61_NO_HDR, vgxy61_hdr_mode_menu); - - /* - * Keep a pointer to these controls as we need to update them when - * setting the format - */ - sensor->pixel_rate_ctrl = v4l2_ctrl_new_std(hdl, ops, - V4L2_CID_PIXEL_RATE, 1, - INT_MAX, 1, - get_pixel_rate(sensor)); - if (sensor->pixel_rate_ctrl) - sensor->pixel_rate_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - sensor->expo_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, - sensor->expo_min, - sensor->expo_max, 1, - sensor->expo_long); - sensor->vblank_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, - sensor->vblank_min, - 0xffff - cur_mode->crop.height, - 1, sensor->vblank); - sensor->vflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, - 0, 1, 1, sensor->vflip); - sensor->hflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, - 0, 1, 1, sensor->hflip); - - if (hdl->error) { - ret = hdl->error; - goto free_ctrls; - } - - ret = v4l2_fwnode_device_parse(&sensor->i2c_client->dev, &props); - if (ret) - goto free_ctrls; - - ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &props); - if (ret) - goto free_ctrls; - - sensor->sd.ctrl_handler = hdl; - return 0; - -free_ctrls: - v4l2_ctrl_handler_free(hdl); - return ret; -} - -static const struct v4l2_subdev_core_ops vgxy61_core_ops = { - .subscribe_event = v4l2_ctrl_subdev_subscribe_event, - .unsubscribe_event = v4l2_event_subdev_unsubscribe, -}; - -static const struct v4l2_subdev_video_ops vgxy61_video_ops = { - .s_stream = vgxy61_s_stream, -}; - -static const struct v4l2_subdev_pad_ops vgxy61_pad_ops = { - .enum_mbus_code = vgxy61_enum_mbus_code, - .get_fmt = vgxy61_get_fmt, - .set_fmt = vgxy61_set_fmt, - .get_selection = vgxy61_get_selection, - .enum_frame_size = vgxy61_enum_frame_size, -}; - -static const struct v4l2_subdev_ops vgxy61_subdev_ops = { - .core = &vgxy61_core_ops, - .video = &vgxy61_video_ops, - .pad = &vgxy61_pad_ops, -}; - -static const struct v4l2_subdev_internal_ops vgxy61_internal_ops = { - .init_state = vgxy61_init_state, -}; - -static const struct media_entity_operations vgxy61_subdev_entity_ops = { - .link_validate = v4l2_subdev_link_validate, -}; - -static int vgxy61_tx_from_ep(struct vgxy61_dev *sensor, - struct fwnode_handle *handle) -{ - struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; - struct i2c_client *client = sensor->i2c_client; - u32 log2phy[VGXY61_NB_POLARITIES] = {~0, ~0, ~0, ~0, ~0}; - u32 phy2log[VGXY61_NB_POLARITIES] = {~0, ~0, ~0, ~0, ~0}; - int polarities[VGXY61_NB_POLARITIES] = {0, 0, 0, 0, 0}; - int l_nb; - unsigned int p, l, i; - int ret; - - ret = v4l2_fwnode_endpoint_alloc_parse(handle, &ep); - if (ret) - return -EINVAL; - - l_nb = ep.bus.mipi_csi2.num_data_lanes; - if (l_nb != 1 && l_nb != 2 && l_nb != 4) { - dev_err(&client->dev, "invalid data lane number %d\n", l_nb); - goto error_ep; - } - - /* Build log2phy, phy2log and polarities from ep info */ - log2phy[0] = ep.bus.mipi_csi2.clock_lane; - phy2log[log2phy[0]] = 0; - for (l = 1; l < l_nb + 1; l++) { - log2phy[l] = ep.bus.mipi_csi2.data_lanes[l - 1]; - phy2log[log2phy[l]] = l; - } - /* - * Then fill remaining slots for every physical slot to have something - * valid for hardware stuff. - */ - for (p = 0; p < VGXY61_NB_POLARITIES; p++) { - if (phy2log[p] != ~0) - continue; - phy2log[p] = l; - log2phy[l] = p; - l++; - } - for (l = 0; l < l_nb + 1; l++) - polarities[l] = ep.bus.mipi_csi2.lane_polarities[l]; - - if (log2phy[0] != 0) { - dev_err(&client->dev, "clk lane must be map to physical lane 0\n"); - goto error_ep; - } - sensor->oif_ctrl = (polarities[4] << 15) + ((phy2log[4] - 1) << 13) + - (polarities[3] << 12) + ((phy2log[3] - 1) << 10) + - (polarities[2] << 9) + ((phy2log[2] - 1) << 7) + - (polarities[1] << 6) + ((phy2log[1] - 1) << 4) + - (polarities[0] << 3) + - l_nb; - sensor->nb_of_lane = l_nb; - - dev_dbg(&client->dev, "tx uses %d lanes", l_nb); - for (i = 0; i < VGXY61_NB_POLARITIES; i++) { - dev_dbg(&client->dev, "log2phy[%d] = %d\n", i, log2phy[i]); - dev_dbg(&client->dev, "phy2log[%d] = %d\n", i, phy2log[i]); - dev_dbg(&client->dev, "polarity[%d] = %d\n", i, polarities[i]); - } - dev_dbg(&client->dev, "oif_ctrl = 0x%04x\n", sensor->oif_ctrl); - - v4l2_fwnode_endpoint_free(&ep); - - return 0; - -error_ep: - v4l2_fwnode_endpoint_free(&ep); - - return -EINVAL; -} - -static int vgxy61_configure(struct vgxy61_dev *sensor) -{ - u32 sensor_freq; - u8 prediv, mult; - u64 line_length; - int ret = 0; - - compute_pll_parameters_by_freq(sensor->clk_freq, &prediv, &mult); - sensor_freq = (mult * sensor->clk_freq) / prediv; - /* Frequency to data rate is 1:1 ratio for MIPI */ - sensor->data_rate_in_mbps = sensor_freq; - /* Video timing ISP path (pixel clock) requires 804/5 mhz = 160 mhz */ - sensor->pclk = sensor_freq / 5; - - cci_read(sensor->regmap, VGXY61_REG_LINE_LENGTH, &line_length, &ret); - if (ret < 0) - return ret; - sensor->line_length = (u16)line_length; - cci_write(sensor->regmap, VGXY61_REG_EXT_CLOCK, sensor->clk_freq, &ret); - cci_write(sensor->regmap, VGXY61_REG_CLK_PLL_PREDIV, prediv, &ret); - cci_write(sensor->regmap, VGXY61_REG_CLK_SYS_PLL_MULT, mult, &ret); - cci_write(sensor->regmap, VGXY61_REG_OIF_CTRL, sensor->oif_ctrl, &ret); - cci_write(sensor->regmap, VGXY61_REG_FRAME_CONTENT_CTRL, 0, &ret); - cci_write(sensor->regmap, VGXY61_REG_BYPASS_CTRL, 4, &ret); - if (ret) - return ret; - vgxy61_update_gpios_strobe_polarity(sensor, sensor->gpios_polarity); - /* Set pattern generator solid to middle value */ - cci_write(sensor->regmap, VGXY61_REG_PATGEN_LONG_DATA_GR, 0x800, &ret); - cci_write(sensor->regmap, VGXY61_REG_PATGEN_LONG_DATA_R, 0x800, &ret); - cci_write(sensor->regmap, VGXY61_REG_PATGEN_LONG_DATA_B, 0x800, &ret); - cci_write(sensor->regmap, VGXY61_REG_PATGEN_LONG_DATA_GB, 0x800, &ret); - cci_write(sensor->regmap, VGXY61_REG_PATGEN_SHORT_DATA_GR, 0x800, &ret); - cci_write(sensor->regmap, VGXY61_REG_PATGEN_SHORT_DATA_R, 0x800, &ret); - cci_write(sensor->regmap, VGXY61_REG_PATGEN_SHORT_DATA_B, 0x800, &ret); - cci_write(sensor->regmap, VGXY61_REG_PATGEN_SHORT_DATA_GB, 0x800, &ret); - if (ret) - return ret; - - return 0; -} - -static int vgxy61_patch(struct vgxy61_dev *sensor) -{ - struct i2c_client *client = sensor->i2c_client; - u64 patch; - int ret; - - ret = vgxy61_write_array(sensor, VGXY61_REG_FWPATCH_START_ADDR, - sizeof(patch_array), patch_array); - cci_write(sensor->regmap, VGXY61_REG_STBY, 0x10, &ret); - if (ret) - return ret; - - ret = vgxy61_poll_reg(sensor, VGXY61_REG_STBY, 0, VGXY61_TIMEOUT_MS); - cci_read(sensor->regmap, VGXY61_REG_FWPATCH_REVISION, &patch, &ret); - if (ret < 0) - return ret; - - if (patch != (VGXY61_FWPATCH_REVISION_MAJOR << 12) + - (VGXY61_FWPATCH_REVISION_MINOR << 8) + - VGXY61_FWPATCH_REVISION_MICRO) { - dev_err(&client->dev, - "bad patch version expected %d.%d.%d got %u.%u.%u\n", - VGXY61_FWPATCH_REVISION_MAJOR, - VGXY61_FWPATCH_REVISION_MINOR, - VGXY61_FWPATCH_REVISION_MICRO, - (u16)patch >> 12, ((u16)patch >> 8) & 0x0f, (u16)patch & 0xff); - return -ENODEV; - } - dev_dbg(&client->dev, "patch %u.%u.%u applied\n", - (u16)patch >> 12, ((u16)patch >> 8) & 0x0f, (u16)patch & 0xff); - - return 0; -} - -static int vgxy61_detect_cut_version(struct vgxy61_dev *sensor) -{ - struct i2c_client *client = sensor->i2c_client; - u64 device_rev; - int ret; - - ret = cci_read(sensor->regmap, VGXY61_REG_REVISION, &device_rev, NULL); - if (ret < 0) - return ret; - - switch (device_rev >> 8) { - case 0xA: - dev_dbg(&client->dev, "Cut1 detected\n"); - dev_err(&client->dev, "Cut1 not supported by this driver\n"); - return -ENODEV; - case 0xB: - dev_dbg(&client->dev, "Cut2 detected\n"); - return 0; - case 0xC: - dev_dbg(&client->dev, "Cut3 detected\n"); - return 0; - default: - dev_err(&client->dev, "Unable to detect cut version\n"); - return -ENODEV; - } -} - -static int vgxy61_detect(struct vgxy61_dev *sensor) -{ - struct i2c_client *client = sensor->i2c_client; - u64 st, id = 0; - int ret; - - ret = cci_read(sensor->regmap, VGXY61_REG_MODEL_ID, &id, NULL); - if (ret < 0) - return ret; - if (id != VG5661_MODEL_ID && id != VG5761_MODEL_ID) { - dev_warn(&client->dev, "Unsupported sensor id %x\n", (u16)id); - return -ENODEV; - } - dev_dbg(&client->dev, "detected sensor id = 0x%04x\n", (u16)id); - sensor->id = id; - - ret = vgxy61_wait_state(sensor, VGXY61_SYSTEM_FSM_SW_STBY, - VGXY61_TIMEOUT_MS); - if (ret) - return ret; - - ret = cci_read(sensor->regmap, VGXY61_REG_NVM, &st, NULL); - if (ret < 0) - return st; - if (st != VGXY61_NVM_OK) - dev_warn(&client->dev, "Bad nvm state got %u\n", (u8)st); - - ret = vgxy61_detect_cut_version(sensor); - if (ret) - return ret; - - return 0; -} - -/* Power/clock management functions */ -static int vgxy61_power_on(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct vgxy61_dev *sensor = to_vgxy61_dev(sd); - int ret; - - ret = regulator_bulk_enable(ARRAY_SIZE(vgxy61_supply_name), - sensor->supplies); - if (ret) { - dev_err(&client->dev, "failed to enable regulators %d\n", ret); - return ret; - } - - ret = clk_prepare_enable(sensor->xclk); - if (ret) { - dev_err(&client->dev, "failed to enable clock %d\n", ret); - goto disable_bulk; - } - - if (sensor->reset_gpio) { - ret = vgxy61_apply_reset(sensor); - if (ret) { - dev_err(&client->dev, "sensor reset failed %d\n", ret); - goto disable_clock; - } - } - - ret = vgxy61_detect(sensor); - if (ret) { - dev_err(&client->dev, "sensor detect failed %d\n", ret); - goto disable_clock; - } - - ret = vgxy61_patch(sensor); - if (ret) { - dev_err(&client->dev, "sensor patch failed %d\n", ret); - goto disable_clock; - } - - ret = vgxy61_configure(sensor); - if (ret) { - dev_err(&client->dev, "sensor configuration failed %d\n", ret); - goto disable_clock; - } - - return 0; - -disable_clock: - clk_disable_unprepare(sensor->xclk); -disable_bulk: - regulator_bulk_disable(ARRAY_SIZE(vgxy61_supply_name), - sensor->supplies); - - return ret; -} - -static int vgxy61_power_off(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct vgxy61_dev *sensor = to_vgxy61_dev(sd); - - clk_disable_unprepare(sensor->xclk); - regulator_bulk_disable(ARRAY_SIZE(vgxy61_supply_name), - sensor->supplies); - return 0; -} - -static void vgxy61_fill_sensor_param(struct vgxy61_dev *sensor) -{ - if (sensor->id == VG5761_MODEL_ID) { - sensor->sensor_width = VGX761_WIDTH; - sensor->sensor_height = VGX761_HEIGHT; - sensor->sensor_modes = vgx761_mode_data; - sensor->sensor_modes_nb = ARRAY_SIZE(vgx761_mode_data); - sensor->default_mode = &vgx761_mode_data[VGX761_DEFAULT_MODE]; - sensor->rot_term = VGX761_SHORT_ROT_TERM; - } else if (sensor->id == VG5661_MODEL_ID) { - sensor->sensor_width = VGX661_WIDTH; - sensor->sensor_height = VGX661_HEIGHT; - sensor->sensor_modes = vgx661_mode_data; - sensor->sensor_modes_nb = ARRAY_SIZE(vgx661_mode_data); - sensor->default_mode = &vgx661_mode_data[VGX661_DEFAULT_MODE]; - sensor->rot_term = VGX661_SHORT_ROT_TERM; - } else { - /* Should never happen */ - WARN_ON(true); - } - sensor->current_mode = sensor->default_mode; -} - -static int vgxy61_probe(struct i2c_client *client) -{ - struct device *dev = &client->dev; - struct fwnode_handle *handle; - struct vgxy61_dev *sensor; - int ret; - - sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); - if (!sensor) - return -ENOMEM; - - sensor->i2c_client = client; - sensor->streaming = false; - sensor->hdr = VGXY61_NO_HDR; - sensor->expo_long = 200; - sensor->expo_short = 0; - sensor->hflip = false; - sensor->vflip = false; - sensor->analog_gain = 0; - sensor->digital_gain = 256; - - sensor->regmap = devm_cci_regmap_init_i2c(client, 16); - if (IS_ERR(sensor->regmap)) { - ret = PTR_ERR(sensor->regmap); - return dev_err_probe(dev, ret, "Failed to init regmap\n"); - } - - handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0); - if (!handle) { - dev_err(dev, "handle node not found\n"); - return -EINVAL; - } - - ret = vgxy61_tx_from_ep(sensor, handle); - fwnode_handle_put(handle); - if (ret) { - dev_err(dev, "Failed to parse handle %d\n", ret); - return ret; - } - - sensor->xclk = devm_clk_get(dev, NULL); - if (IS_ERR(sensor->xclk)) { - dev_err(dev, "failed to get xclk\n"); - return PTR_ERR(sensor->xclk); - } - sensor->clk_freq = clk_get_rate(sensor->xclk); - if (sensor->clk_freq < 6 * HZ_PER_MHZ || - sensor->clk_freq > 27 * HZ_PER_MHZ) { - dev_err(dev, "Only 6Mhz-27Mhz clock range supported. provide %lu MHz\n", - sensor->clk_freq / HZ_PER_MHZ); - return -EINVAL; - } - sensor->gpios_polarity = - device_property_read_bool(dev, "st,strobe-gpios-polarity"); - - v4l2_i2c_subdev_init(&sensor->sd, client, &vgxy61_subdev_ops); - sensor->sd.internal_ops = &vgxy61_internal_ops; - sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | - V4L2_SUBDEV_FL_HAS_EVENTS; - sensor->pad.flags = MEDIA_PAD_FL_SOURCE; - sensor->sd.entity.ops = &vgxy61_subdev_entity_ops; - sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; - - sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", - GPIOD_OUT_HIGH); - - ret = vgxy61_get_regulators(sensor); - if (ret) { - dev_err(&client->dev, "failed to get regulators %d\n", ret); - return ret; - } - - ret = vgxy61_power_on(dev); - if (ret) - return ret; - - vgxy61_fill_sensor_param(sensor); - vgxy61_fill_framefmt(sensor, sensor->current_mode, &sensor->fmt, - VGXY61_MEDIA_BUS_FMT_DEF); - - mutex_init(&sensor->lock); - - ret = vgxy61_update_hdr(sensor, sensor->hdr); - if (ret) - goto error_power_off; - - ret = vgxy61_init_controls(sensor); - if (ret) { - dev_err(&client->dev, "controls initialization failed %d\n", - ret); - goto error_power_off; - } - - ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); - if (ret) { - dev_err(&client->dev, "pads init failed %d\n", ret); - goto error_handler_free; - } - - /* Enable runtime PM and turn off the device */ - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - pm_runtime_idle(dev); - - ret = v4l2_async_register_subdev(&sensor->sd); - if (ret) { - dev_err(&client->dev, "async subdev register failed %d\n", ret); - goto error_pm_runtime; - } - - pm_runtime_set_autosuspend_delay(&client->dev, 1000); - pm_runtime_use_autosuspend(&client->dev); - - dev_dbg(&client->dev, "vgxy61 probe successfully\n"); - - return 0; - -error_pm_runtime: - pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); - media_entity_cleanup(&sensor->sd.entity); -error_handler_free: - v4l2_ctrl_handler_free(sensor->sd.ctrl_handler); -error_power_off: - mutex_destroy(&sensor->lock); - vgxy61_power_off(dev); - - return ret; -} - -static void vgxy61_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct vgxy61_dev *sensor = to_vgxy61_dev(sd); - - v4l2_async_unregister_subdev(&sensor->sd); - mutex_destroy(&sensor->lock); - media_entity_cleanup(&sensor->sd.entity); - - pm_runtime_disable(&client->dev); - if (!pm_runtime_status_suspended(&client->dev)) - vgxy61_power_off(&client->dev); - pm_runtime_set_suspended(&client->dev); -} - -static const struct of_device_id vgxy61_dt_ids[] = { - { .compatible = "st,st-vgxy61" }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, vgxy61_dt_ids); - -static const struct dev_pm_ops vgxy61_pm_ops = { - SET_RUNTIME_PM_OPS(vgxy61_power_off, vgxy61_power_on, NULL) -}; - -static struct i2c_driver vgxy61_i2c_driver = { - .driver = { - .name = "st-vgxy61", - .of_match_table = vgxy61_dt_ids, - .pm = &vgxy61_pm_ops, - }, - .probe = vgxy61_probe, - .remove = vgxy61_remove, -}; - -module_i2c_driver(vgxy61_i2c_driver); - -MODULE_AUTHOR("Benjamin Mugnier "); -MODULE_AUTHOR("Mickael Guene "); -MODULE_AUTHOR("Sylvain Petinot "); -MODULE_DESCRIPTION("VGXY61 camera subdev driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/vgxy61.c b/drivers/media/i2c/vgxy61.c new file mode 100644 index 000000000000..30378e962016 --- /dev/null +++ b/drivers/media/i2c/vgxy61.c @@ -0,0 +1,1895 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for VGXY61 global shutter sensor family driver + * + * Copyright (C) 2022 STMicroelectronics SA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define VGXY61_REG_MODEL_ID CCI_REG16_LE(0x0000) +#define VG5661_MODEL_ID 0x5661 +#define VG5761_MODEL_ID 0x5761 +#define VGXY61_REG_REVISION CCI_REG16_LE(0x0002) +#define VGXY61_REG_FWPATCH_REVISION CCI_REG16_LE(0x0014) +#define VGXY61_REG_FWPATCH_START_ADDR CCI_REG8(0x2000) +#define VGXY61_REG_SYSTEM_FSM CCI_REG8(0x0020) +#define VGXY61_SYSTEM_FSM_SW_STBY 0x03 +#define VGXY61_SYSTEM_FSM_STREAMING 0x04 +#define VGXY61_REG_NVM CCI_REG8(0x0023) +#define VGXY61_NVM_OK 0x04 +#define VGXY61_REG_STBY CCI_REG8(0x0201) +#define VGXY61_STBY_NO_REQ 0 +#define VGXY61_STBY_REQ_TMP_READ BIT(2) +#define VGXY61_REG_STREAMING CCI_REG8(0x0202) +#define VGXY61_STREAMING_NO_REQ 0 +#define VGXY61_STREAMING_REQ_STOP BIT(0) +#define VGXY61_STREAMING_REQ_START BIT(1) +#define VGXY61_REG_EXT_CLOCK CCI_REG32_LE(0x0220) +#define VGXY61_REG_CLK_PLL_PREDIV CCI_REG8(0x0224) +#define VGXY61_REG_CLK_SYS_PLL_MULT CCI_REG8(0x0225) +#define VGXY61_REG_GPIO_0_CTRL CCI_REG8(0x0236) +#define VGXY61_REG_GPIO_1_CTRL CCI_REG8(0x0237) +#define VGXY61_REG_GPIO_2_CTRL CCI_REG8(0x0238) +#define VGXY61_REG_GPIO_3_CTRL CCI_REG8(0x0239) +#define VGXY61_REG_SIGNALS_POLARITY_CTRL CCI_REG8(0x023b) +#define VGXY61_REG_LINE_LENGTH CCI_REG16_LE(0x0300) +#define VGXY61_REG_ORIENTATION CCI_REG8(0x0302) +#define VGXY61_REG_VT_CTRL CCI_REG8(0x0304) +#define VGXY61_REG_FORMAT_CTRL CCI_REG8(0x0305) +#define VGXY61_REG_OIF_CTRL CCI_REG16_LE(0x0306) +#define VGXY61_REG_OIF_ROI0_CTRL CCI_REG8(0x030a) +#define VGXY61_REG_ROI0_START_H CCI_REG16_LE(0x0400) +#define VGXY61_REG_ROI0_START_V CCI_REG16_LE(0x0402) +#define VGXY61_REG_ROI0_END_H CCI_REG16_LE(0x0404) +#define VGXY61_REG_ROI0_END_V CCI_REG16_LE(0x0406) +#define VGXY61_REG_PATGEN_CTRL CCI_REG32_LE(0x0440) +#define VGXY61_PATGEN_LONG_ENABLE BIT(16) +#define VGXY61_PATGEN_SHORT_ENABLE BIT(0) +#define VGXY61_PATGEN_LONG_TYPE_SHIFT 18 +#define VGXY61_PATGEN_SHORT_TYPE_SHIFT 4 +#define VGXY61_REG_FRAME_CONTENT_CTRL CCI_REG8(0x0478) +#define VGXY61_REG_COARSE_EXPOSURE_LONG CCI_REG16_LE(0x0500) +#define VGXY61_REG_COARSE_EXPOSURE_SHORT CCI_REG16_LE(0x0504) +#define VGXY61_REG_ANALOG_GAIN CCI_REG8(0x0508) +#define VGXY61_REG_DIGITAL_GAIN_LONG CCI_REG16_LE(0x050a) +#define VGXY61_REG_DIGITAL_GAIN_SHORT CCI_REG16_LE(0x0512) +#define VGXY61_REG_FRAME_LENGTH CCI_REG16_LE(0x051a) +#define VGXY61_REG_SIGNALS_CTRL CCI_REG16_LE(0x0522) +#define VGXY61_SIGNALS_GPIO_ID_SHIFT 4 +#define VGXY61_REG_READOUT_CTRL CCI_REG8(0x0530) +#define VGXY61_REG_HDR_CTRL CCI_REG8(0x0532) +#define VGXY61_REG_PATGEN_LONG_DATA_GR CCI_REG16_LE(0x092c) +#define VGXY61_REG_PATGEN_LONG_DATA_R CCI_REG16_LE(0x092e) +#define VGXY61_REG_PATGEN_LONG_DATA_B CCI_REG16_LE(0x0930) +#define VGXY61_REG_PATGEN_LONG_DATA_GB CCI_REG16_LE(0x0932) +#define VGXY61_REG_PATGEN_SHORT_DATA_GR CCI_REG16_LE(0x0950) +#define VGXY61_REG_PATGEN_SHORT_DATA_R CCI_REG16_LE(0x0952) +#define VGXY61_REG_PATGEN_SHORT_DATA_B CCI_REG16_LE(0x0954) +#define VGXY61_REG_PATGEN_SHORT_DATA_GB CCI_REG16_LE(0x0956) +#define VGXY61_REG_BYPASS_CTRL CCI_REG8(0x0a60) + +#define VGX661_WIDTH 1464 +#define VGX661_HEIGHT 1104 +#define VGX761_WIDTH 1944 +#define VGX761_HEIGHT 1204 +#define VGX661_DEFAULT_MODE 1 +#define VGX761_DEFAULT_MODE 1 +#define VGX661_SHORT_ROT_TERM 93 +#define VGX761_SHORT_ROT_TERM 90 +#define VGXY61_EXPOS_ROT_TERM 66 +#define VGXY61_WRITE_MULTIPLE_CHUNK_MAX 16 +#define VGXY61_NB_GPIOS 4 +#define VGXY61_NB_POLARITIES 5 +#define VGXY61_FRAME_LENGTH_DEF 1313 +#define VGXY61_MIN_FRAME_LENGTH 1288 +#define VGXY61_MIN_EXPOSURE 10 +#define VGXY61_HDR_LINEAR_RATIO 10 +#define VGXY61_TIMEOUT_MS 500 +#define VGXY61_MEDIA_BUS_FMT_DEF MEDIA_BUS_FMT_Y8_1X8 + +#define VGXY61_FWPATCH_REVISION_MAJOR 2 +#define VGXY61_FWPATCH_REVISION_MINOR 0 +#define VGXY61_FWPATCH_REVISION_MICRO 5 + +static const u8 patch_array[] = { + 0xbf, 0x00, 0x05, 0x20, 0x06, 0x01, 0xe0, 0xe0, 0x04, 0x80, 0xe6, 0x45, + 0xed, 0x6f, 0xfe, 0xff, 0x14, 0x80, 0x1f, 0x84, 0x10, 0x42, 0x05, 0x7c, + 0x01, 0xc4, 0x1e, 0x80, 0xb6, 0x42, 0x00, 0xe0, 0x1e, 0x82, 0x1e, 0xc0, + 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x00, 0xfa, 0x86, 0x0d, 0x70, 0xe1, + 0x04, 0x98, 0x15, 0x00, 0x28, 0xe0, 0x14, 0x02, 0x08, 0xfc, 0x15, 0x40, + 0x28, 0xe0, 0x98, 0x58, 0xe0, 0xef, 0x04, 0x98, 0x0e, 0x04, 0x00, 0xf0, + 0x15, 0x00, 0x28, 0xe0, 0x19, 0xc8, 0x15, 0x40, 0x28, 0xe0, 0xc6, 0x41, + 0xfc, 0xe0, 0x14, 0x80, 0x1f, 0x84, 0x14, 0x02, 0xa0, 0xfc, 0x1e, 0x80, + 0x14, 0x80, 0x14, 0x02, 0x80, 0xfb, 0x14, 0x02, 0xe0, 0xfc, 0x1e, 0x80, + 0x14, 0xc0, 0x1f, 0x84, 0x14, 0x02, 0xa4, 0xfc, 0x1e, 0xc0, 0x14, 0xc0, + 0x14, 0x02, 0x80, 0xfb, 0x14, 0x02, 0xe4, 0xfc, 0x1e, 0xc0, 0x0c, 0x0c, + 0x00, 0xf2, 0x93, 0xdd, 0x86, 0x00, 0xf8, 0xe0, 0x04, 0x80, 0xc6, 0x03, + 0x70, 0xe1, 0x0e, 0x84, 0x93, 0xdd, 0xc3, 0xc1, 0x0c, 0x04, 0x00, 0xfa, + 0x6b, 0x80, 0x06, 0x40, 0x6c, 0xe1, 0x04, 0x80, 0x09, 0x00, 0xe0, 0xe0, + 0x0b, 0xa1, 0x95, 0x84, 0x05, 0x0c, 0x1c, 0xe0, 0x86, 0x02, 0xf9, 0x60, + 0xe0, 0xcf, 0x78, 0x6e, 0x80, 0xef, 0x25, 0x0c, 0x18, 0xe0, 0x05, 0x4c, + 0x1c, 0xe0, 0x86, 0x02, 0xf9, 0x60, 0xe0, 0xcf, 0x0b, 0x84, 0xd8, 0x6d, + 0x80, 0xef, 0x05, 0x4c, 0x18, 0xe0, 0x04, 0xd8, 0x0b, 0xa5, 0x95, 0x84, + 0x05, 0x0c, 0x2c, 0xe0, 0x06, 0x02, 0x01, 0x60, 0xe0, 0xce, 0x18, 0x6d, + 0x80, 0xef, 0x25, 0x0c, 0x30, 0xe0, 0x05, 0x4c, 0x2c, 0xe0, 0x06, 0x02, + 0x01, 0x60, 0xe0, 0xce, 0x0b, 0x84, 0x78, 0x6c, 0x80, 0xef, 0x05, 0x4c, + 0x30, 0xe0, 0x0c, 0x0c, 0x00, 0xf2, 0x93, 0xdd, 0x46, 0x01, 0x70, 0xe1, + 0x08, 0x80, 0x0b, 0xa1, 0x08, 0x5c, 0x00, 0xda, 0x06, 0x01, 0x68, 0xe1, + 0x04, 0x80, 0x4a, 0x40, 0x84, 0xe0, 0x08, 0x5c, 0x00, 0x9a, 0x06, 0x01, + 0xe0, 0xe0, 0x04, 0x80, 0x15, 0x00, 0x60, 0xe0, 0x19, 0xc4, 0x15, 0x40, + 0x60, 0xe0, 0x15, 0x00, 0x78, 0xe0, 0x19, 0xc4, 0x15, 0x40, 0x78, 0xe0, + 0x93, 0xdd, 0xc3, 0xc1, 0x46, 0x01, 0x70, 0xe1, 0x08, 0x80, 0x0b, 0xa1, + 0x08, 0x5c, 0x00, 0xda, 0x06, 0x01, 0x68, 0xe1, 0x04, 0x80, 0x4a, 0x40, + 0x84, 0xe0, 0x08, 0x5c, 0x00, 0x9a, 0x06, 0x01, 0xe0, 0xe0, 0x14, 0x80, + 0x25, 0x02, 0x54, 0xe0, 0x29, 0xc4, 0x25, 0x42, 0x54, 0xe0, 0x24, 0x80, + 0x35, 0x04, 0x6c, 0xe0, 0x39, 0xc4, 0x35, 0x44, 0x6c, 0xe0, 0x25, 0x02, + 0x64, 0xe0, 0x29, 0xc4, 0x25, 0x42, 0x64, 0xe0, 0x04, 0x80, 0x15, 0x00, + 0x7c, 0xe0, 0x19, 0xc4, 0x15, 0x40, 0x7c, 0xe0, 0x93, 0xdd, 0xc3, 0xc1, + 0x4c, 0x04, 0x7c, 0xfa, 0x86, 0x40, 0x98, 0xe0, 0x14, 0x80, 0x1b, 0xa1, + 0x06, 0x00, 0x00, 0xc0, 0x08, 0x42, 0x38, 0xdc, 0x08, 0x64, 0xa0, 0xef, + 0x86, 0x42, 0x3c, 0xe0, 0x68, 0x49, 0x80, 0xef, 0x6b, 0x80, 0x78, 0x53, + 0xc8, 0xef, 0xc6, 0x54, 0x6c, 0xe1, 0x7b, 0x80, 0xb5, 0x14, 0x0c, 0xf8, + 0x05, 0x14, 0x14, 0xf8, 0x1a, 0xac, 0x8a, 0x80, 0x0b, 0x90, 0x38, 0x55, + 0x80, 0xef, 0x1a, 0xae, 0x17, 0xc2, 0x03, 0x82, 0x88, 0x65, 0x80, 0xef, + 0x1b, 0x80, 0x0b, 0x8e, 0x68, 0x65, 0x80, 0xef, 0x9b, 0x80, 0x0b, 0x8c, + 0x08, 0x65, 0x80, 0xef, 0x6b, 0x80, 0x0b, 0x92, 0x1b, 0x8c, 0x98, 0x64, + 0x80, 0xef, 0x1a, 0xec, 0x9b, 0x80, 0x0b, 0x90, 0x95, 0x54, 0x10, 0xe0, + 0xa8, 0x53, 0x80, 0xef, 0x1a, 0xee, 0x17, 0xc2, 0x03, 0x82, 0xf8, 0x63, + 0x80, 0xef, 0x1b, 0x80, 0x0b, 0x8e, 0xd8, 0x63, 0x80, 0xef, 0x1b, 0x8c, + 0x68, 0x63, 0x80, 0xef, 0x6b, 0x80, 0x0b, 0x92, 0x65, 0x54, 0x14, 0xe0, + 0x08, 0x65, 0x84, 0xef, 0x68, 0x63, 0x80, 0xef, 0x7b, 0x80, 0x0b, 0x8c, + 0xa8, 0x64, 0x84, 0xef, 0x08, 0x63, 0x80, 0xef, 0x14, 0xe8, 0x46, 0x44, + 0x94, 0xe1, 0x24, 0x88, 0x4a, 0x4e, 0x04, 0xe0, 0x14, 0xea, 0x1a, 0x04, + 0x08, 0xe0, 0x0a, 0x40, 0x84, 0xed, 0x0c, 0x04, 0x00, 0xe2, 0x4a, 0x40, + 0x04, 0xe0, 0x19, 0x16, 0xc0, 0xe0, 0x0a, 0x40, 0x84, 0xed, 0x21, 0x54, + 0x60, 0xe0, 0x0c, 0x04, 0x00, 0xe2, 0x1b, 0xa5, 0x0e, 0xea, 0x01, 0x89, + 0x21, 0x54, 0x64, 0xe0, 0x7e, 0xe8, 0x65, 0x82, 0x1b, 0xa7, 0x26, 0x00, + 0x00, 0x80, 0xa5, 0x82, 0x1b, 0xa9, 0x65, 0x82, 0x1b, 0xa3, 0x01, 0x85, + 0x16, 0x00, 0x00, 0xc0, 0x01, 0x54, 0x04, 0xf8, 0x06, 0xaa, 0x01, 0x83, + 0x06, 0xa8, 0x65, 0x81, 0x06, 0xa8, 0x01, 0x54, 0x04, 0xf8, 0x01, 0x83, + 0x06, 0xaa, 0x09, 0x14, 0x18, 0xf8, 0x0b, 0xa1, 0x05, 0x84, 0xc6, 0x42, + 0xd4, 0xe0, 0x14, 0x84, 0x01, 0x83, 0x01, 0x54, 0x60, 0xe0, 0x01, 0x54, + 0x64, 0xe0, 0x0b, 0x02, 0x90, 0xe0, 0x10, 0x02, 0x90, 0xe5, 0x01, 0x54, + 0x88, 0xe0, 0xb5, 0x81, 0xc6, 0x40, 0xd4, 0xe0, 0x14, 0x80, 0x0b, 0x02, + 0xe0, 0xe4, 0x10, 0x02, 0x31, 0x66, 0x02, 0xc0, 0x01, 0x54, 0x88, 0xe0, + 0x1a, 0x84, 0x29, 0x14, 0x10, 0xe0, 0x1c, 0xaa, 0x2b, 0xa1, 0xf5, 0x82, + 0x25, 0x14, 0x10, 0xf8, 0x2b, 0x04, 0xa8, 0xe0, 0x20, 0x44, 0x0d, 0x70, + 0x03, 0xc0, 0x2b, 0xa1, 0x04, 0x00, 0x80, 0x9a, 0x02, 0x40, 0x84, 0x90, + 0x03, 0x54, 0x04, 0x80, 0x4c, 0x0c, 0x7c, 0xf2, 0x93, 0xdd, 0x00, 0x00, + 0x02, 0xa9, 0x00, 0x00, 0x64, 0x4a, 0x40, 0x00, 0x08, 0x2d, 0x58, 0xe0, + 0xa8, 0x98, 0x40, 0x00, 0x28, 0x07, 0x34, 0xe0, 0x05, 0xb9, 0x00, 0x00, + 0x28, 0x00, 0x41, 0x05, 0x88, 0x00, 0x41, 0x3c, 0x98, 0x00, 0x41, 0x52, + 0x04, 0x01, 0x41, 0x79, 0x3c, 0x01, 0x41, 0x6a, 0x3d, 0xfe, 0x00, 0x00, +}; + +static const char * const vgxy61_test_pattern_menu[] = { + "Disabled", + "Solid", + "Colorbar", + "Gradbar", + "Hgrey", + "Vgrey", + "Dgrey", + "PN28", +}; + +static const char * const vgxy61_hdr_mode_menu[] = { + "HDR linearize", + "HDR substraction", + "No HDR", +}; + +static const char * const vgxy61_supply_name[] = { + "VCORE", + "VDDIO", + "VANA", +}; + +static const s64 link_freq[] = { + /* + * MIPI output freq is 804Mhz / 2, as it uses both rising edge and + * falling edges to send data + */ + 402000000ULL +}; + +enum vgxy61_bin_mode { + VGXY61_BIN_MODE_NORMAL, + VGXY61_BIN_MODE_DIGITAL_X2, + VGXY61_BIN_MODE_DIGITAL_X4, +}; + +enum vgxy61_hdr_mode { + VGXY61_HDR_LINEAR, + VGXY61_HDR_SUB, + VGXY61_NO_HDR, +}; + +enum vgxy61_strobe_mode { + VGXY61_STROBE_DISABLED, + VGXY61_STROBE_LONG, + VGXY61_STROBE_ENABLED, +}; + +struct vgxy61_mode_info { + u32 width; + u32 height; + enum vgxy61_bin_mode bin_mode; + struct v4l2_rect crop; +}; + +struct vgxy61_fmt_desc { + u32 code; + u8 bpp; + u8 data_type; +}; + +static const struct vgxy61_fmt_desc vgxy61_supported_codes[] = { + { + .code = MEDIA_BUS_FMT_Y8_1X8, + .bpp = 8, + .data_type = MIPI_CSI2_DT_RAW8, + }, + { + .code = MEDIA_BUS_FMT_Y10_1X10, + .bpp = 10, + .data_type = MIPI_CSI2_DT_RAW10, + }, + { + .code = MEDIA_BUS_FMT_Y12_1X12, + .bpp = 12, + .data_type = MIPI_CSI2_DT_RAW12, + }, + { + .code = MEDIA_BUS_FMT_Y14_1X14, + .bpp = 14, + .data_type = MIPI_CSI2_DT_RAW14, + }, + { + .code = MEDIA_BUS_FMT_Y16_1X16, + .bpp = 16, + .data_type = MIPI_CSI2_DT_RAW16, + }, +}; + +static const struct vgxy61_mode_info vgx661_mode_data[] = { + { + .width = VGX661_WIDTH, + .height = VGX661_HEIGHT, + .bin_mode = VGXY61_BIN_MODE_NORMAL, + .crop = { + .left = 0, + .top = 0, + .width = VGX661_WIDTH, + .height = VGX661_HEIGHT, + }, + }, + { + .width = 1280, + .height = 720, + .bin_mode = VGXY61_BIN_MODE_NORMAL, + .crop = { + .left = 92, + .top = 192, + .width = 1280, + .height = 720, + }, + }, + { + .width = 640, + .height = 480, + .bin_mode = VGXY61_BIN_MODE_DIGITAL_X2, + .crop = { + .left = 92, + .top = 72, + .width = 1280, + .height = 960, + }, + }, + { + .width = 320, + .height = 240, + .bin_mode = VGXY61_BIN_MODE_DIGITAL_X4, + .crop = { + .left = 92, + .top = 72, + .width = 1280, + .height = 960, + }, + }, +}; + +static const struct vgxy61_mode_info vgx761_mode_data[] = { + { + .width = VGX761_WIDTH, + .height = VGX761_HEIGHT, + .bin_mode = VGXY61_BIN_MODE_NORMAL, + .crop = { + .left = 0, + .top = 0, + .width = VGX761_WIDTH, + .height = VGX761_HEIGHT, + }, + }, + { + .width = 1920, + .height = 1080, + .bin_mode = VGXY61_BIN_MODE_NORMAL, + .crop = { + .left = 12, + .top = 62, + .width = 1920, + .height = 1080, + }, + }, + { + .width = 1280, + .height = 720, + .bin_mode = VGXY61_BIN_MODE_NORMAL, + .crop = { + .left = 332, + .top = 242, + .width = 1280, + .height = 720, + }, + }, + { + .width = 640, + .height = 480, + .bin_mode = VGXY61_BIN_MODE_DIGITAL_X2, + .crop = { + .left = 332, + .top = 122, + .width = 1280, + .height = 960, + }, + }, + { + .width = 320, + .height = 240, + .bin_mode = VGXY61_BIN_MODE_DIGITAL_X4, + .crop = { + .left = 332, + .top = 122, + .width = 1280, + .height = 960, + }, + }, +}; + +struct vgxy61_dev { + struct i2c_client *i2c_client; + struct regmap *regmap; + struct v4l2_subdev sd; + struct media_pad pad; + struct regulator_bulk_data supplies[ARRAY_SIZE(vgxy61_supply_name)]; + struct gpio_desc *reset_gpio; + struct clk *xclk; + u32 clk_freq; + u16 id; + u16 sensor_width; + u16 sensor_height; + u16 oif_ctrl; + unsigned int nb_of_lane; + u32 data_rate_in_mbps; + u32 pclk; + u16 line_length; + u16 rot_term; + bool gpios_polarity; + /* Lock to protect all members below */ + struct mutex lock; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *pixel_rate_ctrl; + struct v4l2_ctrl *expo_ctrl; + struct v4l2_ctrl *vblank_ctrl; + struct v4l2_ctrl *vflip_ctrl; + struct v4l2_ctrl *hflip_ctrl; + bool streaming; + struct v4l2_mbus_framefmt fmt; + const struct vgxy61_mode_info *sensor_modes; + unsigned int sensor_modes_nb; + const struct vgxy61_mode_info *default_mode; + const struct vgxy61_mode_info *current_mode; + bool hflip; + bool vflip; + enum vgxy61_hdr_mode hdr; + u16 expo_long; + u16 expo_short; + u16 expo_max; + u16 expo_min; + u16 vblank; + u16 vblank_min; + u16 frame_length; + u16 digital_gain; + u8 analog_gain; + enum vgxy61_strobe_mode strobe_mode; + u32 pattern; +}; + +static u8 get_bpp_by_code(__u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(vgxy61_supported_codes); i++) { + if (vgxy61_supported_codes[i].code == code) + return vgxy61_supported_codes[i].bpp; + } + /* Should never happen */ + WARN(1, "Unsupported code %d. default to 8 bpp", code); + return 8; +} + +static u8 get_data_type_by_code(__u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(vgxy61_supported_codes); i++) { + if (vgxy61_supported_codes[i].code == code) + return vgxy61_supported_codes[i].data_type; + } + /* Should never happen */ + WARN(1, "Unsupported code %d. default to MIPI_CSI2_DT_RAW8 data type", + code); + return MIPI_CSI2_DT_RAW8; +} + +static void compute_pll_parameters_by_freq(u32 freq, u8 *prediv, u8 *mult) +{ + const unsigned int predivs[] = {1, 2, 4}; + unsigned int i; + + /* + * Freq range is [6Mhz-27Mhz] already checked. + * Output of divider should be in [6Mhz-12Mhz[. + */ + for (i = 0; i < ARRAY_SIZE(predivs); i++) { + *prediv = predivs[i]; + if (freq / *prediv < 12 * HZ_PER_MHZ) + break; + } + WARN_ON(i == ARRAY_SIZE(predivs)); + + /* + * Target freq is 804Mhz. Don't change this as it will impact image + * quality. + */ + *mult = ((804 * HZ_PER_MHZ) * (*prediv) + freq / 2) / freq; +} + +static s32 get_pixel_rate(struct vgxy61_dev *sensor) +{ + return div64_u64((u64)sensor->data_rate_in_mbps * sensor->nb_of_lane, + get_bpp_by_code(sensor->fmt.code)); +} + +static inline struct vgxy61_dev *to_vgxy61_dev(struct v4l2_subdev *sd) +{ + return container_of(sd, struct vgxy61_dev, sd); +} + +static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct vgxy61_dev, + ctrl_handler)->sd; +} + +static unsigned int get_chunk_size(struct vgxy61_dev *sensor) +{ + struct i2c_adapter *adapter = sensor->i2c_client->adapter; + int max_write_len = VGXY61_WRITE_MULTIPLE_CHUNK_MAX; + + if (adapter->quirks && adapter->quirks->max_write_len) + max_write_len = adapter->quirks->max_write_len - 2; + + max_write_len = min(max_write_len, VGXY61_WRITE_MULTIPLE_CHUNK_MAX); + + return max(max_write_len, 1); +} + +static int vgxy61_write_array(struct vgxy61_dev *sensor, u32 reg, + unsigned int nb, const u8 *array) +{ + const unsigned int chunk_size = get_chunk_size(sensor); + int ret; + unsigned int sz; + + while (nb) { + sz = min(nb, chunk_size); + ret = regmap_bulk_write(sensor->regmap, CCI_REG_ADDR(reg), + array, sz); + if (ret < 0) + return ret; + nb -= sz; + reg += sz; + array += sz; + } + + return 0; +} + +static int vgxy61_poll_reg(struct vgxy61_dev *sensor, u32 reg, u8 poll_val, + unsigned int timeout_ms) +{ + const unsigned int loop_delay_ms = 10; + u64 val; + int ret; + + return read_poll_timeout(cci_read, ret, + ((ret < 0) || (val == poll_val)), + loop_delay_ms * 1000, timeout_ms * 1000, + false, sensor->regmap, reg, &val, NULL); +} + +static int vgxy61_wait_state(struct vgxy61_dev *sensor, int state, + unsigned int timeout_ms) +{ + return vgxy61_poll_reg(sensor, VGXY61_REG_SYSTEM_FSM, state, + timeout_ms); +} + +static int vgxy61_check_bw(struct vgxy61_dev *sensor) +{ + /* + * Simplification of time needed to send short packets and for the MIPI + * to add transition times (EoT, LPS, and SoT packet delimiters) needed + * by the protocol to go in low power between 2 packets of data. This + * is a mipi IP constant for the sensor. + */ + const unsigned int mipi_margin = 1056; + unsigned int binning_scale = sensor->current_mode->crop.height / + sensor->current_mode->height; + u8 bpp = get_bpp_by_code(sensor->fmt.code); + unsigned int max_bit_per_line; + unsigned int bit_per_line; + u64 line_rate; + + line_rate = sensor->nb_of_lane * (u64)sensor->data_rate_in_mbps * + sensor->line_length; + max_bit_per_line = div64_u64(line_rate, sensor->pclk) - mipi_margin; + bit_per_line = (bpp * sensor->current_mode->width) / binning_scale; + + return bit_per_line > max_bit_per_line ? -EINVAL : 0; +} + +static int vgxy61_apply_exposure(struct vgxy61_dev *sensor) +{ + int ret = 0; + + /* We first set expo to zero to avoid forbidden parameters couple */ + cci_write(sensor->regmap, VGXY61_REG_COARSE_EXPOSURE_SHORT, 0, &ret); + cci_write(sensor->regmap, VGXY61_REG_COARSE_EXPOSURE_LONG, + sensor->expo_long, &ret); + cci_write(sensor->regmap, VGXY61_REG_COARSE_EXPOSURE_SHORT, + sensor->expo_short, &ret); + + return ret; +} + +static int vgxy61_get_regulators(struct vgxy61_dev *sensor) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(vgxy61_supply_name); i++) + sensor->supplies[i].supply = vgxy61_supply_name[i]; + + return devm_regulator_bulk_get(&sensor->i2c_client->dev, + ARRAY_SIZE(vgxy61_supply_name), + sensor->supplies); +} + +static int vgxy61_apply_reset(struct vgxy61_dev *sensor) +{ + gpiod_set_value_cansleep(sensor->reset_gpio, 0); + usleep_range(5000, 10000); + gpiod_set_value_cansleep(sensor->reset_gpio, 1); + usleep_range(5000, 10000); + gpiod_set_value_cansleep(sensor->reset_gpio, 0); + usleep_range(40000, 100000); + return vgxy61_wait_state(sensor, VGXY61_SYSTEM_FSM_SW_STBY, + VGXY61_TIMEOUT_MS); +} + +static void vgxy61_fill_framefmt(struct vgxy61_dev *sensor, + const struct vgxy61_mode_info *mode, + struct v4l2_mbus_framefmt *fmt, u32 code) +{ + fmt->code = code; + fmt->width = mode->width; + fmt->height = mode->height; + fmt->colorspace = V4L2_COLORSPACE_RAW; + fmt->field = V4L2_FIELD_NONE; + fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + fmt->quantization = V4L2_QUANTIZATION_DEFAULT; + fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; +} + +static int vgxy61_try_fmt_internal(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt, + const struct vgxy61_mode_info **new_mode) +{ + struct vgxy61_dev *sensor = to_vgxy61_dev(sd); + const struct vgxy61_mode_info *mode; + unsigned int index; + + for (index = 0; index < ARRAY_SIZE(vgxy61_supported_codes); index++) { + if (vgxy61_supported_codes[index].code == fmt->code) + break; + } + if (index == ARRAY_SIZE(vgxy61_supported_codes)) + index = 0; + + mode = v4l2_find_nearest_size(sensor->sensor_modes, + sensor->sensor_modes_nb, width, height, + fmt->width, fmt->height); + if (new_mode) + *new_mode = mode; + + vgxy61_fill_framefmt(sensor, mode, fmt, + vgxy61_supported_codes[index].code); + + return 0; +} + +static int vgxy61_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + struct vgxy61_dev *sensor = to_vgxy61_dev(sd); + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = sensor->current_mode->crop; + return 0; + case V4L2_SEL_TGT_NATIVE_SIZE: + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = sensor->sensor_width; + sel->r.height = sensor->sensor_height; + return 0; + } + + return -EINVAL; +} + +static int vgxy61_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(vgxy61_supported_codes)) + return -EINVAL; + + code->code = vgxy61_supported_codes[code->index].code; + + return 0; +} + +static int vgxy61_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct vgxy61_dev *sensor = to_vgxy61_dev(sd); + struct v4l2_mbus_framefmt *fmt; + + mutex_lock(&sensor->lock); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); + else + fmt = &sensor->fmt; + + format->format = *fmt; + + mutex_unlock(&sensor->lock); + + return 0; +} + +static u16 vgxy61_get_vblank_min(struct vgxy61_dev *sensor, + enum vgxy61_hdr_mode hdr) +{ + u16 min_vblank = VGXY61_MIN_FRAME_LENGTH - + sensor->current_mode->crop.height; + /* Ensure the first rule of thumb can't be negative */ + u16 min_vblank_hdr = VGXY61_MIN_EXPOSURE + sensor->rot_term + 1; + + if (hdr != VGXY61_NO_HDR) + return max(min_vblank, min_vblank_hdr); + return min_vblank; +} + +static int vgxy61_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vgxy61_dev *sensor = to_vgxy61_dev(sd); + + if (fse->index >= sensor->sensor_modes_nb) + return -EINVAL; + + fse->min_width = sensor->sensor_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = sensor->sensor_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int vgxy61_update_analog_gain(struct vgxy61_dev *sensor, u32 target) +{ + sensor->analog_gain = target; + + if (sensor->streaming) + return cci_write(sensor->regmap, VGXY61_REG_ANALOG_GAIN, target, + NULL); + return 0; +} + +static int vgxy61_apply_digital_gain(struct vgxy61_dev *sensor, + u32 digital_gain) +{ + int ret = 0; + + /* + * For a monochrome version, configuring DIGITAL_GAIN_LONG_CH0 and + * DIGITAL_GAIN_SHORT_CH0 is enough to configure the gain of all + * four sub pixels. + */ + cci_write(sensor->regmap, VGXY61_REG_DIGITAL_GAIN_LONG, digital_gain, + &ret); + cci_write(sensor->regmap, VGXY61_REG_DIGITAL_GAIN_SHORT, digital_gain, + &ret); + + return ret; +} + +static int vgxy61_update_digital_gain(struct vgxy61_dev *sensor, u32 target) +{ + sensor->digital_gain = target; + + if (sensor->streaming) + return vgxy61_apply_digital_gain(sensor, sensor->digital_gain); + return 0; +} + +static int vgxy61_apply_patgen(struct vgxy61_dev *sensor, u32 index) +{ + static const u8 index2val[] = { + 0x0, 0x1, 0x2, 0x3, 0x10, 0x11, 0x12, 0x13 + }; + u32 pattern = index2val[index]; + u32 reg = (pattern << VGXY61_PATGEN_LONG_TYPE_SHIFT) | + (pattern << VGXY61_PATGEN_SHORT_TYPE_SHIFT); + + if (pattern) + reg |= VGXY61_PATGEN_LONG_ENABLE | VGXY61_PATGEN_SHORT_ENABLE; + return cci_write(sensor->regmap, VGXY61_REG_PATGEN_CTRL, reg, NULL); +} + +static int vgxy61_update_patgen(struct vgxy61_dev *sensor, u32 pattern) +{ + sensor->pattern = pattern; + + if (sensor->streaming) + return vgxy61_apply_patgen(sensor, sensor->pattern); + return 0; +} + +static int vgxy61_apply_gpiox_strobe_mode(struct vgxy61_dev *sensor, + enum vgxy61_strobe_mode mode, + unsigned int idx) +{ + static const u8 index2val[] = {0x0, 0x1, 0x3}; + u16 mask, val; + + mask = 0xf << (idx * VGXY61_SIGNALS_GPIO_ID_SHIFT); + val = index2val[mode] << (idx * VGXY61_SIGNALS_GPIO_ID_SHIFT); + + return cci_update_bits(sensor->regmap, VGXY61_REG_SIGNALS_CTRL, + mask, val, NULL); +} + +static int vgxy61_update_gpios_strobe_mode(struct vgxy61_dev *sensor, + enum vgxy61_hdr_mode hdr) +{ + unsigned int i; + int ret; + + switch (hdr) { + case VGXY61_HDR_LINEAR: + sensor->strobe_mode = VGXY61_STROBE_ENABLED; + break; + case VGXY61_HDR_SUB: + case VGXY61_NO_HDR: + sensor->strobe_mode = VGXY61_STROBE_LONG; + break; + default: + /* Should never happen */ + WARN_ON(true); + break; + } + + if (!sensor->streaming) + return 0; + + for (i = 0; i < VGXY61_NB_GPIOS; i++) { + ret = vgxy61_apply_gpiox_strobe_mode(sensor, + sensor->strobe_mode, + i); + if (ret) + return ret; + } + + return 0; +} + +static int vgxy61_update_gpios_strobe_polarity(struct vgxy61_dev *sensor, + bool polarity) +{ + int ret = 0; + + if (sensor->streaming) + return -EBUSY; + + cci_write(sensor->regmap, VGXY61_REG_GPIO_0_CTRL, polarity << 1, &ret); + cci_write(sensor->regmap, VGXY61_REG_GPIO_1_CTRL, polarity << 1, &ret); + cci_write(sensor->regmap, VGXY61_REG_GPIO_2_CTRL, polarity << 1, &ret); + cci_write(sensor->regmap, VGXY61_REG_GPIO_3_CTRL, polarity << 1, &ret); + cci_write(sensor->regmap, VGXY61_REG_SIGNALS_POLARITY_CTRL, polarity, + &ret); + + return ret; +} + +static u32 vgxy61_get_expo_long_max(struct vgxy61_dev *sensor, + unsigned int short_expo_ratio) +{ + u32 first_rot_max_expo, second_rot_max_expo, third_rot_max_expo; + + /* Apply sensor's rules of thumb */ + /* + * Short exposure + height must be less than frame length to avoid bad + * pixel line at the botom of the image + */ + first_rot_max_expo = + ((sensor->frame_length - sensor->current_mode->crop.height - + sensor->rot_term) * short_expo_ratio) - 1; + + /* + * Total exposition time must be less than frame length to avoid sensor + * crash + */ + second_rot_max_expo = + (((sensor->frame_length - VGXY61_EXPOS_ROT_TERM) * + short_expo_ratio) / (short_expo_ratio + 1)) - 1; + + /* + * Short exposure times 71 must be less than frame length to avoid + * sensor crash + */ + third_rot_max_expo = (sensor->frame_length / 71) * short_expo_ratio; + + /* Take the minimum from all rules */ + return min(min(first_rot_max_expo, second_rot_max_expo), + third_rot_max_expo); +} + +static int vgxy61_update_exposure(struct vgxy61_dev *sensor, u16 new_expo_long, + enum vgxy61_hdr_mode hdr) +{ + struct i2c_client *client = sensor->i2c_client; + u16 new_expo_short = 0; + u16 expo_short_max = 0; + u16 expo_long_min = VGXY61_MIN_EXPOSURE; + u16 expo_long_max = 0; + + /* Compute short exposure according to hdr mode and long exposure */ + switch (hdr) { + case VGXY61_HDR_LINEAR: + /* + * Take ratio into account for minimal exposures in + * VGXY61_HDR_LINEAR + */ + expo_long_min = VGXY61_MIN_EXPOSURE * VGXY61_HDR_LINEAR_RATIO; + new_expo_long = max(expo_long_min, new_expo_long); + + expo_long_max = + vgxy61_get_expo_long_max(sensor, + VGXY61_HDR_LINEAR_RATIO); + expo_short_max = (expo_long_max + + (VGXY61_HDR_LINEAR_RATIO / 2)) / + VGXY61_HDR_LINEAR_RATIO; + new_expo_short = (new_expo_long + + (VGXY61_HDR_LINEAR_RATIO / 2)) / + VGXY61_HDR_LINEAR_RATIO; + break; + case VGXY61_HDR_SUB: + new_expo_long = max(expo_long_min, new_expo_long); + + expo_long_max = vgxy61_get_expo_long_max(sensor, 1); + /* Short and long are the same in VGXY61_HDR_SUB */ + expo_short_max = expo_long_max; + new_expo_short = new_expo_long; + break; + case VGXY61_NO_HDR: + new_expo_long = max(expo_long_min, new_expo_long); + + /* + * As short expo is 0 here, only the second rule of thumb + * applies, see vgxy61_get_expo_long_max for more + */ + expo_long_max = sensor->frame_length - VGXY61_EXPOS_ROT_TERM; + break; + default: + /* Should never happen */ + WARN_ON(true); + break; + } + + /* If this happens, something is wrong with formulas */ + WARN_ON(expo_long_min > expo_long_max); + + if (new_expo_long > expo_long_max) { + dev_warn(&client->dev, "Exposure %d too high, clamping to %d\n", + new_expo_long, expo_long_max); + new_expo_long = expo_long_max; + new_expo_short = expo_short_max; + } + + sensor->expo_long = new_expo_long; + sensor->expo_short = new_expo_short; + sensor->expo_max = expo_long_max; + sensor->expo_min = expo_long_min; + + if (sensor->streaming) + return vgxy61_apply_exposure(sensor); + return 0; +} + +static int vgxy61_apply_framelength(struct vgxy61_dev *sensor) +{ + return cci_write(sensor->regmap, VGXY61_REG_FRAME_LENGTH, + sensor->frame_length, NULL); +} + +static int vgxy61_update_vblank(struct vgxy61_dev *sensor, u16 vblank, + enum vgxy61_hdr_mode hdr) +{ + int ret; + + sensor->vblank_min = vgxy61_get_vblank_min(sensor, hdr); + sensor->vblank = max(sensor->vblank_min, vblank); + sensor->frame_length = sensor->current_mode->crop.height + + sensor->vblank; + + /* Update exposure according to vblank */ + ret = vgxy61_update_exposure(sensor, sensor->expo_long, hdr); + if (ret) + return ret; + + if (sensor->streaming) + return vgxy61_apply_framelength(sensor); + return 0; +} + +static int vgxy61_apply_hdr(struct vgxy61_dev *sensor, + enum vgxy61_hdr_mode index) +{ + static const u8 index2val[] = {0x1, 0x4, 0xa}; + + return cci_write(sensor->regmap, VGXY61_REG_HDR_CTRL, index2val[index], + NULL); +} + +static int vgxy61_update_hdr(struct vgxy61_dev *sensor, + enum vgxy61_hdr_mode index) +{ + int ret; + + /* + * vblank and short exposure change according to HDR mode, do it first + * as it can violate sensors 'rule of thumbs' and therefore will require + * to change the long exposure. + */ + ret = vgxy61_update_vblank(sensor, sensor->vblank, index); + if (ret) + return ret; + + /* Update strobe mode according to HDR */ + ret = vgxy61_update_gpios_strobe_mode(sensor, index); + if (ret) + return ret; + + sensor->hdr = index; + + if (sensor->streaming) + return vgxy61_apply_hdr(sensor, sensor->hdr); + return 0; +} + +static int vgxy61_apply_settings(struct vgxy61_dev *sensor) +{ + int ret; + unsigned int i; + + ret = vgxy61_apply_hdr(sensor, sensor->hdr); + if (ret) + return ret; + + ret = vgxy61_apply_framelength(sensor); + if (ret) + return ret; + + ret = vgxy61_apply_exposure(sensor); + if (ret) + return ret; + + ret = cci_write(sensor->regmap, VGXY61_REG_ANALOG_GAIN, + sensor->analog_gain, NULL); + if (ret) + return ret; + ret = vgxy61_apply_digital_gain(sensor, sensor->digital_gain); + if (ret) + return ret; + + ret = cci_write(sensor->regmap, VGXY61_REG_ORIENTATION, + sensor->hflip | (sensor->vflip << 1), NULL); + if (ret) + return ret; + + ret = vgxy61_apply_patgen(sensor, sensor->pattern); + if (ret) + return ret; + + for (i = 0; i < VGXY61_NB_GPIOS; i++) { + ret = vgxy61_apply_gpiox_strobe_mode(sensor, + sensor->strobe_mode, i); + if (ret) + return ret; + } + + return 0; +} + +static int vgxy61_stream_enable(struct vgxy61_dev *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); + const struct v4l2_rect *crop = &sensor->current_mode->crop; + int ret = 0; + + ret = vgxy61_check_bw(sensor); + if (ret) + return ret; + + ret = pm_runtime_resume_and_get(&client->dev); + if (ret) + return ret; + + cci_write(sensor->regmap, VGXY61_REG_FORMAT_CTRL, + get_bpp_by_code(sensor->fmt.code), &ret); + cci_write(sensor->regmap, VGXY61_REG_OIF_ROI0_CTRL, + get_data_type_by_code(sensor->fmt.code), &ret); + + cci_write(sensor->regmap, VGXY61_REG_READOUT_CTRL, + sensor->current_mode->bin_mode, &ret); + cci_write(sensor->regmap, VGXY61_REG_ROI0_START_H, crop->left, &ret); + cci_write(sensor->regmap, VGXY61_REG_ROI0_END_H, + crop->left + crop->width - 1, &ret); + cci_write(sensor->regmap, VGXY61_REG_ROI0_START_V, crop->top, &ret); + cci_write(sensor->regmap, VGXY61_REG_ROI0_END_V, + crop->top + crop->height - 1, &ret); + if (ret) + goto err_rpm_put; + + ret = vgxy61_apply_settings(sensor); + if (ret) + goto err_rpm_put; + + ret = cci_write(sensor->regmap, VGXY61_REG_STREAMING, + VGXY61_STREAMING_REQ_START, NULL); + if (ret) + goto err_rpm_put; + + ret = vgxy61_poll_reg(sensor, VGXY61_REG_STREAMING, + VGXY61_STREAMING_NO_REQ, VGXY61_TIMEOUT_MS); + if (ret) + goto err_rpm_put; + + ret = vgxy61_wait_state(sensor, VGXY61_SYSTEM_FSM_STREAMING, + VGXY61_TIMEOUT_MS); + if (ret) + goto err_rpm_put; + + /* vflip and hflip cannot change during streaming */ + __v4l2_ctrl_grab(sensor->vflip_ctrl, true); + __v4l2_ctrl_grab(sensor->hflip_ctrl, true); + + return 0; + +err_rpm_put: + pm_runtime_put(&client->dev); + return ret; +} + +static int vgxy61_stream_disable(struct vgxy61_dev *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); + int ret; + + ret = cci_write(sensor->regmap, VGXY61_REG_STREAMING, + VGXY61_STREAMING_REQ_STOP, NULL); + if (ret) + goto err_str_dis; + + ret = vgxy61_poll_reg(sensor, VGXY61_REG_STREAMING, + VGXY61_STREAMING_NO_REQ, 2000); + if (ret) + goto err_str_dis; + + ret = vgxy61_wait_state(sensor, VGXY61_SYSTEM_FSM_SW_STBY, + VGXY61_TIMEOUT_MS); + if (ret) + goto err_str_dis; + + __v4l2_ctrl_grab(sensor->vflip_ctrl, false); + __v4l2_ctrl_grab(sensor->hflip_ctrl, false); + +err_str_dis: + if (ret) + WARN(1, "Can't disable stream"); + pm_runtime_put(&client->dev); + + return ret; +} + +static int vgxy61_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct vgxy61_dev *sensor = to_vgxy61_dev(sd); + int ret = 0; + + mutex_lock(&sensor->lock); + + ret = enable ? vgxy61_stream_enable(sensor) : + vgxy61_stream_disable(sensor); + if (!ret) + sensor->streaming = enable; + + mutex_unlock(&sensor->lock); + + return ret; +} + +static int vgxy61_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *format) +{ + struct vgxy61_dev *sensor = to_vgxy61_dev(sd); + const struct vgxy61_mode_info *new_mode; + struct v4l2_mbus_framefmt *fmt; + int ret; + + mutex_lock(&sensor->lock); + + if (sensor->streaming) { + ret = -EBUSY; + goto out; + } + + ret = vgxy61_try_fmt_internal(sd, &format->format, &new_mode); + if (ret) + goto out; + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + fmt = v4l2_subdev_state_get_format(sd_state, 0); + *fmt = format->format; + } else if (sensor->current_mode != new_mode || + sensor->fmt.code != format->format.code) { + fmt = &sensor->fmt; + *fmt = format->format; + + sensor->current_mode = new_mode; + + /* Reset vblank and framelength to default */ + ret = vgxy61_update_vblank(sensor, + VGXY61_FRAME_LENGTH_DEF - + new_mode->crop.height, + sensor->hdr); + + /* Update controls to reflect new mode */ + __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_ctrl, + get_pixel_rate(sensor)); + __v4l2_ctrl_modify_range(sensor->vblank_ctrl, + sensor->vblank_min, + 0xffff - new_mode->crop.height, + 1, sensor->vblank); + __v4l2_ctrl_s_ctrl(sensor->vblank_ctrl, sensor->vblank); + __v4l2_ctrl_modify_range(sensor->expo_ctrl, sensor->expo_min, + sensor->expo_max, 1, + sensor->expo_long); + } + +out: + mutex_unlock(&sensor->lock); + + return ret; +} + +static int vgxy61_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct vgxy61_dev *sensor = to_vgxy61_dev(sd); + struct v4l2_subdev_format fmt = { 0 }; + + vgxy61_fill_framefmt(sensor, sensor->current_mode, &fmt.format, + VGXY61_MEDIA_BUS_FMT_DEF); + + return vgxy61_set_fmt(sd, sd_state, &fmt); +} + +static int vgxy61_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sd(ctrl); + struct vgxy61_dev *sensor = to_vgxy61_dev(sd); + const struct vgxy61_mode_info *cur_mode = sensor->current_mode; + int ret; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = vgxy61_update_exposure(sensor, ctrl->val, sensor->hdr); + ctrl->val = sensor->expo_long; + break; + case V4L2_CID_ANALOGUE_GAIN: + ret = vgxy61_update_analog_gain(sensor, ctrl->val); + break; + case V4L2_CID_DIGITAL_GAIN: + ret = vgxy61_update_digital_gain(sensor, ctrl->val); + break; + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + if (sensor->streaming) { + ret = -EBUSY; + break; + } + if (ctrl->id == V4L2_CID_VFLIP) + sensor->vflip = ctrl->val; + if (ctrl->id == V4L2_CID_HFLIP) + sensor->hflip = ctrl->val; + ret = 0; + break; + case V4L2_CID_TEST_PATTERN: + ret = vgxy61_update_patgen(sensor, ctrl->val); + break; + case V4L2_CID_HDR_SENSOR_MODE: + ret = vgxy61_update_hdr(sensor, ctrl->val); + /* Update vblank and exposure controls to match new hdr */ + __v4l2_ctrl_modify_range(sensor->vblank_ctrl, + sensor->vblank_min, + 0xffff - cur_mode->crop.height, + 1, sensor->vblank); + __v4l2_ctrl_modify_range(sensor->expo_ctrl, sensor->expo_min, + sensor->expo_max, 1, + sensor->expo_long); + break; + case V4L2_CID_VBLANK: + ret = vgxy61_update_vblank(sensor, ctrl->val, sensor->hdr); + /* Update exposure control to match new vblank */ + __v4l2_ctrl_modify_range(sensor->expo_ctrl, sensor->expo_min, + sensor->expo_max, 1, + sensor->expo_long); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct v4l2_ctrl_ops vgxy61_ctrl_ops = { + .s_ctrl = vgxy61_s_ctrl, +}; + +static int vgxy61_init_controls(struct vgxy61_dev *sensor) +{ + const struct v4l2_ctrl_ops *ops = &vgxy61_ctrl_ops; + struct v4l2_ctrl_handler *hdl = &sensor->ctrl_handler; + const struct vgxy61_mode_info *cur_mode = sensor->current_mode; + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl *ctrl; + int ret; + + v4l2_ctrl_handler_init(hdl, 16); + /* We can use our own mutex for the ctrl lock */ + hdl->lock = &sensor->lock; + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN, 0, 0x1c, 1, + sensor->analog_gain); + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN, 0, 0xfff, 1, + sensor->digital_gain); + v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(vgxy61_test_pattern_menu) - 1, + 0, 0, vgxy61_test_pattern_menu); + ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, 0, + sensor->line_length, 1, + sensor->line_length - cur_mode->width); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + ctrl = v4l2_ctrl_new_int_menu(hdl, ops, V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq) - 1, 0, link_freq); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_HDR_SENSOR_MODE, + ARRAY_SIZE(vgxy61_hdr_mode_menu) - 1, 0, + VGXY61_NO_HDR, vgxy61_hdr_mode_menu); + + /* + * Keep a pointer to these controls as we need to update them when + * setting the format + */ + sensor->pixel_rate_ctrl = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_PIXEL_RATE, 1, + INT_MAX, 1, + get_pixel_rate(sensor)); + if (sensor->pixel_rate_ctrl) + sensor->pixel_rate_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + sensor->expo_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, + sensor->expo_min, + sensor->expo_max, 1, + sensor->expo_long); + sensor->vblank_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, + sensor->vblank_min, + 0xffff - cur_mode->crop.height, + 1, sensor->vblank); + sensor->vflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, + 0, 1, 1, sensor->vflip); + sensor->hflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, + 0, 1, 1, sensor->hflip); + + if (hdl->error) { + ret = hdl->error; + goto free_ctrls; + } + + ret = v4l2_fwnode_device_parse(&sensor->i2c_client->dev, &props); + if (ret) + goto free_ctrls; + + ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &props); + if (ret) + goto free_ctrls; + + sensor->sd.ctrl_handler = hdl; + return 0; + +free_ctrls: + v4l2_ctrl_handler_free(hdl); + return ret; +} + +static const struct v4l2_subdev_core_ops vgxy61_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops vgxy61_video_ops = { + .s_stream = vgxy61_s_stream, +}; + +static const struct v4l2_subdev_pad_ops vgxy61_pad_ops = { + .enum_mbus_code = vgxy61_enum_mbus_code, + .get_fmt = vgxy61_get_fmt, + .set_fmt = vgxy61_set_fmt, + .get_selection = vgxy61_get_selection, + .enum_frame_size = vgxy61_enum_frame_size, +}; + +static const struct v4l2_subdev_ops vgxy61_subdev_ops = { + .core = &vgxy61_core_ops, + .video = &vgxy61_video_ops, + .pad = &vgxy61_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops vgxy61_internal_ops = { + .init_state = vgxy61_init_state, +}; + +static const struct media_entity_operations vgxy61_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int vgxy61_tx_from_ep(struct vgxy61_dev *sensor, + struct fwnode_handle *handle) +{ + struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + struct i2c_client *client = sensor->i2c_client; + u32 log2phy[VGXY61_NB_POLARITIES] = {~0, ~0, ~0, ~0, ~0}; + u32 phy2log[VGXY61_NB_POLARITIES] = {~0, ~0, ~0, ~0, ~0}; + int polarities[VGXY61_NB_POLARITIES] = {0, 0, 0, 0, 0}; + int l_nb; + unsigned int p, l, i; + int ret; + + ret = v4l2_fwnode_endpoint_alloc_parse(handle, &ep); + if (ret) + return -EINVAL; + + l_nb = ep.bus.mipi_csi2.num_data_lanes; + if (l_nb != 1 && l_nb != 2 && l_nb != 4) { + dev_err(&client->dev, "invalid data lane number %d\n", l_nb); + goto error_ep; + } + + /* Build log2phy, phy2log and polarities from ep info */ + log2phy[0] = ep.bus.mipi_csi2.clock_lane; + phy2log[log2phy[0]] = 0; + for (l = 1; l < l_nb + 1; l++) { + log2phy[l] = ep.bus.mipi_csi2.data_lanes[l - 1]; + phy2log[log2phy[l]] = l; + } + /* + * Then fill remaining slots for every physical slot to have something + * valid for hardware stuff. + */ + for (p = 0; p < VGXY61_NB_POLARITIES; p++) { + if (phy2log[p] != ~0) + continue; + phy2log[p] = l; + log2phy[l] = p; + l++; + } + for (l = 0; l < l_nb + 1; l++) + polarities[l] = ep.bus.mipi_csi2.lane_polarities[l]; + + if (log2phy[0] != 0) { + dev_err(&client->dev, "clk lane must be map to physical lane 0\n"); + goto error_ep; + } + sensor->oif_ctrl = (polarities[4] << 15) + ((phy2log[4] - 1) << 13) + + (polarities[3] << 12) + ((phy2log[3] - 1) << 10) + + (polarities[2] << 9) + ((phy2log[2] - 1) << 7) + + (polarities[1] << 6) + ((phy2log[1] - 1) << 4) + + (polarities[0] << 3) + + l_nb; + sensor->nb_of_lane = l_nb; + + dev_dbg(&client->dev, "tx uses %d lanes", l_nb); + for (i = 0; i < VGXY61_NB_POLARITIES; i++) { + dev_dbg(&client->dev, "log2phy[%d] = %d\n", i, log2phy[i]); + dev_dbg(&client->dev, "phy2log[%d] = %d\n", i, phy2log[i]); + dev_dbg(&client->dev, "polarity[%d] = %d\n", i, polarities[i]); + } + dev_dbg(&client->dev, "oif_ctrl = 0x%04x\n", sensor->oif_ctrl); + + v4l2_fwnode_endpoint_free(&ep); + + return 0; + +error_ep: + v4l2_fwnode_endpoint_free(&ep); + + return -EINVAL; +} + +static int vgxy61_configure(struct vgxy61_dev *sensor) +{ + u32 sensor_freq; + u8 prediv, mult; + u64 line_length; + int ret = 0; + + compute_pll_parameters_by_freq(sensor->clk_freq, &prediv, &mult); + sensor_freq = (mult * sensor->clk_freq) / prediv; + /* Frequency to data rate is 1:1 ratio for MIPI */ + sensor->data_rate_in_mbps = sensor_freq; + /* Video timing ISP path (pixel clock) requires 804/5 mhz = 160 mhz */ + sensor->pclk = sensor_freq / 5; + + cci_read(sensor->regmap, VGXY61_REG_LINE_LENGTH, &line_length, &ret); + if (ret < 0) + return ret; + sensor->line_length = (u16)line_length; + cci_write(sensor->regmap, VGXY61_REG_EXT_CLOCK, sensor->clk_freq, &ret); + cci_write(sensor->regmap, VGXY61_REG_CLK_PLL_PREDIV, prediv, &ret); + cci_write(sensor->regmap, VGXY61_REG_CLK_SYS_PLL_MULT, mult, &ret); + cci_write(sensor->regmap, VGXY61_REG_OIF_CTRL, sensor->oif_ctrl, &ret); + cci_write(sensor->regmap, VGXY61_REG_FRAME_CONTENT_CTRL, 0, &ret); + cci_write(sensor->regmap, VGXY61_REG_BYPASS_CTRL, 4, &ret); + if (ret) + return ret; + vgxy61_update_gpios_strobe_polarity(sensor, sensor->gpios_polarity); + /* Set pattern generator solid to middle value */ + cci_write(sensor->regmap, VGXY61_REG_PATGEN_LONG_DATA_GR, 0x800, &ret); + cci_write(sensor->regmap, VGXY61_REG_PATGEN_LONG_DATA_R, 0x800, &ret); + cci_write(sensor->regmap, VGXY61_REG_PATGEN_LONG_DATA_B, 0x800, &ret); + cci_write(sensor->regmap, VGXY61_REG_PATGEN_LONG_DATA_GB, 0x800, &ret); + cci_write(sensor->regmap, VGXY61_REG_PATGEN_SHORT_DATA_GR, 0x800, &ret); + cci_write(sensor->regmap, VGXY61_REG_PATGEN_SHORT_DATA_R, 0x800, &ret); + cci_write(sensor->regmap, VGXY61_REG_PATGEN_SHORT_DATA_B, 0x800, &ret); + cci_write(sensor->regmap, VGXY61_REG_PATGEN_SHORT_DATA_GB, 0x800, &ret); + if (ret) + return ret; + + return 0; +} + +static int vgxy61_patch(struct vgxy61_dev *sensor) +{ + struct i2c_client *client = sensor->i2c_client; + u64 patch; + int ret; + + ret = vgxy61_write_array(sensor, VGXY61_REG_FWPATCH_START_ADDR, + sizeof(patch_array), patch_array); + cci_write(sensor->regmap, VGXY61_REG_STBY, 0x10, &ret); + if (ret) + return ret; + + ret = vgxy61_poll_reg(sensor, VGXY61_REG_STBY, 0, VGXY61_TIMEOUT_MS); + cci_read(sensor->regmap, VGXY61_REG_FWPATCH_REVISION, &patch, &ret); + if (ret < 0) + return ret; + + if (patch != (VGXY61_FWPATCH_REVISION_MAJOR << 12) + + (VGXY61_FWPATCH_REVISION_MINOR << 8) + + VGXY61_FWPATCH_REVISION_MICRO) { + dev_err(&client->dev, + "bad patch version expected %d.%d.%d got %u.%u.%u\n", + VGXY61_FWPATCH_REVISION_MAJOR, + VGXY61_FWPATCH_REVISION_MINOR, + VGXY61_FWPATCH_REVISION_MICRO, + (u16)patch >> 12, ((u16)patch >> 8) & 0x0f, (u16)patch & 0xff); + return -ENODEV; + } + dev_dbg(&client->dev, "patch %u.%u.%u applied\n", + (u16)patch >> 12, ((u16)patch >> 8) & 0x0f, (u16)patch & 0xff); + + return 0; +} + +static int vgxy61_detect_cut_version(struct vgxy61_dev *sensor) +{ + struct i2c_client *client = sensor->i2c_client; + u64 device_rev; + int ret; + + ret = cci_read(sensor->regmap, VGXY61_REG_REVISION, &device_rev, NULL); + if (ret < 0) + return ret; + + switch (device_rev >> 8) { + case 0xA: + dev_dbg(&client->dev, "Cut1 detected\n"); + dev_err(&client->dev, "Cut1 not supported by this driver\n"); + return -ENODEV; + case 0xB: + dev_dbg(&client->dev, "Cut2 detected\n"); + return 0; + case 0xC: + dev_dbg(&client->dev, "Cut3 detected\n"); + return 0; + default: + dev_err(&client->dev, "Unable to detect cut version\n"); + return -ENODEV; + } +} + +static int vgxy61_detect(struct vgxy61_dev *sensor) +{ + struct i2c_client *client = sensor->i2c_client; + u64 st, id = 0; + int ret; + + ret = cci_read(sensor->regmap, VGXY61_REG_MODEL_ID, &id, NULL); + if (ret < 0) + return ret; + if (id != VG5661_MODEL_ID && id != VG5761_MODEL_ID) { + dev_warn(&client->dev, "Unsupported sensor id %x\n", (u16)id); + return -ENODEV; + } + dev_dbg(&client->dev, "detected sensor id = 0x%04x\n", (u16)id); + sensor->id = id; + + ret = vgxy61_wait_state(sensor, VGXY61_SYSTEM_FSM_SW_STBY, + VGXY61_TIMEOUT_MS); + if (ret) + return ret; + + ret = cci_read(sensor->regmap, VGXY61_REG_NVM, &st, NULL); + if (ret < 0) + return st; + if (st != VGXY61_NVM_OK) + dev_warn(&client->dev, "Bad nvm state got %u\n", (u8)st); + + ret = vgxy61_detect_cut_version(sensor); + if (ret) + return ret; + + return 0; +} + +/* Power/clock management functions */ +static int vgxy61_power_on(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct vgxy61_dev *sensor = to_vgxy61_dev(sd); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(vgxy61_supply_name), + sensor->supplies); + if (ret) { + dev_err(&client->dev, "failed to enable regulators %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(sensor->xclk); + if (ret) { + dev_err(&client->dev, "failed to enable clock %d\n", ret); + goto disable_bulk; + } + + if (sensor->reset_gpio) { + ret = vgxy61_apply_reset(sensor); + if (ret) { + dev_err(&client->dev, "sensor reset failed %d\n", ret); + goto disable_clock; + } + } + + ret = vgxy61_detect(sensor); + if (ret) { + dev_err(&client->dev, "sensor detect failed %d\n", ret); + goto disable_clock; + } + + ret = vgxy61_patch(sensor); + if (ret) { + dev_err(&client->dev, "sensor patch failed %d\n", ret); + goto disable_clock; + } + + ret = vgxy61_configure(sensor); + if (ret) { + dev_err(&client->dev, "sensor configuration failed %d\n", ret); + goto disable_clock; + } + + return 0; + +disable_clock: + clk_disable_unprepare(sensor->xclk); +disable_bulk: + regulator_bulk_disable(ARRAY_SIZE(vgxy61_supply_name), + sensor->supplies); + + return ret; +} + +static int vgxy61_power_off(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct vgxy61_dev *sensor = to_vgxy61_dev(sd); + + clk_disable_unprepare(sensor->xclk); + regulator_bulk_disable(ARRAY_SIZE(vgxy61_supply_name), + sensor->supplies); + return 0; +} + +static void vgxy61_fill_sensor_param(struct vgxy61_dev *sensor) +{ + if (sensor->id == VG5761_MODEL_ID) { + sensor->sensor_width = VGX761_WIDTH; + sensor->sensor_height = VGX761_HEIGHT; + sensor->sensor_modes = vgx761_mode_data; + sensor->sensor_modes_nb = ARRAY_SIZE(vgx761_mode_data); + sensor->default_mode = &vgx761_mode_data[VGX761_DEFAULT_MODE]; + sensor->rot_term = VGX761_SHORT_ROT_TERM; + } else if (sensor->id == VG5661_MODEL_ID) { + sensor->sensor_width = VGX661_WIDTH; + sensor->sensor_height = VGX661_HEIGHT; + sensor->sensor_modes = vgx661_mode_data; + sensor->sensor_modes_nb = ARRAY_SIZE(vgx661_mode_data); + sensor->default_mode = &vgx661_mode_data[VGX661_DEFAULT_MODE]; + sensor->rot_term = VGX661_SHORT_ROT_TERM; + } else { + /* Should never happen */ + WARN_ON(true); + } + sensor->current_mode = sensor->default_mode; +} + +static int vgxy61_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct fwnode_handle *handle; + struct vgxy61_dev *sensor; + int ret; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + sensor->i2c_client = client; + sensor->streaming = false; + sensor->hdr = VGXY61_NO_HDR; + sensor->expo_long = 200; + sensor->expo_short = 0; + sensor->hflip = false; + sensor->vflip = false; + sensor->analog_gain = 0; + sensor->digital_gain = 256; + + sensor->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(sensor->regmap)) { + ret = PTR_ERR(sensor->regmap); + return dev_err_probe(dev, ret, "Failed to init regmap\n"); + } + + handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0); + if (!handle) { + dev_err(dev, "handle node not found\n"); + return -EINVAL; + } + + ret = vgxy61_tx_from_ep(sensor, handle); + fwnode_handle_put(handle); + if (ret) { + dev_err(dev, "Failed to parse handle %d\n", ret); + return ret; + } + + sensor->xclk = devm_clk_get(dev, NULL); + if (IS_ERR(sensor->xclk)) { + dev_err(dev, "failed to get xclk\n"); + return PTR_ERR(sensor->xclk); + } + sensor->clk_freq = clk_get_rate(sensor->xclk); + if (sensor->clk_freq < 6 * HZ_PER_MHZ || + sensor->clk_freq > 27 * HZ_PER_MHZ) { + dev_err(dev, "Only 6Mhz-27Mhz clock range supported. provide %lu MHz\n", + sensor->clk_freq / HZ_PER_MHZ); + return -EINVAL; + } + sensor->gpios_polarity = + device_property_read_bool(dev, "st,strobe-gpios-polarity"); + + v4l2_i2c_subdev_init(&sensor->sd, client, &vgxy61_subdev_ops); + sensor->sd.internal_ops = &vgxy61_internal_ops; + sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + sensor->sd.entity.ops = &vgxy61_subdev_entity_ops; + sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + + ret = vgxy61_get_regulators(sensor); + if (ret) { + dev_err(&client->dev, "failed to get regulators %d\n", ret); + return ret; + } + + ret = vgxy61_power_on(dev); + if (ret) + return ret; + + vgxy61_fill_sensor_param(sensor); + vgxy61_fill_framefmt(sensor, sensor->current_mode, &sensor->fmt, + VGXY61_MEDIA_BUS_FMT_DEF); + + mutex_init(&sensor->lock); + + ret = vgxy61_update_hdr(sensor, sensor->hdr); + if (ret) + goto error_power_off; + + ret = vgxy61_init_controls(sensor); + if (ret) { + dev_err(&client->dev, "controls initialization failed %d\n", + ret); + goto error_power_off; + } + + ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); + if (ret) { + dev_err(&client->dev, "pads init failed %d\n", ret); + goto error_handler_free; + } + + /* Enable runtime PM and turn off the device */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + ret = v4l2_async_register_subdev(&sensor->sd); + if (ret) { + dev_err(&client->dev, "async subdev register failed %d\n", ret); + goto error_pm_runtime; + } + + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_use_autosuspend(&client->dev); + + dev_dbg(&client->dev, "vgxy61 probe successfully\n"); + + return 0; + +error_pm_runtime: + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + media_entity_cleanup(&sensor->sd.entity); +error_handler_free: + v4l2_ctrl_handler_free(sensor->sd.ctrl_handler); +error_power_off: + mutex_destroy(&sensor->lock); + vgxy61_power_off(dev); + + return ret; +} + +static void vgxy61_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct vgxy61_dev *sensor = to_vgxy61_dev(sd); + + v4l2_async_unregister_subdev(&sensor->sd); + mutex_destroy(&sensor->lock); + media_entity_cleanup(&sensor->sd.entity); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + vgxy61_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); +} + +static const struct of_device_id vgxy61_dt_ids[] = { + { .compatible = "st,st-vgxy61" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, vgxy61_dt_ids); + +static const struct dev_pm_ops vgxy61_pm_ops = { + SET_RUNTIME_PM_OPS(vgxy61_power_off, vgxy61_power_on, NULL) +}; + +static struct i2c_driver vgxy61_i2c_driver = { + .driver = { + .name = "vgxy61", + .of_match_table = vgxy61_dt_ids, + .pm = &vgxy61_pm_ops, + }, + .probe = vgxy61_probe, + .remove = vgxy61_remove, +}; + +module_i2c_driver(vgxy61_i2c_driver); + +MODULE_AUTHOR("Benjamin Mugnier "); +MODULE_AUTHOR("Mickael Guene "); +MODULE_AUTHOR("Sylvain Petinot "); +MODULE_DESCRIPTION("VGXY61 camera subdev driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 971b4eef86ccb8b107ad2875993e510eec4fdeae Mon Sep 17 00:00:00 2001 From: Abdulrasaq Lawani Date: Wed, 12 Jun 2024 06:51:58 -0400 Subject: media: i2c: ov5647: replacing of_node_put with __free(device_node) Replace instance of of_node_put with __free(device_node) to protect against any memory leaks due to future changes in control flow. Signed-off-by: Abdulrasaq Lawani Acked-by: Dave Stevenson Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/ov5647.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 7e1ecdf2485f..0fb4d7bff9d1 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -1360,24 +1360,21 @@ static int ov5647_parse_dt(struct ov5647 *sensor, struct device_node *np) struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_CSI2_DPHY, }; - struct device_node *ep; + struct device_node *ep __free(device_node) = + of_graph_get_endpoint_by_regs(np, 0, -1); int ret; - ep = of_graph_get_endpoint_by_regs(np, 0, -1); if (!ep) return -EINVAL; ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg); if (ret) - goto out; + return ret; sensor->clock_ncont = bus_cfg.bus.mipi_csi2.flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; -out: - of_node_put(ep); - - return ret; + return 0; } static int ov5647_probe(struct i2c_client *client) -- cgit v1.2.3 From 91798162245991e26949ef62851719bb2177a9c2 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Mon, 10 Jun 2024 11:33:38 -0700 Subject: media: v4l: add missing MODULE_DESCRIPTION() macros make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/v4l2-core/v4l2-async.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/v4l2-core/v4l2-fwnode.o Add the missing invocations of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-async.c | 1 + drivers/media/v4l2-core/v4l2-fwnode.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 81a9b5473969..ee884a8221fb 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -972,4 +972,5 @@ module_exit(v4l2_async_exit); MODULE_AUTHOR("Guennadi Liakhovetski "); MODULE_AUTHOR("Sakari Ailus "); MODULE_AUTHOR("Ezequiel Garcia "); +MODULE_DESCRIPTION("V4L2 asynchronous subdevice registration API"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 89c7192148df..f19c8adf2c61 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -1251,6 +1251,7 @@ out_cleanup: } EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor); +MODULE_DESCRIPTION("V4L2 fwnode binding parsing library"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Sakari Ailus "); MODULE_AUTHOR("Sylwester Nawrocki "); -- cgit v1.2.3 From a0df8d9450dff25652b628a07f9240ea66228eba Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Sat, 23 Mar 2024 10:48:02 +0000 Subject: media: uvcvideo: Support timestamp lists of any size The tail of the list lives at the position before the head. This is mathematically noted as: ``` (head-1) mod size. ``` Unfortunately C, does not have a modulus operator, but a remainder operator (%). The reminder operation has a different result than the modulus if (head -1) is a negative number and size is not a power of two. Adding size to (head-1) allows the code to run with any value of size. This does not change the current behaviour of the driver, as the size is always a power of two, but avoid tedious debugging if we ever change its size. Reviewed-by: Sergey Senozhatsky Signed-off-by: Ricardo Ribalda Reviewed-by: Tomasz Figa Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20240323-resend-hwtimestamp-v10-1-b08e590d97c7@chromium.org Signed-off-by: Laurent Pinchart --- drivers/media/usb/uvc/uvc_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 7cbf4692bd87..659c9e9880a9 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -732,7 +732,7 @@ void uvc_video_clock_update(struct uvc_streaming *stream, goto done; first = &clock->samples[clock->head]; - last = &clock->samples[(clock->head - 1) % clock->size]; + last = &clock->samples[(clock->head - 1 + clock->size) % clock->size]; /* First step, PTS to SOF conversion. */ delta_stc = buf->pts - (1UL << 31); -- cgit v1.2.3 From 5cd7c25f6f0576073b3d03bc4cfb1e8ca63a1195 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Sat, 23 Mar 2024 10:48:03 +0000 Subject: media: uvcvideo: Ignore empty TS packets Some SunplusIT cameras took a borderline interpretation of the UVC 1.5 standard, and fill the PTS and SCR fields with invalid data if the package does not contain data. "STC must be captured when the first video data of a video frame is put on the USB bus." Some SunplusIT devices send, e.g., buffer: 0xa7755c00 len 000012 header:0x8c stc 00000000 sof 0000 pts 00000000 buffer: 0xa7755c00 len 000012 header:0x8c stc 00000000 sof 0000 pts 00000000 buffer: 0xa7755c00 len 000668 header:0x8c stc 73779dba sof 070c pts 7376d37a While the UVC specification meant that the first two packets shouldn't have had the SCR bit set in the header. This borderline/buggy interpretation has been implemented in a variety of devices, from directly SunplusIT and from other OEMs that rebrand SunplusIT products. So quirking based on VID:PID will be problematic. All the affected modules have the following extension unit: VideoControl Interface Descriptor: guidExtensionCode {82066163-7050-ab49-b8cc-b3855e8d221d} But the vendor plans to use that GUID in the future and fix the bug, this means that we should use heuristic to figure out the broken packets. This patch takes care of this. lsusb of one of the affected cameras: Bus 001 Device 003: ID 1bcf:2a01 Sunplus Innovation Technology Inc. Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.01 bDeviceClass 239 Miscellaneous Device bDeviceSubClass 2 ? bDeviceProtocol 1 Interface Association bMaxPacketSize0 64 idVendor 0x1bcf Sunplus Innovation Technology Inc. idProduct 0x2a01 bcdDevice 0.02 iManufacturer 1 SunplusIT Inc iProduct 2 HanChen Wise Camera iSerial 3 01.00.00 bNumConfigurations 1 Tested-by: HungNien Chen Reviewed-by: Sergey Senozhatsky Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Reviewed-by: Tomasz Figa Link: https://lore.kernel.org/r/20240323-resend-hwtimestamp-v10-2-b08e590d97c7@chromium.org Signed-off-by: Laurent Pinchart --- drivers/media/usb/uvc/uvc_video.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 659c9e9880a9..b2e70fcf4eb4 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -478,6 +478,7 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, ktime_t time; u16 host_sof; u16 dev_sof; + u32 dev_stc; switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) { case UVC_STREAM_PTS | UVC_STREAM_SCR: @@ -526,6 +527,34 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, if (dev_sof == stream->clock.last_sof) return; + dev_stc = get_unaligned_le32(&data[header_size - 6]); + + /* + * STC (Source Time Clock) is the clock used by the camera. The UVC 1.5 + * standard states that it "must be captured when the first video data + * of a video frame is put on the USB bus". This is generally understood + * as requiring devices to clear the payload header's SCR bit before + * the first packet containing video data. + * + * Most vendors follow that interpretation, but some (namely SunplusIT + * on some devices) always set the `UVC_STREAM_SCR` bit, fill the SCR + * field with 0's,and expect that the driver only processes the SCR if + * there is data in the packet. + * + * Ignore all the hardware timestamp information if we haven't received + * any data for this frame yet, the packet contains no data, and both + * STC and SOF are zero. This heuristics should be safe on compliant + * devices. This should be safe with compliant devices, as in the very + * unlikely case where a UVC 1.1 device would send timing information + * only before the first packet containing data, and both STC and SOF + * happen to be zero for a particular frame, we would only miss one + * clock sample from many and the clock recovery algorithm wouldn't + * suffer from this condition. + */ + if (buf && buf->bytesused == 0 && len == header_size && + dev_stc == 0 && dev_sof == 0) + return; + stream->clock.last_sof = dev_sof; host_sof = usb_get_current_frame_number(stream->dev->udev); @@ -564,7 +593,7 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, spin_lock_irqsave(&stream->clock.lock, flags); sample = &stream->clock.samples[stream->clock.head]; - sample->dev_stc = get_unaligned_le32(&data[header_size - 6]); + sample->dev_stc = dev_stc; sample->dev_sof = dev_sof; sample->host_sof = host_sof; sample->host_time = time; -- cgit v1.2.3 From 9183c6f1a21e0da4415762c504e2d7f784304d12 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Sat, 23 Mar 2024 10:48:04 +0000 Subject: media: uvcvideo: Quirk for invalid dev_sof in Logitech C922 Logitech C922 internal SOF does not increases at a stable rate of 1kHz. This causes that the device_sof and the host_sof run at different rates, breaking the clock domain conversion algorithm. Eg: 30 (6) [-] none 30 614400 B 21.245557 21.395214 34.133 fps ts mono/SoE 31 (7) [-] none 31 614400 B 21.275327 21.427246 33.591 fps ts mono/SoE 32 (0) [-] none 32 614400 B 21.304739 21.459256 34.000 fps ts mono/SoE 33 (1) [-] none 33 614400 B 21.334324 21.495274 33.801 fps ts mono/SoE * 34 (2) [-] none 34 614400 B 21.529237 21.527297 5.130 fps ts mono/SoE * 35 (3) [-] none 35 614400 B 21.649416 21.559306 8.321 fps ts mono/SoE 36 (4) [-] none 36 614400 B 21.678789 21.595320 34.045 fps ts mono/SoE ... 99 (3) [-] none 99 614400 B 23.542226 23.696352 33.541 fps ts mono/SoE 100 (4) [-] none 100 614400 B 23.571578 23.728404 34.069 fps ts mono/SoE 101 (5) [-] none 101 614400 B 23.601425 23.760420 33.504 fps ts mono/SoE * 102 (6) [-] none 102 614400 B 23.798324 23.796428 5.079 fps ts mono/SoE * 103 (7) [-] none 103 614400 B 23.916271 23.828450 8.478 fps ts mono/SoE 104 (0) [-] none 104 614400 B 23.945720 23.860479 33.957 fps ts mono/SoE Instead of disabling completely the hardware timestamping for such hardware we take the assumption that the packet handling jitter is under 2ms and use the host_sof as dev_sof. We can think of the UVC hardware clock as a system with a coarse clock (the SOF) and a fine clock (the PTS). The coarse clock can be replaced with a clock on the same frequency, if the jitter of such clock is smaller than its sampling rate. That way we can save some of the precision of the fine clock. To probe this point we have run three experiments on the Logitech C922. On that experiment we run the camera at 33fps and we analyse the difference in msec between a frame and its predecessor. If we display the histogram of that value, a thinner histogram will mean a better meassurement. The results for: - original hw timestamp: https://ibb.co/D1HJJ4x - pure software timestamp: https://ibb.co/QC9MgVK - modified hw timestamp: https://ibb.co/8s9dBdk This bug in the camera firmware has been confirmed by the vendor. lsusb -v Bus 001 Device 044: ID 046d:085c Logitech, Inc. C922 Pro Stream Webcam Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 239 Miscellaneous Device bDeviceSubClass 2 bDeviceProtocol 1 Interface Association bMaxPacketSize0 64 idVendor 0x046d Logitech, Inc. idProduct 0x085c C922 Pro Stream Webcam bcdDevice 0.16 iManufacturer 0 iProduct 2 C922 Pro Stream Webcam iSerial 1 80B912DF bNumConfigurations 1 Reviewed-by: Sergey Senozhatsky Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Reviewed-by: Ricardo Ribalda Signed-off-by: Oleksandr Natalenko Reviewed-by: Tomasz Figa Link: https://lore.kernel.org/r/20240323-resend-hwtimestamp-v10-3-b08e590d97c7@chromium.org Signed-off-by: Laurent Pinchart --- drivers/media/usb/uvc/uvc_driver.c | 9 +++++++++ drivers/media/usb/uvc/uvc_video.c | 11 +++++++++++ drivers/media/usb/uvc/uvcvideo.h | 1 + 3 files changed, 21 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 8fe24c98087e..6e55f91ac17f 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2581,6 +2581,15 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTORE_CTRLS_ON_INIT) }, + /* Logitech HD Pro Webcam C922 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x046d, + .idProduct = 0x085c, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_INVALID_DEVICE_SOF) }, /* Logitech Rally Bar Huddle */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index b2e70fcf4eb4..d6ca383f643e 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -558,6 +558,17 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, stream->clock.last_sof = dev_sof; host_sof = usb_get_current_frame_number(stream->dev->udev); + + /* + * On some devices, like the Logitech C922, the device SOF does not run + * at a stable rate of 1kHz. For those devices use the host SOF instead. + * In the tests performed so far, this improves the timestamp precision. + * This is probably explained by a small packet handling jitter from the + * host, but the exact reason hasn't been fully determined. + */ + if (stream->dev->quirks & UVC_QUIRK_INVALID_DEVICE_SOF) + dev_sof = host_sof; + time = uvc_video_get_time(); /* diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 3653b2c8a86c..e5b12717016f 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -75,6 +75,7 @@ #define UVC_QUIRK_WAKE_AUTOSUSPEND 0x00002000 #define UVC_QUIRK_NO_RESET_RESUME 0x00004000 #define UVC_QUIRK_DISABLE_AUTOSUSPEND 0x00008000 +#define UVC_QUIRK_INVALID_DEVICE_SOF 0x00010000 /* Format flags */ #define UVC_FMT_FLAG_COMPRESSED 0x00000001 -- cgit v1.2.3 From 6243c83be6ee8d95cf5661b5a123621106491974 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Sat, 23 Mar 2024 10:48:05 +0000 Subject: media: uvcvideo: Allow hw clock updates with buffers not full With UVC 1.5 we get as little as one clock sample per frame. Which means that it takes 32 frames to move from the software timestamp to the hardware timestamp method. This results in abrupt changes in the timestamping after 32 frames (~1 second), resulting in noticeable artifacts when used for encoding. With this patch we modify the update algorithm to work with whatever amount of values are available. Tested-by: HungNien Chen Reviewed-by: Sergey Senozhatsky Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Reviewed-by: Tomasz Figa Link: https://lore.kernel.org/r/20240323-resend-hwtimestamp-v10-4-b08e590d97c7@chromium.org Signed-off-by: Laurent Pinchart --- drivers/media/usb/uvc/uvc_video.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index d6ca383f643e..af25b9f1b53f 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -768,10 +768,10 @@ void uvc_video_clock_update(struct uvc_streaming *stream, spin_lock_irqsave(&clock->lock, flags); - if (clock->count < clock->size) + if (clock->count < 2) goto done; - first = &clock->samples[clock->head]; + first = &clock->samples[(clock->head - clock->count + clock->size) % clock->size]; last = &clock->samples[(clock->head - 1 + clock->size) % clock->size]; /* First step, PTS to SOF conversion. */ @@ -786,6 +786,18 @@ void uvc_video_clock_update(struct uvc_streaming *stream, if (y2 < y1) y2 += 2048 << 16; + /* + * Have at least 1/4 of a second of timestamps before we + * try to do any calculation. Otherwise we do not have enough + * precision. This value was determined by running Android CTS + * on different devices. + * + * dev_sof runs at 1KHz, and we have a fixed point precision of + * 16 bits. + */ + if ((y2 - y1) < ((1000 / 4) << 16)) + goto done; + y = (u64)(y2 - y1) * (1ULL << 31) + (u64)y1 * (u64)x2 - (u64)y2 * (u64)x1; y = div_u64(y, x2 - x1); -- cgit v1.2.3 From 141270bd95d48ea77adf2ea851e0621dc6725782 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Sat, 23 Mar 2024 10:48:06 +0000 Subject: media: uvcvideo: Refactor clock circular buffer Isolate all the changes related to the clock circular buffer to its own function, that way we can make changes easier to the buffer logic. Also simplify the lock, by removing the circular buffer clock handling from uvc_video_clock_decode(). And now that we are at it, unify the API of the clock functions. Tested-by: HungNien Chen Reviewed-by: Sergey Senozhatsky Reviewed-by: Laurent Pinchart Signed-off-by: Ricardo Ribalda Reviewed-by: Tomasz Figa Link: https://lore.kernel.org/r/20240323-resend-hwtimestamp-v10-5-b08e590d97c7@chromium.org Signed-off-by: Laurent Pinchart --- drivers/media/usb/uvc/uvc_video.c | 83 +++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 47 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index af25b9f1b53f..5df8f61d39cd 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -466,19 +466,28 @@ static inline ktime_t uvc_video_get_time(void) return ktime_get_real(); } +static void uvc_video_clock_add_sample(struct uvc_clock *clock, + const struct uvc_clock_sample *sample) +{ + unsigned long flags; + + spin_lock_irqsave(&clock->lock, flags); + + clock->samples[clock->head] = *sample; + clock->head = (clock->head + 1) % clock->size; + clock->count = min(clock->count + 1, clock->size); + + spin_unlock_irqrestore(&clock->lock, flags); +} + static void uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, const u8 *data, int len) { - struct uvc_clock_sample *sample; + struct uvc_clock_sample sample; unsigned int header_size; bool has_pts = false; bool has_scr = false; - unsigned long flags; - ktime_t time; - u16 host_sof; - u16 dev_sof; - u32 dev_stc; switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) { case UVC_STREAM_PTS | UVC_STREAM_SCR: @@ -523,11 +532,11 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, * all the data packets of the same frame contains the same SOF. In that * case only the first one will match the host_sof. */ - dev_sof = get_unaligned_le16(&data[header_size - 2]); - if (dev_sof == stream->clock.last_sof) + sample.dev_sof = get_unaligned_le16(&data[header_size - 2]); + if (sample.dev_sof == stream->clock.last_sof) return; - dev_stc = get_unaligned_le32(&data[header_size - 6]); + sample.dev_stc = get_unaligned_le32(&data[header_size - 6]); /* * STC (Source Time Clock) is the clock used by the camera. The UVC 1.5 @@ -552,12 +561,10 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, * suffer from this condition. */ if (buf && buf->bytesused == 0 && len == header_size && - dev_stc == 0 && dev_sof == 0) + sample.dev_stc == 0 && sample.dev_sof == 0) return; - stream->clock.last_sof = dev_sof; - - host_sof = usb_get_current_frame_number(stream->dev->udev); + sample.host_sof = usb_get_current_frame_number(stream->dev->udev); /* * On some devices, like the Logitech C922, the device SOF does not run @@ -567,9 +574,9 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, * host, but the exact reason hasn't been fully determined. */ if (stream->dev->quirks & UVC_QUIRK_INVALID_DEVICE_SOF) - dev_sof = host_sof; + sample.dev_sof = sample.host_sof; - time = uvc_video_get_time(); + sample.host_time = uvc_video_get_time(); /* * The UVC specification allows device implementations that can't obtain @@ -592,46 +599,28 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf, * the 8 LSBs of the delta are kept. */ if (stream->clock.sof_offset == (u16)-1) { - u16 delta_sof = (host_sof - dev_sof) & 255; + u16 delta_sof = (sample.host_sof - sample.dev_sof) & 255; if (delta_sof >= 10) stream->clock.sof_offset = delta_sof; else stream->clock.sof_offset = 0; } - dev_sof = (dev_sof + stream->clock.sof_offset) & 2047; - - spin_lock_irqsave(&stream->clock.lock, flags); - - sample = &stream->clock.samples[stream->clock.head]; - sample->dev_stc = dev_stc; - sample->dev_sof = dev_sof; - sample->host_sof = host_sof; - sample->host_time = time; - - /* Update the sliding window head and count. */ - stream->clock.head = (stream->clock.head + 1) % stream->clock.size; - - if (stream->clock.count < stream->clock.size) - stream->clock.count++; - - spin_unlock_irqrestore(&stream->clock.lock, flags); + sample.dev_sof = (sample.dev_sof + stream->clock.sof_offset) & 2047; + uvc_video_clock_add_sample(&stream->clock, &sample); + stream->clock.last_sof = sample.dev_sof; } -static void uvc_video_clock_reset(struct uvc_streaming *stream) +static void uvc_video_clock_reset(struct uvc_clock *clock) { - struct uvc_clock *clock = &stream->clock; - clock->head = 0; clock->count = 0; clock->last_sof = -1; clock->sof_offset = -1; } -static int uvc_video_clock_init(struct uvc_streaming *stream) +static int uvc_video_clock_init(struct uvc_clock *clock) { - struct uvc_clock *clock = &stream->clock; - spin_lock_init(&clock->lock); clock->size = 32; @@ -640,15 +629,15 @@ static int uvc_video_clock_init(struct uvc_streaming *stream) if (clock->samples == NULL) return -ENOMEM; - uvc_video_clock_reset(stream); + uvc_video_clock_reset(clock); return 0; } -static void uvc_video_clock_cleanup(struct uvc_streaming *stream) +static void uvc_video_clock_cleanup(struct uvc_clock *clock) { - kfree(stream->clock.samples); - stream->clock.samples = NULL; + kfree(clock->samples); + clock->samples = NULL; } /* @@ -2123,7 +2112,7 @@ int uvc_video_resume(struct uvc_streaming *stream, int reset) stream->frozen = 0; - uvc_video_clock_reset(stream); + uvc_video_clock_reset(&stream->clock); if (!uvc_queue_streaming(&stream->queue)) return 0; @@ -2272,7 +2261,7 @@ int uvc_video_start_streaming(struct uvc_streaming *stream) { int ret; - ret = uvc_video_clock_init(stream); + ret = uvc_video_clock_init(&stream->clock); if (ret < 0) return ret; @@ -2290,7 +2279,7 @@ int uvc_video_start_streaming(struct uvc_streaming *stream) error_video: usb_set_interface(stream->dev->udev, stream->intfnum, 0); error_commit: - uvc_video_clock_cleanup(stream); + uvc_video_clock_cleanup(&stream->clock); return ret; } @@ -2318,5 +2307,5 @@ void uvc_video_stop_streaming(struct uvc_streaming *stream) usb_clear_halt(stream->dev->udev, pipe); } - uvc_video_clock_cleanup(stream); + uvc_video_clock_cleanup(&stream->clock); } -- cgit v1.2.3 From 53d7995383fae089319e24636250f248b0fdb0ce Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 10 Jun 2024 19:17:48 +0000 Subject: media: uvcvideo: Fix hw timestamp handling for slow FPS In UVC 1.5 we get a single clock value per frame. With the current buffer size of 32, FPS slowers than 32 might roll-over twice. The current code cannot handle two roll-over and provide invalid timestamps. Revome all the samples from the circular buffer that are more than two rollovers old, so the algorithm always provides good timestamps. Note that we are removing values that are more than one second old, which means that there is enough distance between the two points that we use for the interpolation to provide good values. Tested-by: HungNien Chen Reviewed-by: Sergey Senozhatsky Reviewed-by: Tomasz Figa Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20240610-hwtimestamp-followup-v1-1-f9eaed7be7f0@chromium.org Signed-off-by: Laurent Pinchart --- drivers/media/usb/uvc/uvc_video.c | 22 ++++++++++++++++++++++ drivers/media/usb/uvc/uvcvideo.h | 1 + 2 files changed, 23 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 5df8f61d39cd..aebcf9b25a16 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -471,8 +471,29 @@ static void uvc_video_clock_add_sample(struct uvc_clock *clock, { unsigned long flags; + /* + * If we write new data on the position where we had the last + * overflow, remove the overflow pointer. There is no SOF overflow + * in the whole circular buffer. + */ + if (clock->head == clock->last_sof_overflow) + clock->last_sof_overflow = -1; + spin_lock_irqsave(&clock->lock, flags); + if (clock->count > 0 && clock->last_sof > sample->dev_sof) { + /* + * Remove data from the circular buffer that is older than the + * last SOF overflow. We only support one SOF overflow per + * circular buffer. + */ + if (clock->last_sof_overflow != -1) + clock->count = (clock->head - clock->last_sof_overflow + + clock->size) % clock->size; + clock->last_sof_overflow = clock->head; + } + + /* Add sample. */ clock->samples[clock->head] = *sample; clock->head = (clock->head + 1) % clock->size; clock->count = min(clock->count + 1, clock->size); @@ -616,6 +637,7 @@ static void uvc_video_clock_reset(struct uvc_clock *clock) clock->head = 0; clock->count = 0; clock->last_sof = -1; + clock->last_sof_overflow = -1; clock->sof_offset = -1; } diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index e5b12717016f..f21207debd54 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -501,6 +501,7 @@ struct uvc_streaming { unsigned int head; unsigned int count; unsigned int size; + unsigned int last_sof_overflow; u16 last_sof; u16 sof_offset; -- cgit v1.2.3 From 8676a5e796fa18f55897ca36a94b2adf7f73ebd1 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 10 Jun 2024 19:17:49 +0000 Subject: media: uvcvideo: Fix integer overflow calculating timestamp The function uvc_video_clock_update() supports a single SOF overflow. Or in other words, the maximum difference between the first ant the last timestamp can be 4096 ticks or 4.096 seconds. This results in a maximum value for y2 of: 0x12FBECA00, that overflows 32bits. y2 = (u32)ktime_to_ns(ktime_sub(last->host_time, first->host_time)) + y1; Extend the size of y2 to u64 to support all its values. Without this patch: # yavta -s 1920x1080 -f YUYV -t 1/5 -c /dev/video0 Device /dev/v4l/by-id/usb-Shine-Optics_Integrated_Camera_0001-video-index0 opened. Device `Integrated Camera: Integrated C' on `usb-0000:00:14.0-6' (driver 'uvcvideo') supports video, capture, without mplanes. Video format set: YUYV (56595559) 1920x1080 (stride 3840) field none buffer size 4147200 Video format: YUYV (56595559) 1920x1080 (stride 3840) field none buffer size 4147200 Current frame rate: 1/5 Setting frame rate to: 1/5 Frame rate set: 1/5 8 buffers requested. length: 4147200 offset: 0 timestamp type/source: mono/SoE Buffer 0/0 mapped at address 0x7947ea94c000. length: 4147200 offset: 4149248 timestamp type/source: mono/SoE Buffer 1/0 mapped at address 0x7947ea557000. length: 4147200 offset: 8298496 timestamp type/source: mono/SoE Buffer 2/0 mapped at address 0x7947ea162000. length: 4147200 offset: 12447744 timestamp type/source: mono/SoE Buffer 3/0 mapped at address 0x7947e9d6d000. length: 4147200 offset: 16596992 timestamp type/source: mono/SoE Buffer 4/0 mapped at address 0x7947e9978000. length: 4147200 offset: 20746240 timestamp type/source: mono/SoE Buffer 5/0 mapped at address 0x7947e9583000. length: 4147200 offset: 24895488 timestamp type/source: mono/SoE Buffer 6/0 mapped at address 0x7947e918e000. length: 4147200 offset: 29044736 timestamp type/source: mono/SoE Buffer 7/0 mapped at address 0x7947e8d99000. 0 (0) [-] none 0 4147200 B 507.554210 508.874282 242.836 fps ts mono/SoE 1 (1) [-] none 2 4147200 B 508.886298 509.074289 0.751 fps ts mono/SoE 2 (2) [-] none 3 4147200 B 509.076362 509.274307 5.261 fps ts mono/SoE 3 (3) [-] none 4 4147200 B 509.276371 509.474336 5.000 fps ts mono/SoE 4 (4) [-] none 5 4147200 B 509.476394 509.674394 4.999 fps ts mono/SoE 5 (5) [-] none 6 4147200 B 509.676506 509.874345 4.997 fps ts mono/SoE 6 (6) [-] none 7 4147200 B 509.876430 510.074370 5.002 fps ts mono/SoE 7 (7) [-] none 8 4147200 B 510.076434 510.274365 5.000 fps ts mono/SoE 8 (0) [-] none 9 4147200 B 510.276421 510.474333 5.000 fps ts mono/SoE 9 (1) [-] none 10 4147200 B 510.476391 510.674429 5.001 fps ts mono/SoE 10 (2) [-] none 11 4147200 B 510.676434 510.874283 4.999 fps ts mono/SoE 11 (3) [-] none 12 4147200 B 510.886264 511.074349 4.766 fps ts mono/SoE 12 (4) [-] none 13 4147200 B 511.070577 511.274304 5.426 fps ts mono/SoE 13 (5) [-] none 14 4147200 B 511.286249 511.474301 4.637 fps ts mono/SoE 14 (6) [-] none 15 4147200 B 511.470542 511.674251 5.426 fps ts mono/SoE 15 (7) [-] none 16 4147200 B 511.672651 511.874337 4.948 fps ts mono/SoE 16 (0) [-] none 17 4147200 B 511.873988 512.074462 4.967 fps ts mono/SoE 17 (1) [-] none 18 4147200 B 512.075982 512.278296 4.951 fps ts mono/SoE 18 (2) [-] none 19 4147200 B 512.282631 512.482423 4.839 fps ts mono/SoE 19 (3) [-] none 20 4147200 B 518.986637 512.686333 0.149 fps ts mono/SoE 20 (4) [-] none 21 4147200 B 518.342709 512.886386 -1.553 fps ts mono/SoE 21 (5) [-] none 22 4147200 B 517.909812 513.090360 -2.310 fps ts mono/SoE 22 (6) [-] none 23 4147200 B 517.590775 513.294454 -3.134 fps ts mono/SoE 23 (7) [-] none 24 4147200 B 513.298465 513.494335 -0.233 fps ts mono/SoE 24 (0) [-] none 25 4147200 B 513.510273 513.698375 4.721 fps ts mono/SoE 25 (1) [-] none 26 4147200 B 513.698904 513.902327 5.301 fps ts mono/SoE 26 (2) [-] none 27 4147200 B 513.895971 514.102348 5.074 fps ts mono/SoE 27 (3) [-] none 28 4147200 B 514.099091 514.306337 4.923 fps ts mono/SoE 28 (4) [-] none 29 4147200 B 514.310348 514.510567 4.734 fps ts mono/SoE 29 (5) [-] none 30 4147200 B 514.509295 514.710367 5.026 fps ts mono/SoE 30 (6) [-] none 31 4147200 B 521.532513 514.914398 0.142 fps ts mono/SoE 31 (7) [-] none 32 4147200 B 520.885277 515.118385 -1.545 fps ts mono/SoE 32 (0) [-] none 33 4147200 B 520.411140 515.318336 -2.109 fps ts mono/SoE 33 (1) [-] none 34 4147200 B 515.325425 515.522278 -0.197 fps ts mono/SoE 34 (2) [-] none 35 4147200 B 515.538276 515.726423 4.698 fps ts mono/SoE 35 (3) [-] none 36 4147200 B 515.720767 515.930373 5.480 fps ts mono/SoE Cc: stable@vger.kernel.org Fixes: 66847ef013cc ("[media] uvcvideo: Add UVC timestamps support") Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20240610-hwtimestamp-followup-v1-2-f9eaed7be7f0@chromium.org Signed-off-by: Laurent Pinchart --- drivers/media/usb/uvc/uvc_video.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index aebcf9b25a16..4dfc1b86bdee 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -760,11 +760,11 @@ void uvc_video_clock_update(struct uvc_streaming *stream, unsigned long flags; u64 timestamp; u32 delta_stc; - u32 y1, y2; + u32 y1; u32 x1, x2; u32 mean; u32 sof; - u64 y; + u64 y, y2; if (!uvc_hw_timestamps_param) return; @@ -816,7 +816,7 @@ void uvc_video_clock_update(struct uvc_streaming *stream, sof = y; uvc_dbg(stream->dev, CLOCK, - "%s: PTS %u y %llu.%06llu SOF %u.%06llu (x1 %u x2 %u y1 %u y2 %u SOF offset %u)\n", + "%s: PTS %u y %llu.%06llu SOF %u.%06llu (x1 %u x2 %u y1 %u y2 %llu SOF offset %u)\n", stream->dev->name, buf->pts, y >> 16, div_u64((y & 0xffff) * 1000000, 65536), sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536), @@ -831,7 +831,7 @@ void uvc_video_clock_update(struct uvc_streaming *stream, goto done; y1 = NSEC_PER_SEC; - y2 = (u32)ktime_to_ns(ktime_sub(last->host_time, first->host_time)) + y1; + y2 = ktime_to_ns(ktime_sub(last->host_time, first->host_time)) + y1; /* * Interpolated and host SOF timestamps can wrap around at slightly @@ -852,7 +852,7 @@ void uvc_video_clock_update(struct uvc_streaming *stream, timestamp = ktime_to_ns(first->host_time) + y - y1; uvc_dbg(stream->dev, CLOCK, - "%s: SOF %u.%06llu y %llu ts %llu buf ts %llu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %u)\n", + "%s: SOF %u.%06llu y %llu ts %llu buf ts %llu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %llu)\n", stream->dev->name, sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536), y, timestamp, vbuf->vb2_buf.timestamp, -- cgit v1.2.3 From 85fbe91a7c9210bb30638846b551fa5d3cb7bc4c Mon Sep 17 00:00:00 2001 From: Oleksandr Natalenko Date: Mon, 25 Mar 2024 15:26:11 +0100 Subject: media: uvcvideo: Add quirk for invalid dev_sof in Logitech C920 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similarly to Logitech C922, C920 seems to also suffer from a firmware bug that breaks hardware timestamping. Add a quirk for this camera model too. Before applying the quirk: ``` 100 (4) [-] none 100 200717 B 212.919114 213.079004 33.727 fps ts mono/SoE 101 (5) [-] none 101 200889 B 213.003703 213.114996 11.822 fps ts mono/SoE 102 (6) [-] none 102 200926 B 213.035571 213.146999 31.379 fps ts mono/SoE 103 (7) [-] none 103 200839 B 213.067424 213.179003 31.394 fps ts mono/SoE 104 (0) [-] none 104 200692 B 213.293180 213.214991 4.430 fps ts mono/SoE 105 (1) [-] none 105 200937 B 213.322374 213.247001 34.254 fps ts mono/SoE 106 (2) [-] none 106 201013 B 213.352228 213.279005 33.496 fps ts mono/SoE … ``` After applying the quirk: ``` 154 (2) [-] none 154 192417 B 42.199823 42.207788 27.779 fps ts mono/SoE 155 (3) [-] none 155 192040 B 42.231834 42.239791 31.239 fps ts mono/SoE 156 (4) [-] none 156 192213 B 42.263823 42.271822 31.261 fps ts mono/SoE 157 (5) [-] none 157 191981 B 42.299824 42.303827 27.777 fps ts mono/SoE 158 (6) [-] none 158 191953 B 42.331835 42.339811 31.239 fps ts mono/SoE 159 (7) [-] none 159 191904 B 42.363824 42.371813 31.261 fps ts mono/SoE 160 (0) [-] none 160 192210 B 42.399834 42.407801 27.770 fps ts mono/SoE ``` Fixes: 5d0fd3c806b9 ("[media] uvcvideo: Disable hardware timestamps by default") Signed-off-by: Oleksandr Natalenko Reviewed-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20240325142611.15550-1-oleksandr@natalenko.name Signed-off-by: Laurent Pinchart --- drivers/media/usb/uvc/uvc_driver.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 6e55f91ac17f..d435b6a6c295 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2580,7 +2580,8 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, - .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTORE_CTRLS_ON_INIT) }, + .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTORE_CTRLS_ON_INIT + | UVC_QUIRK_INVALID_DEVICE_SOF) }, /* Logitech HD Pro Webcam C922 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, -- cgit v1.2.3 From c8931ef55bd325052ec496f242aea7f6de47dc9c Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 4 Apr 2024 17:56:18 +0000 Subject: media: uvcvideo: Enforce alignment of frame and interval Struct uvc_frame and interval (u32*) are packaged together on streaming->formats on a single contiguous allocation. Right now they are allocated right after uvc_format, without taking into consideration their required alignment. This is working fine because both structures have a field with a pointer, but it will stop working when the sizeof() of any of those structs is not a multiple of the sizeof(void*). Enforce that alignment during the allocation. Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20240404-uvc-align-v2-1-9e104b0ecfbd@chromium.org Signed-off-by: Laurent Pinchart --- drivers/media/usb/uvc/uvc_driver.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index d435b6a6c295..13c2c11cfdf6 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -687,16 +687,26 @@ static int uvc_parse_streaming(struct uvc_device *dev, goto error; } - size = nformats * sizeof(*format) + nframes * sizeof(*frame) + /* + * Allocate memory for the formats, the frames and the intervals, + * plus any required padding to guarantee that everything has the + * correct alignment. + */ + size = nformats * sizeof(*format); + size = ALIGN(size, __alignof__(*frame)) + nframes * sizeof(*frame); + size = ALIGN(size, __alignof__(*interval)) + nintervals * sizeof(*interval); + format = kzalloc(size, GFP_KERNEL); - if (format == NULL) { + if (!format) { ret = -ENOMEM; goto error; } - frame = (struct uvc_frame *)&format[nformats]; - interval = (u32 *)&frame[nframes]; + frame = (void *)format + nformats * sizeof(*format); + frame = PTR_ALIGN(frame, __alignof__(*frame)); + interval = (void *)frame + nframes * sizeof(*frame); + interval = PTR_ALIGN(interval, __alignof__(*interval)); streaming->formats = format; streaming->nformats = 0; -- cgit v1.2.3 From 86419686e66da5b90a07fb8a40ab138fe97189b5 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 2 Jun 2024 14:50:53 +0800 Subject: media: uvcvideo: Override default flags When the UVC device has a control that is readonly it doesn't set the SET_CUR flag. For example the privacy control has SET_CUR flag set in the defaults in the `uvc_ctrls` variable. Even if the device does not have it set, it's not cleared by uvc_ctrl_get_flags(). Originally written with assignment in commit 859086ae3636 ("media: uvcvideo: Apply flags from device to actual properties"). But changed to |= in commit 0dc68cabdb62 ("media: uvcvideo: Prevent setting unavailable flags"). It would not clear the default flags. With this patch applied the correct flags are reported to user space. Tested with: ``` > v4l2-ctl --list-ctrls | grep privacy privacy 0x009a0910 (bool) : default=0 value=0 flags=read-only ``` Signed-off-by: Daniel Schaefer Fixes: 0dc68cabdb62 ("media: uvcvideo: Prevent setting unavailable flags") Reviewed-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20240602065053.36850-1-dhs@frame.work Signed-off-by: Laurent Pinchart --- drivers/media/usb/uvc/uvc_ctrl.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 4b685f883e4d..a7d0ec22d95c 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -2031,7 +2031,13 @@ static int uvc_ctrl_get_flags(struct uvc_device *dev, else ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum, info->selector, data, 1); - if (!ret) + + if (!ret) { + info->flags &= ~(UVC_CTRL_FLAG_GET_CUR | + UVC_CTRL_FLAG_SET_CUR | + UVC_CTRL_FLAG_AUTO_UPDATE | + UVC_CTRL_FLAG_ASYNCHRONOUS); + info->flags |= (data[0] & UVC_CONTROL_CAP_GET ? UVC_CTRL_FLAG_GET_CUR : 0) | (data[0] & UVC_CONTROL_CAP_SET ? @@ -2040,6 +2046,7 @@ static int uvc_ctrl_get_flags(struct uvc_device *dev, UVC_CTRL_FLAG_AUTO_UPDATE : 0) | (data[0] & UVC_CONTROL_CAP_ASYNCHRONOUS ? UVC_CTRL_FLAG_ASYNCHRONOUS : 0); + } kfree(data); return ret; -- cgit v1.2.3 From c397e8c45d911443b4ab60084fb723edf2a5b604 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 15 Jan 2023 22:52:10 +0200 Subject: media: uvcvideo: Force UVC version to 1.0a for 0408:4035 The Quanta ACER HD User Facing camera reports a UVC 1.50 version, but implements UVC 1.0a as shown by the UVC probe control being 26 bytes long. Force the UVC version for that device. Reported-by: Giuliano Lotta Closes: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2000947 Link: https://lore.kernel.org/r/20230115205210.20077-1-laurent.pinchart@ideasonboard.com Tested-by: Giuliano Lotta Signed-off-by: Laurent Pinchart --- drivers/media/usb/uvc/uvc_driver.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 13c2c11cfdf6..a557314d5145 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2471,6 +2471,17 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = UVC_PC_PROTOCOL_15, .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, + /* Quanta ACER HD User Facing */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0408, + .idProduct = 0x4035, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = UVC_PC_PROTOCOL_15, + .driver_info = (kernel_ulong_t)&(const struct uvc_device_info){ + .uvc_version = 0x010a, + } }, /* LogiLink Wireless Webcam */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, -- cgit v1.2.3 From 9e3d55fbd160b3ca376599a68b4cddfdc67d4153 Mon Sep 17 00:00:00 2001 From: Michal Pecio Date: Sun, 14 Apr 2024 19:00:40 +0200 Subject: media: uvcvideo: Fix the bandwdith quirk on USB 3.x The bandwidth fixup quirk doesn't know that SuperSpeed exists and has the same 8 service intervals per millisecond as High Speed, hence its calculations are wrong. Assume that all speeds from HS up use 8 intervals per millisecond. No further changes are needed, updated code has been confirmed to work with all speeds from FS to SS. Signed-off-by: Michal Pecio Reviewed-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20240414190040.2255a0bc@foxbook Signed-off-by: Laurent Pinchart --- drivers/media/usb/uvc/uvc_video.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 4dfc1b86bdee..cd9c29532fb0 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -214,13 +214,13 @@ static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, * Compute a bandwidth estimation by multiplying the frame * size by the number of video frames per second, divide the * result by the number of USB frames (or micro-frames for - * high-speed devices) per second and add the UVC header size - * (assumed to be 12 bytes long). + * high- and super-speed devices) per second and add the UVC + * header size (assumed to be 12 bytes long). */ bandwidth = frame->wWidth * frame->wHeight / 8 * format->bpp; bandwidth *= 10000000 / interval + 1; bandwidth /= 1000; - if (stream->dev->udev->speed == USB_SPEED_HIGH) + if (stream->dev->udev->speed >= USB_SPEED_HIGH) bandwidth /= 8; bandwidth += 12; -- cgit v1.2.3 From 8f4362a8d42b918e4832037ab3cc6f25de61a080 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 10 Jun 2024 23:09:52 +0000 Subject: media: uvcvideo: Allow custom control mapping Some advanced controls might not be completely implemented by vendors. If the controls are a enumeration, UVC does not gives a way to probe what is implemented and what is not. Let's create a new callback function where heuristics can be implemented to detect what is implemented and what not and update the control mapping accordingly. Reviewed-by: Sergey Senozhatsky Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Reviewed-by: Ricardo Ribalda --- drivers/media/usb/uvc/uvc_ctrl.c | 7 +++++++ drivers/media/usb/uvc/uvcvideo.h | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index a7d0ec22d95c..c619370f9afd 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -2659,6 +2659,13 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain, for (i = 0; i < ARRAY_SIZE(uvc_ctrl_mappings); ++i) { const struct uvc_control_mapping *mapping = &uvc_ctrl_mappings[i]; + /* Let the device provide a custom mapping. */ + if (mapping->filter_mapping) { + mapping = mapping->filter_mapping(chain, ctrl); + if (!mapping) + continue; + } + if (uvc_entity_match_guid(ctrl->entity, mapping->entity) && ctrl->info.selector == mapping->selector) __uvc_ctrl_add_mapping(chain, ctrl, mapping); diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index f21207debd54..5cf5e194efd9 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -87,7 +87,9 @@ struct gpio_desc; struct sg_table; +struct uvc_control; struct uvc_device; +struct uvc_video_chain; /* * TODO: Put the most frequently accessed fields at the beginning of @@ -126,6 +128,9 @@ struct uvc_control_mapping { s32 master_manual; u32 slave_ids[2]; + const struct uvc_control_mapping *(*filter_mapping) + (struct uvc_video_chain *chain, + struct uvc_control *ctrl); s32 (*get)(struct uvc_control_mapping *mapping, u8 query, const u8 *data); void (*set)(struct uvc_control_mapping *mapping, s32 value, -- cgit v1.2.3 From a8505ad3be3e931c02d7dea505ab7783530fcdaa Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 10 Jun 2024 23:09:53 +0000 Subject: media: uvcvideo: Refactor Power Line Frequency limit selection Move the PLF mapping logic to its own mapping filter function. This commit does not introduce any new functionality to the logic, it is just a preparation patch. Reviewed-by: Sergey Senozhatsky Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Reviewed-by: Ricardo Ribalda --- drivers/media/usb/uvc/uvc_ctrl.c | 88 +++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 38 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index c619370f9afd..f5230cd14577 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -459,6 +459,51 @@ static void uvc_ctrl_set_rel_speed(struct uvc_control_mapping *mapping, data[first+1] = min_t(int, abs(value), 0xff); } +const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, + .size = 2, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_MENU, + .data_type = UVC_CTRL_DATA_TYPE_ENUM, + .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ, + V4L2_CID_POWER_LINE_FREQUENCY_50HZ), +}; + +const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, + .size = 2, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_MENU, + .data_type = UVC_CTRL_DATA_TYPE_ENUM, + .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ, + V4L2_CID_POWER_LINE_FREQUENCY_DISABLED), +}; + +static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc15 = { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, + .size = 2, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_MENU, + .data_type = UVC_CTRL_DATA_TYPE_ENUM, + .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_AUTO, + V4L2_CID_POWER_LINE_FREQUENCY_DISABLED), +}; + +static const struct uvc_control_mapping *uvc_ctrl_filter_plf_mapping( + struct uvc_video_chain *chain, struct uvc_control *ctrl) +{ + if (chain->dev->uvc_version < 0x150) + return &uvc_ctrl_power_line_mapping_uvc11; + else + return &uvc_ctrl_power_line_mapping_uvc15; +} + static const struct uvc_control_mapping uvc_ctrl_mappings[] = { { .id = V4L2_CID_BRIGHTNESS, @@ -748,51 +793,18 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = { .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, }, -}; - -const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = { - .id = V4L2_CID_POWER_LINE_FREQUENCY, - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, - .size = 2, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_MENU, - .data_type = UVC_CTRL_DATA_TYPE_ENUM, - .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ, - V4L2_CID_POWER_LINE_FREQUENCY_50HZ), -}; - -const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = { - .id = V4L2_CID_POWER_LINE_FREQUENCY, - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, - .size = 2, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_MENU, - .data_type = UVC_CTRL_DATA_TYPE_ENUM, - .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_60HZ, - V4L2_CID_POWER_LINE_FREQUENCY_DISABLED), + { + .entity = UVC_GUID_UVC_PROCESSING, + .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, + .filter_mapping = uvc_ctrl_filter_plf_mapping, + }, }; static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc11[] = { - &uvc_ctrl_power_line_mapping_uvc11, NULL, /* Sentinel */ }; -static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc15 = { - .id = V4L2_CID_POWER_LINE_FREQUENCY, - .entity = UVC_GUID_UVC_PROCESSING, - .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, - .size = 2, - .offset = 0, - .v4l2_type = V4L2_CTRL_TYPE_MENU, - .data_type = UVC_CTRL_DATA_TYPE_ENUM, - .menu_mask = GENMASK(V4L2_CID_POWER_LINE_FREQUENCY_AUTO, - V4L2_CID_POWER_LINE_FREQUENCY_DISABLED), -}; - static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc15[] = { - &uvc_ctrl_power_line_mapping_uvc15, NULL, /* Sentinel */ }; -- cgit v1.2.3 From b2b5fcb1c5b645d5177ef3e3f41c7a706fc2688d Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 10 Jun 2024 23:09:54 +0000 Subject: media: uvcvideo: Probe the PLF characteristics The UVC 1.5 standard defines 4 values for the PLF control: Off, 50Hz, 60Hz and Auto. But it does not clearly define if all the values must be implemented or not. Instead of just using the UVC version to determine what the PLF control can do, probe it. Reviewed-by: Sergey Senozhatsky Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Reviewed-by: Ricardo Ribalda --- drivers/media/usb/uvc/uvc_ctrl.c | 49 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index f5230cd14577..1ce1caaccfb7 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -498,10 +498,53 @@ static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc15 = { static const struct uvc_control_mapping *uvc_ctrl_filter_plf_mapping( struct uvc_video_chain *chain, struct uvc_control *ctrl) { + const struct uvc_control_mapping *out_mapping = + &uvc_ctrl_power_line_mapping_uvc11; + u8 *buf __free(kfree) = NULL; + u8 init_val; + int ret; + + buf = kmalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return NULL; + + /* Save the current PLF value, so we can restore it. */ + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id, + chain->dev->intfnum, ctrl->info.selector, + buf, sizeof(*buf)); + /* If we cannot read the control skip it. */ + if (ret) + return NULL; + init_val = *buf; + + /* If PLF value cannot be set to off, it is limited. */ + *buf = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED; + ret = uvc_query_ctrl(chain->dev, UVC_SET_CUR, ctrl->entity->id, + chain->dev->intfnum, ctrl->info.selector, + buf, sizeof(*buf)); + if (ret) + return &uvc_ctrl_power_line_mapping_limited; + + /* UVC 1.1 does not define auto, we can exit. */ if (chain->dev->uvc_version < 0x150) - return &uvc_ctrl_power_line_mapping_uvc11; - else - return &uvc_ctrl_power_line_mapping_uvc15; + goto end; + + /* Check if the device supports auto. */ + *buf = V4L2_CID_POWER_LINE_FREQUENCY_AUTO; + ret = uvc_query_ctrl(chain->dev, UVC_SET_CUR, ctrl->entity->id, + chain->dev->intfnum, ctrl->info.selector, + buf, sizeof(*buf)); + if (!ret) + out_mapping = &uvc_ctrl_power_line_mapping_uvc15; + +end: + /* Restore initial value and add mapping. */ + *buf = init_val; + uvc_query_ctrl(chain->dev, UVC_SET_CUR, ctrl->entity->id, + chain->dev->intfnum, ctrl->info.selector, + buf, sizeof(*buf)); + + return out_mapping; } static const struct uvc_control_mapping uvc_ctrl_mappings[] = { -- cgit v1.2.3 From 6c7f1f756e75b44e096898d9907dcd0c81000d0b Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 10 Jun 2024 23:09:55 +0000 Subject: media: uvcvideo: Cleanup version-specific mapping We do not have more version specific mappings. Let's remove this code for now. It can be easily reverted later if needed. Reviewed-by: Laurent Pinchart Reviewed-by: Sergey Senozhatsky Signed-off-by: Ricardo Ribalda Signed-off-by: Laurent Pinchart Reviewed-by: Ricardo Ribalda --- drivers/media/usb/uvc/uvc_ctrl.c | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 1ce1caaccfb7..024fa86ed2d5 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -843,14 +843,6 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = { }, }; -static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc11[] = { - NULL, /* Sentinel */ -}; - -static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc15[] = { - NULL, /* Sentinel */ -}; - /* ------------------------------------------------------------------------ * Utility functions */ @@ -2653,7 +2645,6 @@ static void uvc_ctrl_prune_entity(struct uvc_device *dev, static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain, struct uvc_control *ctrl) { - const struct uvc_control_mapping **mappings; unsigned int i; /* @@ -2725,18 +2716,6 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain, ctrl->info.selector == mapping->selector) __uvc_ctrl_add_mapping(chain, ctrl, mapping); } - - /* Finally process version-specific mappings. */ - mappings = chain->dev->uvc_version < 0x0150 - ? uvc_ctrl_mappings_uvc11 : uvc_ctrl_mappings_uvc15; - - for (i = 0; mappings[i]; ++i) { - const struct uvc_control_mapping *mapping = mappings[i]; - - if (uvc_entity_match_guid(ctrl->entity, mapping->entity) && - ctrl->info.selector == mapping->selector) - __uvc_ctrl_add_mapping(chain, ctrl, mapping); - } } /* -- cgit v1.2.3 From e5cbddd09d4a9d66ed93e7c80ebf66ecf50ec3f3 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 10 Jun 2024 23:09:56 +0000 Subject: media: uvcvideo: Remove PLF device quirking We can use heuristics to figure out the proper range of the control instead of quirking every single device. Reviewed-by: Sergey Senozhatsky Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Reviewed-by: Ricardo Ribalda --- drivers/media/usb/uvc/uvc_ctrl.c | 4 +- drivers/media/usb/uvc/uvc_driver.c | 131 ------------------------------------- drivers/media/usb/uvc/uvcvideo.h | 2 - 3 files changed, 2 insertions(+), 135 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 024fa86ed2d5..2cc99a8b5c02 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -459,7 +459,7 @@ static void uvc_ctrl_set_rel_speed(struct uvc_control_mapping *mapping, data[first+1] = min_t(int, abs(value), 0xff); } -const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = { +static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = { .id = V4L2_CID_POWER_LINE_FREQUENCY, .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, @@ -471,7 +471,7 @@ const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = { V4L2_CID_POWER_LINE_FREQUENCY_50HZ), }; -const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = { +static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = { .id = V4L2_CID_POWER_LINE_FREQUENCY, .entity = UVC_GUID_UVC_PROCESSING, .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index a557314d5145..f0febdc08c2d 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2400,20 +2400,6 @@ MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); * Driver initialization and cleanup */ -static const struct uvc_device_info uvc_ctrl_power_line_limited = { - .mappings = (const struct uvc_control_mapping *[]) { - &uvc_ctrl_power_line_mapping_limited, - NULL, /* Sentinel */ - }, -}; - -static const struct uvc_device_info uvc_ctrl_power_line_uvc11 = { - .mappings = (const struct uvc_control_mapping *[]) { - &uvc_ctrl_power_line_mapping_uvc11, - NULL, /* Sentinel */ - }, -}; - static const struct uvc_device_info uvc_quirk_probe_minmax = { .quirks = UVC_QUIRK_PROBE_MINMAX, }; @@ -2444,33 +2430,6 @@ static const struct uvc_device_info uvc_quirk_force_y8 = { * though they are compliant. */ static const struct usb_device_id uvc_ids[] = { - /* Quanta USB2.0 HD UVC Webcam */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x0408, - .idProduct = 0x3090, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, - /* Quanta USB2.0 HD UVC Webcam */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x0408, - .idProduct = 0x4030, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, - /* Quanta USB2.0 HD UVC Webcam */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x0408, - .idProduct = 0x4034, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = UVC_PC_PROTOCOL_15, - .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, /* Quanta ACER HD User Facing */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2648,42 +2607,6 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTRICT_FRAME_RATE) }, - /* Chicony EasyCamera */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x04f2, - .idProduct = 0xb5eb, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, - /* Chicony Electronics Co., Ltd Integrated Camera */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x04f2, - .idProduct = 0xb67c, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = UVC_PC_PROTOCOL_15, - .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 }, - /* Chicony EasyCamera */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x04f2, - .idProduct = 0xb6ba, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, - /* Chicony EasyCamera */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x04f2, - .idProduct = 0xb746, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, /* Alcor Micro AU3820 (Future Boy PC USB Webcam) */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -3068,15 +2991,6 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_FORCE_BPP) }, - /* SunplusIT Inc HD Camera */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x2b7e, - .idProduct = 0xb752, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = UVC_PC_PROTOCOL_15, - .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 }, /* Insta360 Link */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -3086,51 +3000,6 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_DISABLE_AUTOSUSPEND) }, - /* Lenovo Integrated Camera */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x30c9, - .idProduct = 0x0093, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = UVC_PC_PROTOCOL_15, - .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 }, - /* Sonix Technology USB 2.0 Camera */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x3277, - .idProduct = 0x0072, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, - /* Shine-Optics Integrated Camera */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x3277, - .idProduct = 0x009e, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = UVC_PC_PROTOCOL_15, - .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 }, - /* Acer EasyCamera */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x1172, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, - /* Acer EasyCamera */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x1180, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, /* Intel D410/ASR depth camera */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 5cf5e194efd9..d84933294e2a 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -757,8 +757,6 @@ int uvc_status_start(struct uvc_device *dev, gfp_t flags); void uvc_status_stop(struct uvc_device *dev); /* Controls */ -extern const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited; -extern const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11; extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops; int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, -- cgit v1.2.3 From 8c40efeda94108d65c52038ea82ee83b2fb933e2 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 10 Jun 2024 23:09:57 +0000 Subject: media: uvcvideo: Remove mappings form uvc_device_info We do not have any quirk device making us of this. Remove from now. It can be easily reverted later if needed. Signed-off-by: Ricardo Ribalda Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Reviewed-by: Ricardo Ribalda --- drivers/media/usb/uvc/uvc_ctrl.c | 27 +-------------------------- drivers/media/usb/uvc/uvcvideo.h | 1 - 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 2cc99a8b5c02..0136df5732ba 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -2676,32 +2676,7 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain, if (!ctrl->initialized) return; - /* - * First check if the device provides a custom mapping for this control, - * used to override standard mappings for non-conformant devices. Don't - * process standard mappings if a custom mapping is found. This - * mechanism doesn't support combining standard and custom mappings for - * a single control. - */ - if (chain->dev->info->mappings) { - bool custom = false; - - for (i = 0; chain->dev->info->mappings[i]; ++i) { - const struct uvc_control_mapping *mapping = - chain->dev->info->mappings[i]; - - if (uvc_entity_match_guid(ctrl->entity, mapping->entity) && - ctrl->info.selector == mapping->selector) { - __uvc_ctrl_add_mapping(chain, ctrl, mapping); - custom = true; - } - } - - if (custom) - return; - } - - /* Process common mappings next. */ + /* Process common mappings. */ for (i = 0; i < ARRAY_SIZE(uvc_ctrl_mappings); ++i) { const struct uvc_control_mapping *mapping = &uvc_ctrl_mappings[i]; diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index d84933294e2a..b7d24a853ce4 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -531,7 +531,6 @@ struct uvc_device_info { u32 quirks; u32 meta_format; u16 uvc_version; - const struct uvc_control_mapping **mappings; }; struct uvc_status_streaming { -- cgit v1.2.3 From 9caf253e8ad6f4c66f5591bac900f9f68b6b6620 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 17 Jun 2024 18:11:24 +0200 Subject: media: rcar-vin: Fix YUYV8_1X16 handling for CSI-2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The YUYV8_1X16 and UYVY8_1X16 formats are treated as 'ITU-R BT.601/BT.1358 16-bit YCbCr-422 input' (YUV16 - 0x5) in the R-Car VIN driver and are thus disallowed when capturing frames from the R-Car CSI-2 interface according to the hardware manual. As the 1X16 format variants are meant to be used with serial busses they have to be treated as 'YCbCr-422 8-bit data input' (0x1) when capturing from CSI-2, which is a valid setting for CSI-2. Commit 78b3f9d75a62 ("media: rcar-vin: Add check that input interface and format are valid") disallowed capturing YUV16 when using the CSI-2 interface. Fix this by using YUV8_BT601 for YCbCr422 when CSI-2 is in use. Fixes: 78b3f9d75a62 ("media: rcar-vin: Add check that input interface and format are valid") Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Tested-by: Niklas Söderlund Link: https://lore.kernel.org/r/20240617161135.130719-2-jacopo.mondi@ideasonboard.com Signed-off-by: Laurent Pinchart --- drivers/media/platform/renesas/rcar-vin/rcar-dma.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c index e2c40abc6d3d..21d5b2815e86 100644 --- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c +++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c @@ -742,12 +742,22 @@ static int rvin_setup(struct rvin_dev *vin) */ switch (vin->mbus_code) { case MEDIA_BUS_FMT_YUYV8_1X16: - /* BT.601/BT.1358 16bit YCbCr422 */ - vnmc |= VNMC_INF_YUV16; + if (vin->is_csi) + /* YCbCr422 8-bit */ + vnmc |= VNMC_INF_YUV8_BT601; + else + /* BT.601/BT.1358 16bit YCbCr422 */ + vnmc |= VNMC_INF_YUV16; input_is_yuv = true; break; case MEDIA_BUS_FMT_UYVY8_1X16: - vnmc |= VNMC_INF_YUV16 | VNMC_YCAL; + if (vin->is_csi) + /* YCbCr422 8-bit */ + vnmc |= VNMC_INF_YUV8_BT601; + else + /* BT.601/BT.1358 16bit YCbCr422 */ + vnmc |= VNMC_INF_YUV16; + vnmc |= VNMC_YCAL; input_is_yuv = true; break; case MEDIA_BUS_FMT_UYVY8_2X8: -- cgit v1.2.3 From e306183628f7c2e95f9bf853d8fcb86288f606de Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 17 Jun 2024 18:11:25 +0200 Subject: media: rcar-csi2: Disable runtime_pm in probe error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disable pm_runtime in the probe() function error path. Fixes: 769afd212b16 ("media: rcar-csi2: add Renesas R-Car MIPI CSI-2 receiver driver") Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Tested-by: Niklas Söderlund Link: https://lore.kernel.org/r/20240617161135.130719-3-jacopo.mondi@ideasonboard.com Signed-off-by: Laurent Pinchart --- drivers/media/platform/renesas/rcar-csi2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/renesas/rcar-csi2.c b/drivers/media/platform/renesas/rcar-csi2.c index 582d5e35db0e..249e58c77176 100644 --- a/drivers/media/platform/renesas/rcar-csi2.c +++ b/drivers/media/platform/renesas/rcar-csi2.c @@ -1914,12 +1914,14 @@ static int rcsi2_probe(struct platform_device *pdev) ret = v4l2_async_register_subdev(&priv->subdev); if (ret < 0) - goto error_async; + goto error_pm_runtime; dev_info(priv->dev, "%d lanes found\n", priv->lanes); return 0; +error_pm_runtime: + pm_runtime_disable(&pdev->dev); error_async: v4l2_async_nf_unregister(&priv->notifier); v4l2_async_nf_cleanup(&priv->notifier); -- cgit v1.2.3 From f6d64d0d2897ed4e85ac00afe43e45c8b8fc0c44 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 17 Jun 2024 18:11:26 +0200 Subject: media: rcar-csi2: Cleanup subdevice in remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cleanup the V4L2 subdevice in the driver's remove function to ensure its async connection are freed, and guarantee in future that the subdev active state is cleaned up. Fixes: 769afd212b16 ("media: rcar-csi2: add Renesas R-Car MIPI CSI-2 receiver driver") Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Tested-by: Niklas Söderlund Link: https://lore.kernel.org/r/20240617161135.130719-4-jacopo.mondi@ideasonboard.com Signed-off-by: Laurent Pinchart --- drivers/media/platform/renesas/rcar-csi2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/renesas/rcar-csi2.c b/drivers/media/platform/renesas/rcar-csi2.c index 249e58c77176..2d464e43a5be 100644 --- a/drivers/media/platform/renesas/rcar-csi2.c +++ b/drivers/media/platform/renesas/rcar-csi2.c @@ -1938,6 +1938,7 @@ static void rcsi2_remove(struct platform_device *pdev) v4l2_async_nf_unregister(&priv->notifier); v4l2_async_nf_cleanup(&priv->notifier); v4l2_async_unregister_subdev(&priv->subdev); + v4l2_subdev_cleanup(&priv->subdev); pm_runtime_disable(&pdev->dev); -- cgit v1.2.3 From a399b36ec83b2b1390f15d493fbd0db2ff467985 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 17 Jun 2024 18:11:27 +0200 Subject: media: rcar-csi2: Use the subdev active state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create the subdevice state with v4l2_subdev_init_finalize() and implement the init_state() operation to guarantee the state is initialized. Store the current image format in the subdev active state and remove it from the driver private structure. To guarantee the same image format is applied to all source pads, propagate the format from the sink pad to the sources, disallowing changing format on a source pad. To support both gen3 and gen4, which feature a different number of source pads, introduce an helper function to return the correct number of pads. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Tested-by: Niklas Söderlund Link: https://lore.kernel.org/r/20240617161135.130719-5-jacopo.mondi@ideasonboard.com Signed-off-by: Laurent Pinchart --- drivers/media/platform/renesas/rcar-csi2.c | 152 +++++++++++++++++------------ 1 file changed, 90 insertions(+), 62 deletions(-) diff --git a/drivers/media/platform/renesas/rcar-csi2.c b/drivers/media/platform/renesas/rcar-csi2.c index 2d464e43a5be..c419ddb4c5a2 100644 --- a/drivers/media/platform/renesas/rcar-csi2.c +++ b/drivers/media/platform/renesas/rcar-csi2.c @@ -587,7 +587,8 @@ enum rcar_csi2_pads { struct rcar_csi2_info { int (*init_phtw)(struct rcar_csi2 *priv, unsigned int mbps); int (*phy_post_init)(struct rcar_csi2 *priv); - int (*start_receiver)(struct rcar_csi2 *priv); + int (*start_receiver)(struct rcar_csi2 *priv, + struct v4l2_subdev_state *state); void (*enter_standby)(struct rcar_csi2 *priv); const struct rcsi2_mbps_reg *hsfreqrange; unsigned int csi0clkfreqrange; @@ -613,8 +614,6 @@ struct rcar_csi2 { int channel_vc[4]; - struct mutex lock; /* Protects mf and stream_count. */ - struct v4l2_mbus_framefmt mf; int stream_count; bool cphy; @@ -632,6 +631,16 @@ static inline struct rcar_csi2 *notifier_to_csi2(struct v4l2_async_notifier *n) return container_of(n, struct rcar_csi2, notifier); } +static unsigned int rcsi2_num_pads(const struct rcar_csi2 *priv) +{ + /* Used together with R-Car ISP: one sink and one source pad. */ + if (priv->info->use_isp) + return 2; + + /* Used together with R-Car VIN: one sink and four source pads. */ + return 5; +} + static u32 rcsi2_read(struct rcar_csi2 *priv, unsigned int reg) { return ioread32(priv->base + reg); @@ -808,20 +817,25 @@ static int rcsi2_get_active_lanes(struct rcar_csi2 *priv, return 0; } -static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv) +static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv, + struct v4l2_subdev_state *state) { const struct rcar_csi2_format *format; u32 phycnt, vcdt = 0, vcdt2 = 0, fld = 0; + const struct v4l2_mbus_framefmt *fmt; unsigned int lanes; unsigned int i; int mbps, ret; + /* Use the format on the sink pad to compute the receiver config. */ + fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK); + dev_dbg(priv->dev, "Input size (%ux%u%c)\n", - priv->mf.width, priv->mf.height, - priv->mf.field == V4L2_FIELD_NONE ? 'p' : 'i'); + fmt->width, fmt->height, + fmt->field == V4L2_FIELD_NONE ? 'p' : 'i'); /* Code is validated in set_fmt. */ - format = rcsi2_code_to_fmt(priv->mf.code); + format = rcsi2_code_to_fmt(fmt->code); if (!format) return -EINVAL; @@ -849,11 +863,11 @@ static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv) vcdt2 |= vcdt_part << ((i % 2) * 16); } - if (priv->mf.field == V4L2_FIELD_ALTERNATE) { + if (fmt->field == V4L2_FIELD_ALTERNATE) { fld = FLD_DET_SEL(1) | FLD_FLD_EN4 | FLD_FLD_EN3 | FLD_FLD_EN2 | FLD_FLD_EN; - if (priv->mf.height == 240) + if (fmt->height == 240) fld |= FLD_FLD_NUM(0); else fld |= FLD_FLD_NUM(1); @@ -1049,15 +1063,18 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps) return 0; } -static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv) +static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv, + struct v4l2_subdev_state *state) { const struct rcar_csi2_format *format; + const struct v4l2_mbus_framefmt *fmt; unsigned int lanes; int msps; int ret; - /* Calculate parameters */ - format = rcsi2_code_to_fmt(priv->mf.code); + /* Use the format on the sink pad to compute the receiver config. */ + fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK); + format = rcsi2_code_to_fmt(fmt->code); if (!format) return -EINVAL; @@ -1114,7 +1131,7 @@ static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv) return 0; } -static int rcsi2_start(struct rcar_csi2 *priv) +static int rcsi2_start(struct rcar_csi2 *priv, struct v4l2_subdev_state *state) { int ret; @@ -1122,7 +1139,7 @@ static int rcsi2_start(struct rcar_csi2 *priv) if (ret < 0) return ret; - ret = priv->info->start_receiver(priv); + ret = priv->info->start_receiver(priv, state); if (ret) { rcsi2_enter_standby(priv); return ret; @@ -1146,17 +1163,16 @@ static void rcsi2_stop(struct rcar_csi2 *priv) static int rcsi2_s_stream(struct v4l2_subdev *sd, int enable) { struct rcar_csi2 *priv = sd_to_csi2(sd); + struct v4l2_subdev_state *state; int ret = 0; - mutex_lock(&priv->lock); + if (!priv->remote) + return -ENODEV; - if (!priv->remote) { - ret = -ENODEV; - goto out; - } + state = v4l2_subdev_lock_and_get_active_state(&priv->subdev); if (enable && priv->stream_count == 0) { - ret = rcsi2_start(priv); + ret = rcsi2_start(priv, state); if (ret) goto out; } else if (!enable && priv->stream_count == 1) { @@ -1165,49 +1181,29 @@ static int rcsi2_s_stream(struct v4l2_subdev *sd, int enable) priv->stream_count += enable ? 1 : -1; out: - mutex_unlock(&priv->lock); + v4l2_subdev_unlock_state(state); return ret; } static int rcsi2_set_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { struct rcar_csi2 *priv = sd_to_csi2(sd); - struct v4l2_mbus_framefmt *framefmt; + unsigned int num_pads = rcsi2_num_pads(priv); - mutex_lock(&priv->lock); + if (format->pad > RCAR_CSI2_SINK) + return v4l2_subdev_get_fmt(sd, state, format); if (!rcsi2_code_to_fmt(format->format.code)) format->format.code = rcar_csi2_formats[0].code; - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - priv->mf = format->format; - } else { - framefmt = v4l2_subdev_state_get_format(sd_state, 0); - *framefmt = format->format; - } - - mutex_unlock(&priv->lock); - - return 0; -} - -static int rcsi2_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct rcar_csi2 *priv = sd_to_csi2(sd); - - mutex_lock(&priv->lock); - - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - format->format = priv->mf; - else - format->format = *v4l2_subdev_state_get_format(sd_state, 0); + *v4l2_subdev_state_get_format(state, format->pad) = format->format; - mutex_unlock(&priv->lock); + /* Propagate the format to the source pads. */ + for (unsigned int i = RCAR_CSI2_SOURCE_VC0; i < num_pads; i++) + *v4l2_subdev_state_get_format(state, i) = format->format; return 0; } @@ -1218,7 +1214,7 @@ static const struct v4l2_subdev_video_ops rcar_csi2_video_ops = { static const struct v4l2_subdev_pad_ops rcar_csi2_pad_ops = { .set_fmt = rcsi2_set_pad_format, - .get_fmt = rcsi2_get_pad_format, + .get_fmt = v4l2_subdev_get_fmt, }; static const struct v4l2_subdev_ops rcar_csi2_subdev_ops = { @@ -1226,6 +1222,33 @@ static const struct v4l2_subdev_ops rcar_csi2_subdev_ops = { .pad = &rcar_csi2_pad_ops, }; +static int rcsi2_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct rcar_csi2 *priv = sd_to_csi2(sd); + unsigned int num_pads = rcsi2_num_pads(priv); + + static const struct v4l2_mbus_framefmt rcar_csi2_default_fmt = { + .width = 1920, + .height = 1080, + .code = MEDIA_BUS_FMT_RGB888_1X24, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, + .quantization = V4L2_QUANTIZATION_DEFAULT, + .xfer_func = V4L2_XFER_FUNC_DEFAULT, + }; + + for (unsigned int i = RCAR_CSI2_SINK; i < num_pads; i++) + *v4l2_subdev_state_get_format(state, i) = rcar_csi2_default_fmt; + + return 0; +} + +static const struct v4l2_subdev_internal_ops rcar_csi2_internal_ops = { + .init_state = rcsi2_init_state, +}; + static irqreturn_t rcsi2_irq(int irq, void *data) { struct rcar_csi2 *priv = data; @@ -1251,14 +1274,17 @@ static irqreturn_t rcsi2_irq(int irq, void *data) static irqreturn_t rcsi2_irq_thread(int irq, void *data) { + struct v4l2_subdev_state *state; struct rcar_csi2 *priv = data; - mutex_lock(&priv->lock); + state = v4l2_subdev_lock_and_get_active_state(&priv->subdev); + rcsi2_stop(priv); usleep_range(1000, 2000); - if (rcsi2_start(priv)) + if (rcsi2_start(priv, state)) dev_warn(priv->dev, "Failed to restart CSI-2 receiver\n"); - mutex_unlock(&priv->lock); + + v4l2_subdev_unlock_state(state); return IRQ_HANDLED; } @@ -1870,23 +1896,23 @@ static int rcsi2_probe(struct platform_device *pdev) priv->dev = &pdev->dev; - mutex_init(&priv->lock); priv->stream_count = 0; ret = rcsi2_probe_resources(priv, pdev); if (ret) { dev_err(priv->dev, "Failed to get resources\n"); - goto error_mutex; + return ret; } platform_set_drvdata(pdev, priv); ret = rcsi2_parse_dt(priv); if (ret) - goto error_mutex; + return ret; priv->subdev.owner = THIS_MODULE; priv->subdev.dev = &pdev->dev; + priv->subdev.internal_ops = &rcar_csi2_internal_ops; v4l2_subdev_init(&priv->subdev, &rcar_csi2_subdev_ops); v4l2_set_subdevdata(&priv->subdev, &pdev->dev); snprintf(priv->subdev.name, sizeof(priv->subdev.name), "%s %s", @@ -1896,7 +1922,7 @@ static int rcsi2_probe(struct platform_device *pdev) priv->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; priv->subdev.entity.ops = &rcar_csi2_entity_ops; - num_pads = priv->info->use_isp ? 2 : NR_OF_RCAR_CSI2_PAD; + num_pads = rcsi2_num_pads(priv); priv->pads[RCAR_CSI2_SINK].flags = MEDIA_PAD_FL_SINK; for (i = RCAR_CSI2_SOURCE_VC0; i < num_pads; i++) @@ -1912,21 +1938,25 @@ static int rcsi2_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); + ret = v4l2_subdev_init_finalize(&priv->subdev); + if (ret) + goto error_pm_runtime; + ret = v4l2_async_register_subdev(&priv->subdev); if (ret < 0) - goto error_pm_runtime; + goto error_subdev; dev_info(priv->dev, "%d lanes found\n", priv->lanes); return 0; +error_subdev: + v4l2_subdev_cleanup(&priv->subdev); error_pm_runtime: pm_runtime_disable(&pdev->dev); error_async: v4l2_async_nf_unregister(&priv->notifier); v4l2_async_nf_cleanup(&priv->notifier); -error_mutex: - mutex_destroy(&priv->lock); return ret; } @@ -1941,8 +1971,6 @@ static void rcsi2_remove(struct platform_device *pdev) v4l2_subdev_cleanup(&priv->subdev); pm_runtime_disable(&pdev->dev); - - mutex_destroy(&priv->lock); } static struct platform_driver rcar_csi2_pdrv = { -- cgit v1.2.3 From f6ef8e21f2688dfc5a31bb98a996271119d54e7f Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 17 Jun 2024 18:11:28 +0200 Subject: media: adv748x-csi2: Implement enum_mbus_codes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define a list of supported mbus codes for the TXA and TXB CSI-2 transmitters and implement the enum_mbus_code operation. The TXB transmitter only support YUV422 while the TXA one supports multiple formats as reported by the chip's manual in section 9.7. but the HDMI and AFE subdevices only provide RGB888 and YUV422, so only list those ones here. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Tested-by: Niklas Söderlund Link: https://lore.kernel.org/r/20240617161135.130719-6-jacopo.mondi@ideasonboard.com Signed-off-by: Laurent Pinchart --- drivers/media/i2c/adv748x/adv748x-csi2.c | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index 5b265b722394..29b18b6c8b0e 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -14,6 +14,15 @@ #include "adv748x.h" +static const unsigned int adv748x_csi2_txa_fmts[] = { + MEDIA_BUS_FMT_UYVY8_1X16, + MEDIA_BUS_FMT_RGB888_1X24, +}; + +static const unsigned int adv748x_csi2_txb_fmts[] = { + MEDIA_BUS_FMT_UYVY8_1X16, +}; + int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx, unsigned int vc) { return tx_write(tx, ADV748X_CSI_VC_REF, vc << ADV748X_CSI_VC_REF_SHIFT); @@ -139,6 +148,41 @@ static const struct v4l2_subdev_video_ops adv748x_csi2_video_ops = { * But we must support setting the pad formats for format propagation. */ +static int adv748x_csi2_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); + const unsigned int *codes = is_txa(tx) ? + adv748x_csi2_txa_fmts : + adv748x_csi2_txb_fmts; + size_t num_fmts = is_txa(tx) ? ARRAY_SIZE(adv748x_csi2_txa_fmts) + : ARRAY_SIZE(adv748x_csi2_txb_fmts); + + /* + * The format available on the source pad is the one applied on the sink + * pad. + */ + if (code->pad == ADV748X_CSI2_SOURCE) { + struct v4l2_mbus_framefmt *fmt; + + if (code->index) + return -EINVAL; + + fmt = v4l2_subdev_state_get_format(sd_state, ADV748X_CSI2_SINK); + code->code = fmt->code; + + return 0; + } + + if (code->index >= num_fmts) + return -EINVAL; + + code->code = codes[code->index]; + + return 0; +} + static struct v4l2_mbus_framefmt * adv748x_csi2_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, @@ -228,6 +272,7 @@ static int adv748x_csi2_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad } static const struct v4l2_subdev_pad_ops adv748x_csi2_pad_ops = { + .enum_mbus_code = adv748x_csi2_enum_mbus_code, .get_fmt = adv748x_csi2_get_format, .set_fmt = adv748x_csi2_set_format, .get_mbus_config = adv748x_csi2_get_mbus_config, -- cgit v1.2.3 From 990ef913e48f186a0b3c6bdf3dbcf779582ac7b5 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 17 Jun 2024 18:11:29 +0200 Subject: media: adv748x-afe: Use 1X16 media bus code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the adv748x CSI-2 transmitter driver validates the supported formats, it is required for subdevices along the pipeline to produce and consume the same media bus codes. The adv748x analog front end driver use the 2X8 variant of the UYVY8 media bus code, while the CSI-2 transmitter use the 1X16 variant, which is the correct one to use for the serial bus. Make the adv748x afe use the 1X16 format variant to maintain the pipeline validation correct. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Tested-by: Niklas Söderlund Link: https://lore.kernel.org/r/20240617161135.130719-7-jacopo.mondi@ideasonboard.com Signed-off-by: Laurent Pinchart --- drivers/media/i2c/adv748x/adv748x-afe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c index 50d9fbadbe38..5edb3295dc58 100644 --- a/drivers/media/i2c/adv748x/adv748x-afe.c +++ b/drivers/media/i2c/adv748x/adv748x-afe.c @@ -114,7 +114,7 @@ static void adv748x_afe_fill_format(struct adv748x_afe *afe, { memset(fmt, 0, sizeof(*fmt)); - fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; + fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; fmt->field = V4L2_FIELD_ALTERNATE; @@ -337,7 +337,7 @@ static int adv748x_afe_enum_mbus_code(struct v4l2_subdev *sd, if (code->index != 0) return -EINVAL; - code->code = MEDIA_BUS_FMT_UYVY8_2X8; + code->code = MEDIA_BUS_FMT_UYVY8_1X16; return 0; } -- cgit v1.2.3 From 61893eccf6f2f097ed66e162253a5038fd42e43f Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 17 Jun 2024 18:11:30 +0200 Subject: media: adv748x-csi2: Validate the image format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The adv748x-csi2 driver configures the CSI-2 transmitter to automatically infer the image stream format from the connected frontend (HDMI or AFE). Setting a new format on the subdevice hence does not actually control the CSI-2 output format, but it's only there for the purpose of pipeline validation. However, there is currently no validation that the supplied media bus code is valid and supported by the device. With the introduction of enum_mbus_codes a list of supported format is now available, use it to validate that the supplied format is correct and use the default UYVY8 one if that's not the case. Signed-off-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Tested-by: Niklas Söderlund Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20240617161135.130719-8-jacopo.mondi@ideasonboard.com Signed-off-by: Laurent Pinchart --- drivers/media/i2c/adv748x/adv748x-csi2.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index 29b18b6c8b0e..0cdb397d9e0a 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -218,6 +218,22 @@ static int adv748x_csi2_get_format(struct v4l2_subdev *sd, return 0; } +static bool adv748x_csi2_is_fmt_supported(struct adv748x_csi2 *tx, u32 code) +{ + const unsigned int *codes = is_txa(tx) ? + adv748x_csi2_txa_fmts : + adv748x_csi2_txb_fmts; + size_t num_fmts = is_txa(tx) ? ARRAY_SIZE(adv748x_csi2_txa_fmts) + : ARRAY_SIZE(adv748x_csi2_txb_fmts); + + for (unsigned int i = 0; i < num_fmts; i++) { + if (codes[i] == code) + return true; + } + + return false; +} + static int adv748x_csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *sdformat) @@ -227,6 +243,13 @@ static int adv748x_csi2_set_format(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mbusformat; int ret = 0; + /* + * Make sure the format is supported, if not default it to + * UYVY8 as it's supported by both TXes. + */ + if (!adv748x_csi2_is_fmt_supported(tx, sdformat->format.code)) + sdformat->format.code = MEDIA_BUS_FMT_UYVY8_1X16; + mbusformat = adv748x_csi2_get_pad_format(sd, sd_state, sdformat->pad, sdformat->which); if (!mbusformat) -- cgit v1.2.3 From 3164347e24688062f68edd43c18e1f7b38cbd357 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 17 Jun 2024 18:11:31 +0200 Subject: media: adv748x-csi2: Use the subdev active state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initialize and use the subdev active state to store the subdevice format. This simplifies the implementation of the get_fmt and set_fmt pad operations. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Tested-by: Niklas Söderlund Link: https://lore.kernel.org/r/20240617161135.130719-9-jacopo.mondi@ideasonboard.com Signed-off-by: Laurent Pinchart --- drivers/media/i2c/adv748x/adv748x-csi2.c | 107 ++++++++++++------------------- drivers/media/i2c/adv748x/adv748x.h | 1 - 2 files changed, 42 insertions(+), 66 deletions(-) diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index 0cdb397d9e0a..ebe7da8ebed7 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -6,7 +6,6 @@ */ #include -#include #include #include @@ -68,7 +67,33 @@ static int adv748x_csi2_register_link(struct adv748x_csi2 *tx, /* ----------------------------------------------------------------------------- * v4l2_subdev_internal_ops - * + */ + +static int adv748x_csi2_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + static const struct v4l2_mbus_framefmt adv748x_csi2_default_fmt = { + .width = 1280, + .height = 720, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, + .quantization = V4L2_QUANTIZATION_DEFAULT, + .xfer_func = V4L2_XFER_FUNC_DEFAULT, + }; + struct v4l2_mbus_framefmt *fmt; + + fmt = v4l2_subdev_state_get_format(state, ADV748X_CSI2_SINK); + *fmt = adv748x_csi2_default_fmt; + + fmt = v4l2_subdev_state_get_format(state, ADV748X_CSI2_SOURCE); + *fmt = adv748x_csi2_default_fmt; + + return 0; +} + +/* * We use the internal registered operation to be able to ensure that our * incremental subdevices (not connected in the forward path) can be registered * against the resulting video path and media device. @@ -118,6 +143,7 @@ static int adv748x_csi2_registered(struct v4l2_subdev *sd) } static const struct v4l2_subdev_internal_ops adv748x_csi2_internal_ops = { + .init_state = adv748x_csi2_init_state, .registered = adv748x_csi2_registered, }; @@ -183,41 +209,6 @@ static int adv748x_csi2_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static struct v4l2_mbus_framefmt * -adv748x_csi2_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - unsigned int pad, u32 which) -{ - struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); - - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_state_get_format(sd_state, pad); - - return &tx->format; -} - -static int adv748x_csi2_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *sdformat) -{ - struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); - struct adv748x_state *state = tx->state; - struct v4l2_mbus_framefmt *mbusformat; - - mbusformat = adv748x_csi2_get_pad_format(sd, sd_state, sdformat->pad, - sdformat->which); - if (!mbusformat) - return -EINVAL; - - mutex_lock(&state->mutex); - - sdformat->format = *mbusformat; - - mutex_unlock(&state->mutex); - - return 0; -} - static bool adv748x_csi2_is_fmt_supported(struct adv748x_csi2 *tx, u32 code) { const unsigned int *codes = is_txa(tx) ? @@ -239,9 +230,10 @@ static int adv748x_csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_format *sdformat) { struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); - struct adv748x_state *state = tx->state; struct v4l2_mbus_framefmt *mbusformat; - int ret = 0; + + if (sdformat->pad == ADV748X_CSI2_SOURCE) + return v4l2_subdev_get_fmt(sd, sd_state, sdformat); /* * Make sure the format is supported, if not default it to @@ -250,34 +242,14 @@ static int adv748x_csi2_set_format(struct v4l2_subdev *sd, if (!adv748x_csi2_is_fmt_supported(tx, sdformat->format.code)) sdformat->format.code = MEDIA_BUS_FMT_UYVY8_1X16; - mbusformat = adv748x_csi2_get_pad_format(sd, sd_state, sdformat->pad, - sdformat->which); - if (!mbusformat) - return -EINVAL; - - mutex_lock(&state->mutex); - - if (sdformat->pad == ADV748X_CSI2_SOURCE) { - const struct v4l2_mbus_framefmt *sink_fmt; - - sink_fmt = adv748x_csi2_get_pad_format(sd, sd_state, - ADV748X_CSI2_SINK, - sdformat->which); - - if (!sink_fmt) { - ret = -EINVAL; - goto unlock; - } - - sdformat->format = *sink_fmt; - } - + mbusformat = v4l2_subdev_state_get_format(sd_state, sdformat->pad); *mbusformat = sdformat->format; -unlock: - mutex_unlock(&state->mutex); + /* Propagate format to the source pad. */ + mbusformat = v4l2_subdev_state_get_format(sd_state, ADV748X_CSI2_SOURCE); + *mbusformat = sdformat->format; - return ret; + return 0; } static int adv748x_csi2_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, @@ -296,7 +268,7 @@ static int adv748x_csi2_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad static const struct v4l2_subdev_pad_ops adv748x_csi2_pad_ops = { .enum_mbus_code = adv748x_csi2_enum_mbus_code, - .get_fmt = adv748x_csi2_get_format, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = adv748x_csi2_set_format, .get_mbus_config = adv748x_csi2_get_mbus_config, }; @@ -388,6 +360,11 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx) if (ret) goto err_cleanup_subdev; + tx->sd.state_lock = &state->mutex; + ret = v4l2_subdev_init_finalize(&tx->sd); + if (ret) + goto err_free_ctrl; + ret = v4l2_async_register_subdev(&tx->sd); if (ret) goto err_free_ctrl; diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index d2b5e722e997..9bc0121d0eff 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -75,7 +75,6 @@ enum adv748x_csi2_pads { struct adv748x_csi2 { struct adv748x_state *state; - struct v4l2_mbus_framefmt format; unsigned int page; unsigned int port; unsigned int num_lanes; -- cgit v1.2.3 From b0023db63c467442bc7dec8e3a299eb10ae97dec Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 17 Jun 2024 18:11:32 +0200 Subject: media: max9286: Fix enum_mbus_code The max9286 driver supports multiple output formats but only a single one is reported through the .enum_mbus_code operation. Fix that. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20240617161135.130719-10-jacopo.mondi@ideasonboard.com Signed-off-by: Laurent Pinchart --- drivers/media/i2c/max9286.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index dfcb3fc03350..46ce8e51c011 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -914,10 +914,10 @@ static int max9286_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { - if (code->pad || code->index > 0) + if (code->pad || code->index >= ARRAY_SIZE(max9286_formats)) return -EINVAL; - code->code = MEDIA_BUS_FMT_UYVY8_1X16; + code->code = max9286_formats[code->index].code; return 0; } -- cgit v1.2.3 From 33fb59ca95bedcacf8ec19b6a63102883bcc4ad5 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 17 Jun 2024 18:11:33 +0200 Subject: media: max9286: Use the subdev active state Use the subdev active state in the max9286 driver to store the image format. Replace the .open() function call with the .init_state() one and simplify the set/get_pad_fmt() operations. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20240617161135.130719-11-jacopo.mondi@ideasonboard.com Signed-off-by: Laurent Pinchart --- drivers/media/i2c/max9286.c | 129 +++++++++++++++----------------------------- 1 file changed, 44 insertions(+), 85 deletions(-) diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index 46ce8e51c011..e247512dfec4 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -198,12 +197,8 @@ struct max9286_priv { struct v4l2_ctrl *pixelrate_ctrl; unsigned int pixelrate; - struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS]; struct v4l2_fract interval; - /* Protects controls and fmt structures */ - struct mutex mutex; - unsigned int nsources; unsigned int source_mask; unsigned int route_mask; @@ -788,19 +783,22 @@ static void max9286_v4l2_notifier_unregister(struct max9286_priv *priv) static int max9286_s_stream(struct v4l2_subdev *sd, int enable) { struct max9286_priv *priv = sd_to_max9286(sd); + struct v4l2_subdev_state *state; struct max9286_source *source; unsigned int i; bool sync = false; - int ret; + int ret = 0; + + state = v4l2_subdev_lock_and_get_active_state(sd); if (enable) { const struct v4l2_mbus_framefmt *format; /* - * Get the format from the first used sink pad, as all sink - * formats must be identical. + * Get the format from the source pad, as all formats must be + * identical. */ - format = &priv->fmt[__ffs(priv->bound_sources)]; + format = v4l2_subdev_state_get_format(state, MAX9286_SRC_PAD); max9286_set_video_format(priv, format); max9286_set_fsync_period(priv); @@ -816,12 +814,12 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable) for_each_source(priv, source) { ret = v4l2_subdev_call(source->sd, video, s_stream, 1); if (ret) - return ret; + goto unlock; } ret = max9286_check_video_links(priv); if (ret) - return ret; + goto unlock; /* * Wait until frame synchronization is locked. @@ -842,7 +840,8 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable) if (!sync) { dev_err(&priv->client->dev, "Failed to get frame synchronization\n"); - return -EXDEV; /* Invalid cross-device link */ + ret = -EXDEV; /* Invalid cross-device link */ + goto unlock; } /* @@ -865,7 +864,10 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable) max9286_i2c_mux_close(priv); } - return 0; +unlock: + v4l2_subdev_unlock_state(state); + + return ret; } static int max9286_get_frame_interval(struct v4l2_subdev *sd, @@ -922,31 +924,20 @@ static int max9286_enum_mbus_code(struct v4l2_subdev *sd, return 0; } -static struct v4l2_mbus_framefmt * -max9286_get_pad_format(struct max9286_priv *priv, - struct v4l2_subdev_state *sd_state, - unsigned int pad, u32 which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_state_get_format(sd_state, pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &priv->fmt[pad]; - default: - return NULL; - } -} - static int max9286_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_state *state, struct v4l2_subdev_format *format) { struct max9286_priv *priv = sd_to_max9286(sd); - struct v4l2_mbus_framefmt *cfg_fmt; + struct max9286_source *source; unsigned int i; + /* + * Disable setting format on the source pad: format is propagated + * from the sinks. + */ if (format->pad == MAX9286_SRC_PAD) - return -EINVAL; + return v4l2_subdev_get_fmt(sd, state, format); /* Validate the format. */ for (i = 0; i < ARRAY_SIZE(max9286_formats); ++i) { @@ -957,42 +948,17 @@ static int max9286_set_fmt(struct v4l2_subdev *sd, if (i == ARRAY_SIZE(max9286_formats)) format->format.code = max9286_formats[0].code; - cfg_fmt = max9286_get_pad_format(priv, sd_state, format->pad, - format->which); - if (!cfg_fmt) - return -EINVAL; - - mutex_lock(&priv->mutex); - *cfg_fmt = format->format; - mutex_unlock(&priv->mutex); - - return 0; -} - -static int max9286_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct max9286_priv *priv = sd_to_max9286(sd); - struct v4l2_mbus_framefmt *cfg_fmt; - unsigned int pad = format->pad; - /* - * Multiplexed Stream Support: Support link validation by returning the - * format of the first bound link. All links must have the same format, - * as we do not support mixing and matching of cameras connected to the - * max9286. + * Apply the same format on all the other pad as all links must have the + * same format. */ - if (pad == MAX9286_SRC_PAD) - pad = __ffs(priv->bound_sources); + for_each_source(priv, source) { + unsigned int index = to_index(priv, source); - cfg_fmt = max9286_get_pad_format(priv, sd_state, pad, format->which); - if (!cfg_fmt) - return -EINVAL; + *v4l2_subdev_state_get_format(state, index) = format->format; + } - mutex_lock(&priv->mutex); - format->format = *cfg_fmt; - mutex_unlock(&priv->mutex); + *v4l2_subdev_state_get_format(state, MAX9286_SRC_PAD) = format->format; return 0; } @@ -1003,7 +969,7 @@ static const struct v4l2_subdev_video_ops max9286_video_ops = { static const struct v4l2_subdev_pad_ops max9286_pad_ops = { .enum_mbus_code = max9286_enum_mbus_code, - .get_fmt = max9286_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = max9286_set_fmt, .get_frame_interval = max9286_get_frame_interval, .set_frame_interval = max9286_set_frame_interval, @@ -1025,26 +991,17 @@ static const struct v4l2_mbus_framefmt max9286_default_format = { .xfer_func = V4L2_XFER_FUNC_DEFAULT, }; -static void max9286_init_format(struct v4l2_mbus_framefmt *fmt) -{ - *fmt = max9286_default_format; -} - -static int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +static int max9286_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) { - struct v4l2_mbus_framefmt *format; - unsigned int i; - - for (i = 0; i < MAX9286_N_SINKS; i++) { - format = v4l2_subdev_state_get_format(fh->state, i); - max9286_init_format(format); - } + for (unsigned int i = 0; i < MAX9286_N_PADS; i++) + *v4l2_subdev_state_get_format(state, i) = max9286_default_format; return 0; } static const struct v4l2_subdev_internal_ops max9286_subdev_internal_ops = { - .open = max9286_open, + .init_state = max9286_init_state, }; static const struct media_entity_operations max9286_media_ops = { @@ -1079,10 +1036,6 @@ static int max9286_v4l2_register(struct max9286_priv *priv) } /* Configure V4L2 for the MAX9286 itself */ - - for (i = 0; i < MAX9286_N_SINKS; i++) - max9286_init_format(&priv->fmt[i]); - v4l2_i2c_subdev_init(&priv->sd, priv->client, &max9286_subdev_ops); priv->sd.internal_ops = &max9286_subdev_internal_ops; priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -1109,14 +1062,21 @@ static int max9286_v4l2_register(struct max9286_priv *priv) if (ret) goto err_async; + priv->sd.state_lock = priv->ctrls.lock; + ret = v4l2_subdev_init_finalize(&priv->sd); + if (ret) + goto err_async; + ret = v4l2_async_register_subdev(&priv->sd); if (ret < 0) { dev_err(dev, "Unable to register subdevice\n"); - goto err_async; + goto err_subdev; } return 0; +err_subdev: + v4l2_subdev_cleanup(&priv->sd); err_async: v4l2_ctrl_handler_free(&priv->ctrls); max9286_v4l2_notifier_unregister(priv); @@ -1126,6 +1086,7 @@ err_async: static void max9286_v4l2_unregister(struct max9286_priv *priv) { + v4l2_subdev_cleanup(&priv->sd); v4l2_ctrl_handler_free(&priv->ctrls); v4l2_async_unregister_subdev(&priv->sd); max9286_v4l2_notifier_unregister(priv); @@ -1629,8 +1590,6 @@ static int max9286_probe(struct i2c_client *client) if (!priv) return -ENOMEM; - mutex_init(&priv->mutex); - priv->client = client; /* GPIO values default to high */ -- cgit v1.2.3 From 65f3594366eb303680eae97b935fa0fc90cb7ccc Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Mon, 17 Jun 2024 18:11:34 +0200 Subject: media: max9286: Use frame interval from subdev state Use the frame interval stored in the subdev state instead of storing a copy in the driver private structure. Initialize the frame interval to the special case 0/0 that in the max9286 driver represents automatic handling of frame sync. As the frame sync mode is set at s_stream() time, we can remove it from max9286_setup(). Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20240617161135.130719-12-jacopo.mondi@ideasonboard.com Signed-off-by: Laurent Pinchart --- drivers/media/i2c/max9286.c | 52 +++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index e247512dfec4..9fc4e130a273 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -197,8 +197,6 @@ struct max9286_priv { struct v4l2_ctrl *pixelrate_ctrl; unsigned int pixelrate; - struct v4l2_fract interval; - unsigned int nsources; unsigned int source_mask; unsigned int route_mask; @@ -571,11 +569,14 @@ static void max9286_set_video_format(struct max9286_priv *priv, MAX9286_INVVS | MAX9286_HVSRC_D14); } -static void max9286_set_fsync_period(struct max9286_priv *priv) +static void max9286_set_fsync_period(struct max9286_priv *priv, + struct v4l2_subdev_state *state) { + const struct v4l2_fract *interval; u32 fsync; - if (!priv->interval.numerator || !priv->interval.denominator) { + interval = v4l2_subdev_state_get_interval(state, MAX9286_SRC_PAD); + if (!interval->numerator || !interval->denominator) { /* * Special case, a null interval enables automatic FRAMESYNC * mode. FRAMESYNC is taken from the slowest link. @@ -591,8 +592,8 @@ static void max9286_set_fsync_period(struct max9286_priv *priv) * The FRAMESYNC generator is configured with a period expressed as a * number of PCLK periods. */ - fsync = div_u64((u64)priv->pixelrate * priv->interval.numerator, - priv->interval.denominator); + fsync = div_u64((u64)priv->pixelrate * interval->numerator, + interval->denominator); dev_dbg(&priv->client->dev, "fsync period %u (pclk %u)\n", fsync, priv->pixelrate); @@ -801,7 +802,7 @@ static int max9286_s_stream(struct v4l2_subdev *sd, int enable) format = v4l2_subdev_state_get_format(state, MAX9286_SRC_PAD); max9286_set_video_format(priv, format); - max9286_set_fsync_period(priv); + max9286_set_fsync_period(priv, state); /* * The frame sync between cameras is transmitted across the @@ -874,19 +875,11 @@ static int max9286_get_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval *interval) { - struct max9286_priv *priv = sd_to_max9286(sd); - - /* - * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 - * subdev active state API. - */ - if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - if (interval->pad != MAX9286_SRC_PAD) return -EINVAL; - interval->interval = priv->interval; + interval->interval = *v4l2_subdev_state_get_interval(sd_state, + interval->pad); return 0; } @@ -895,19 +888,11 @@ static int max9286_set_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_interval *interval) { - struct max9286_priv *priv = sd_to_max9286(sd); - - /* - * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 - * subdev active state API. - */ - if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - if (interval->pad != MAX9286_SRC_PAD) return -EINVAL; - priv->interval = interval->interval; + *v4l2_subdev_state_get_interval(sd_state, + interval->pad) = interval->interval; return 0; } @@ -994,9 +979,21 @@ static const struct v4l2_mbus_framefmt max9286_default_format = { static int max9286_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *state) { + struct v4l2_fract *interval; + for (unsigned int i = 0; i < MAX9286_N_PADS; i++) *v4l2_subdev_state_get_format(state, i) = max9286_default_format; + /* + * Special case: a null interval enables automatic FRAMESYNC mode. + * + * FRAMESYNC is taken from the slowest link. See register 0x01 + * configuration. + */ + interval = v4l2_subdev_state_get_interval(state, MAX9286_SRC_PAD); + interval->numerator = 0; + interval->denominator = 0; + return 0; } @@ -1143,7 +1140,6 @@ static int max9286_setup(struct max9286_priv *priv) max9286_write(priv, 0x69, (0xf & ~priv->route_mask)); max9286_set_video_format(priv, &max9286_default_format); - max9286_set_fsync_period(priv); cfg = max9286_read(priv, 0x1c); if (cfg < 0) -- cgit v1.2.3 From 57edbbcf5258c378a9b9d0c80d33b03a010b22c8 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 5 May 2024 20:22:27 +0300 Subject: media: renesas: vsp1: Fix _irqsave and _irq mix The histogram support mixes _irqsave and _irq, causing the following smatch warning: drivers/media/platform/renesas/vsp1/vsp1_histo.c:153 histo_stop_streaming() warn: mixing irqsave and irq The histo_stop_streaming() calls spin_lock_irqsave() followed by wait_event_lock_irq(). The former hints that interrupts may be disabled by the caller, while the latter reenables interrupts unconditionally. This doesn't cause any real bug, as the function is always called with interrupts enabled, but the pattern is still incorrect. Fix the problem by using spin_lock_irq() instead of spin_lock_irqsave() in histo_stop_streaming(). While at it, switch to spin_lock_irq() and spin_lock() as appropriate elsewhere. Fixes: 99362e32332b ("[media] v4l: vsp1: Add histogram support") Reported-by: Dan Carpenter Closes: https://lore.kernel.org/linux-renesas-soc/164d74ff-312c-468f-be64-afa7182cd2f4@moroto.mountain/ Reviewed-by: Kieran Bingham Signed-off-by: Laurent Pinchart --- drivers/media/platform/renesas/vsp1/vsp1_histo.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c index 71155282ca11..cd1c8778662e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_histo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c @@ -36,9 +36,8 @@ struct vsp1_histogram_buffer * vsp1_histogram_buffer_get(struct vsp1_histogram *histo) { struct vsp1_histogram_buffer *buf = NULL; - unsigned long flags; - spin_lock_irqsave(&histo->irqlock, flags); + spin_lock(&histo->irqlock); if (list_empty(&histo->irqqueue)) goto done; @@ -49,7 +48,7 @@ vsp1_histogram_buffer_get(struct vsp1_histogram *histo) histo->readout = true; done: - spin_unlock_irqrestore(&histo->irqlock, flags); + spin_unlock(&histo->irqlock); return buf; } @@ -58,7 +57,6 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo, size_t size) { struct vsp1_pipeline *pipe = histo->entity.pipe; - unsigned long flags; /* * The pipeline pointer is guaranteed to be valid as this function is @@ -70,10 +68,10 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo, vb2_set_plane_payload(&buf->buf.vb2_buf, 0, size); vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); - spin_lock_irqsave(&histo->irqlock, flags); + spin_lock(&histo->irqlock); histo->readout = false; wake_up(&histo->wait_queue); - spin_unlock_irqrestore(&histo->irqlock, flags); + spin_unlock(&histo->irqlock); } /* ----------------------------------------------------------------------------- @@ -124,11 +122,10 @@ static void histo_buffer_queue(struct vb2_buffer *vb) struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vsp1_histogram *histo = vb2_get_drv_priv(vb->vb2_queue); struct vsp1_histogram_buffer *buf = to_vsp1_histogram_buffer(vbuf); - unsigned long flags; - spin_lock_irqsave(&histo->irqlock, flags); + spin_lock_irq(&histo->irqlock); list_add_tail(&buf->queue, &histo->irqqueue); - spin_unlock_irqrestore(&histo->irqlock, flags); + spin_unlock_irq(&histo->irqlock); } static int histo_start_streaming(struct vb2_queue *vq, unsigned int count) @@ -140,9 +137,8 @@ static void histo_stop_streaming(struct vb2_queue *vq) { struct vsp1_histogram *histo = vb2_get_drv_priv(vq); struct vsp1_histogram_buffer *buffer; - unsigned long flags; - spin_lock_irqsave(&histo->irqlock, flags); + spin_lock_irq(&histo->irqlock); /* Remove all buffers from the IRQ queue. */ list_for_each_entry(buffer, &histo->irqqueue, queue) @@ -152,7 +148,7 @@ static void histo_stop_streaming(struct vb2_queue *vq) /* Wait for the buffer being read out (if any) to complete. */ wait_event_lock_irq(histo->wait_queue, !histo->readout, histo->irqlock); - spin_unlock_irqrestore(&histo->irqlock, flags); + spin_unlock_irq(&histo->irqlock); } static const struct vb2_ops histo_video_queue_qops = { -- cgit v1.2.3 From 3f98113e1d51ab410b49e2698ef31cc06abfedc5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 5 May 2024 20:19:45 +0300 Subject: media: videobuf2: core: Drop unneeded forward declaration The static __enqueue_in_driver() function is forward-declared needlessly, as it isn't called before its definition. Drop the forward declaration. Signed-off-by: Laurent Pinchart Reviewed-by: Tomasz Figa --- drivers/media/common/videobuf2/videobuf2-core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 358f1fe42975..0217392fcc0d 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -199,7 +199,6 @@ module_param(debug, int, 0644); }) static void __vb2_queue_cancel(struct vb2_queue *q); -static void __enqueue_in_driver(struct vb2_buffer *vb); static const char *vb2_state_name(enum vb2_buffer_state s) { -- cgit v1.2.3 From 18a8f4c28884b7fb5fd3ba759c777cca693788ef Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 13 Nov 2023 11:35:23 +0200 Subject: media: v4l2-subdev: Drop unreacheable warning The v4l2_subdev_link_validate_get_format() function warns if the pad given as argument belongs to a non-subdev entity. This can't happen, as the function is called from v4l2_subdev_link_validate() only (indirectly through v4l2_subdev_link_validate_locked()), and that function ensures that both ends of the link are subdevs. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen --- drivers/media/v4l2-core/v4l2-subdev.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 4f71199bf592..7c5812d55315 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -1269,14 +1269,6 @@ v4l2_subdev_link_validate_get_format(struct media_pad *pad, u32 stream, struct v4l2_subdev *sd; int ret; - if (!is_media_entity_v4l2_subdev(pad->entity)) { - WARN(pad->entity->function != MEDIA_ENT_F_IO_V4L, - "Driver bug! Wrong media entity type 0x%08x, entity %s\n", - pad->entity->function, pad->entity->name); - - return -EINVAL; - } - sd = media_entity_to_v4l2_subdev(pad->entity); fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; -- cgit v1.2.3 From dce863203d259d019693b5d082b0a4c4b5063cd1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 18 Jun 2024 21:46:44 +0300 Subject: media: renesas: vsp1: Print debug message to diagnose validation failure When formats don't match between a subdev and a connected video device, starting streaming returns an error without giving the user any indication as to what went wrong. To help debugging pipeline misconfigurations, add a debug message that indicates the cause of the failure. Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham --- drivers/media/platform/renesas/vsp1/vsp1_video.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index 5a9cb0e5640e..28cee0304e77 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -78,8 +78,14 @@ static int vsp1_video_verify_format(struct vsp1_video *video) if (video->rwpf->fmtinfo->mbus != fmt.format.code || video->rwpf->format.height != fmt.format.height || - video->rwpf->format.width != fmt.format.width) - return -EINVAL; + video->rwpf->format.width != fmt.format.width) { + dev_dbg(video->vsp1->dev, + "Format mismatch: 0x%04x/%ux%u != 0x%04x/%ux%u\n", + video->rwpf->fmtinfo->mbus, video->rwpf->format.width, + video->rwpf->format.height, fmt.format.code, + fmt.format.width, fmt.format.height); + return -EPIPE; + } return 0; } -- cgit v1.2.3 From 0aaf7db087267734da8b0353533ff67b1e4080df Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 12 Nov 2023 04:14:05 +0200 Subject: media: renesas: vsp1: Drop vsp1_entity_get_pad_format() wrapper The vsp1_entity_get_pad_format() function is just a wrapper around v4l2_subdev_state_get_format() without any added value. Drop it and call v4l2_subdev_state_get_format() directly. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- drivers/media/platform/renesas/vsp1/vsp1_brx.c | 18 ++++++------ drivers/media/platform/renesas/vsp1/vsp1_clu.c | 3 +- drivers/media/platform/renesas/vsp1/vsp1_entity.c | 27 ++++------------- drivers/media/platform/renesas/vsp1/vsp1_entity.h | 4 --- drivers/media/platform/renesas/vsp1/vsp1_histo.c | 6 ++-- drivers/media/platform/renesas/vsp1/vsp1_hsit.c | 5 ++-- drivers/media/platform/renesas/vsp1/vsp1_lif.c | 4 +-- drivers/media/platform/renesas/vsp1/vsp1_rpf.c | 10 +++---- drivers/media/platform/renesas/vsp1/vsp1_rwpf.c | 14 ++++----- drivers/media/platform/renesas/vsp1/vsp1_sru.c | 31 +++++++++----------- drivers/media/platform/renesas/vsp1/vsp1_uds.c | 35 ++++++++++------------- drivers/media/platform/renesas/vsp1/vsp1_uif.c | 5 ++-- drivers/media/platform/renesas/vsp1/vsp1_video.c | 10 +++---- drivers/media/platform/renesas/vsp1/vsp1_wpf.c | 25 +++++++--------- 14 files changed, 73 insertions(+), 124 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index a8535c6e2c46..0eb4d8fe4285 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -119,8 +119,8 @@ static void brx_try_format(struct vsp1_brx *brx, default: /* The BRx can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&brx->entity, sd_state, - BRX_PAD_SINK(0)); + format = v4l2_subdev_state_get_format(sd_state, + BRX_PAD_SINK(0)); fmt->code = format->code; break; } @@ -150,7 +150,7 @@ static int brx_set_format(struct v4l2_subdev *subdev, brx_try_format(brx, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&brx->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); *format = fmt->format; /* Reset the compose rectangle. */ @@ -169,8 +169,7 @@ static int brx_set_format(struct v4l2_subdev *subdev, unsigned int i; for (i = 0; i <= brx->entity.source_pad; ++i) { - format = vsp1_entity_get_pad_format(&brx->entity, - state, i); + format = v4l2_subdev_state_get_format(state, i); format->code = fmt->format.code; } } @@ -242,8 +241,7 @@ static int brx_set_selection(struct v4l2_subdev *subdev, * The compose rectangle top left corner must be inside the output * frame. */ - format = vsp1_entity_get_pad_format(&brx->entity, state, - brx->entity.source_pad); + format = v4l2_subdev_state_get_format(state, brx->entity.source_pad); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); @@ -251,7 +249,7 @@ static int brx_set_selection(struct v4l2_subdev *subdev, * Scaling isn't supported, the compose rectangle size must be identical * to the sink format size. */ - format = vsp1_entity_get_pad_format(&brx->entity, state, sel->pad); + format = v4l2_subdev_state_get_format(state, sel->pad); sel->r.width = format->width; sel->r.height = format->height; @@ -290,8 +288,8 @@ static void brx_configure_stream(struct vsp1_entity *entity, unsigned int flags; unsigned int i; - format = vsp1_entity_get_pad_format(&brx->entity, brx->entity.state, - brx->entity.source_pad); + format = v4l2_subdev_state_get_format(brx->entity.state, + brx->entity.source_pad); /* * The hardware is extremely flexible but we have no userspace API to diff --git a/drivers/media/platform/renesas/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c index 625776a9bda4..1e57676a420c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_clu.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c @@ -181,8 +181,7 @@ static void clu_configure_stream(struct vsp1_entity *entity, * The yuv_mode can't be changed during streaming. Cache it internally * for future runtime configuration calls. */ - format = vsp1_entity_get_pad_format(&clu->entity, clu->entity.state, - CLU_PAD_SINK); + format = v4l2_subdev_state_get_format(clu->entity.state, CLU_PAD_SINK); clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index 0a5a7f9cc870..fa748cf89d44 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -127,23 +127,6 @@ vsp1_entity_get_state(struct vsp1_entity *entity, } } -/** - * vsp1_entity_get_pad_format - Get a pad format from storage for an entity - * @entity: the entity - * @sd_state: the state storage - * @pad: the pad number - * - * Return the format stored in the given configuration for an entity's pad. The - * configuration can be an ACTIVE or TRY configuration. - */ -struct v4l2_mbus_framefmt * -vsp1_entity_get_pad_format(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - unsigned int pad) -{ - return v4l2_subdev_state_get_format(sd_state, pad); -} - /** * vsp1_entity_get_pad_selection - Get a pad selection from storage for entity * @entity: the entity @@ -191,7 +174,7 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, return -EINVAL; mutex_lock(&entity->lock); - fmt->format = *vsp1_entity_get_pad_format(entity, state, fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(state, fmt->pad); mutex_unlock(&entity->lock); return 0; @@ -238,7 +221,7 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, return -EINVAL; mutex_lock(&entity->lock); - format = vsp1_entity_get_pad_format(entity, state, 0); + format = v4l2_subdev_state_get_format(state, 0); code->code = format->code; mutex_unlock(&entity->lock); } @@ -276,7 +259,7 @@ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(entity, state, fse->pad); + format = v4l2_subdev_state_get_format(state, fse->pad); mutex_lock(&entity->lock); @@ -346,7 +329,7 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, goto done; } - format = vsp1_entity_get_pad_format(entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); if (fmt->pad == entity->source_pad) { /* The output format can't be modified. */ @@ -374,7 +357,7 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(entity, state, entity->source_pad); + format = v4l2_subdev_state_get_format(state, entity->source_pad); *format = fmt->format; /* Reset the crop and compose rectangles. */ diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h index 735f32dde4b5..e913befe7fc8 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h @@ -138,10 +138,6 @@ struct v4l2_subdev_state * vsp1_entity_get_state(struct vsp1_entity *entity, struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which); -struct v4l2_mbus_framefmt * -vsp1_entity_get_pad_format(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - unsigned int pad); struct v4l2_rect * vsp1_entity_get_pad_selection(struct vsp1_entity *entity, struct v4l2_subdev_state *sd_state, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c index cd1c8778662e..0d7e4afae0f8 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_histo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c @@ -229,8 +229,7 @@ static int histo_get_selection(struct v4l2_subdev *subdev, case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: - format = vsp1_entity_get_pad_format(&histo->entity, state, - HISTO_PAD_SINK); + format = v4l2_subdev_state_get_format(state, HISTO_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = format->width; @@ -262,8 +261,7 @@ static int histo_set_crop(struct v4l2_subdev *subdev, struct v4l2_rect *selection; /* The crop rectangle must be inside the input frame. */ - format = vsp1_entity_get_pad_format(&histo->entity, sd_state, - HISTO_PAD_SINK); + format = v4l2_subdev_state_get_format(sd_state, HISTO_PAD_SINK); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); sel->r.width = clamp_t(unsigned int, sel->r.width, HISTO_MIN_SIZE, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c index bc1299c29ac9..4a8cce808c93 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c @@ -78,7 +78,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, goto done; } - format = vsp1_entity_get_pad_format(&hsit->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); if (fmt->pad == HSIT_PAD_SOURCE) { /* @@ -101,8 +101,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&hsit->entity, state, - HSIT_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, HSIT_PAD_SOURCE); *format = fmt->format; format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32 : MEDIA_BUS_FMT_AHSV8888_1X32; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lif.c b/drivers/media/platform/renesas/vsp1/vsp1_lif.c index b1d21a54837b..29d4c1521e6a 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lif.c @@ -93,8 +93,8 @@ static void lif_configure_stream(struct vsp1_entity *entity, unsigned int obth; unsigned int lbth; - format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.state, - LIF_PAD_SOURCE); + format = v4l2_subdev_state_get_format(lif->entity.state, + LIF_PAD_SOURCE); switch (entity->vsp1->version & VI6_IP_VERSION_MODEL_MASK) { case VI6_IP_VERSION_MODEL_VSPD_GEN2: diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c index c47579efc65f..19d9f078748c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c @@ -80,12 +80,10 @@ static void rpf_configure_stream(struct vsp1_entity *entity, vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_PSTRIDE, pstride); /* Format */ - sink_format = vsp1_entity_get_pad_format(&rpf->entity, - rpf->entity.state, - RWPF_PAD_SINK); - source_format = vsp1_entity_get_pad_format(&rpf->entity, - rpf->entity.state, - RWPF_PAD_SOURCE); + sink_format = v4l2_subdev_state_get_format(rpf->entity.state, + RWPF_PAD_SINK); + source_format = v4l2_subdev_state_get_format(rpf->entity.state, + RWPF_PAD_SOURCE); infmt = VI6_RPF_INFMT_CIPM | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c index 09fb6ffa14e2..574623a48a3d 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c @@ -79,7 +79,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - format = vsp1_entity_get_pad_format(&rwpf->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); if (fmt->pad == RWPF_PAD_SOURCE) { /* @@ -113,8 +113,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, } /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, state, - RWPF_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); *format = fmt->format; if (rwpf->flip.rotate) { @@ -157,8 +156,7 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, break; case V4L2_SEL_TGT_CROP_BOUNDS: - format = vsp1_entity_get_pad_format(&rwpf->entity, state, - RWPF_PAD_SINK); + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = format->width; @@ -204,8 +202,7 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, } /* Make sure the crop rectangle is entirely contained in the image. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, state, - RWPF_PAD_SINK); + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); /* * Restrict the crop rectangle coordinates to multiples of 2 to avoid @@ -229,8 +226,7 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, *crop = sel->r; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, state, - RWPF_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); format->width = crop->width; format->height = crop->height; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c index 11e008aa9f20..749ba7705000 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c @@ -131,7 +131,7 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(&sru->entity, state, SRU_PAD_SINK); + format = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); mutex_lock(&sru->entity.lock); @@ -184,8 +184,7 @@ static void sru_try_format(struct vsp1_sru *sru, case SRU_PAD_SOURCE: /* The SRU can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&sru->entity, sd_state, - SRU_PAD_SINK); + format = v4l2_subdev_state_get_format(sd_state, SRU_PAD_SINK); fmt->code = format->code; /* @@ -234,13 +233,12 @@ static int sru_set_format(struct v4l2_subdev *subdev, sru_try_format(sru, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&sru->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); *format = fmt->format; if (fmt->pad == SRU_PAD_SINK) { /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&sru->entity, state, - SRU_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); *format = fmt->format; sru_try_format(sru, state, SRU_PAD_SOURCE, format); @@ -277,10 +275,9 @@ static void sru_configure_stream(struct vsp1_entity *entity, struct v4l2_mbus_framefmt *output; u32 ctrl0; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SOURCE); + input = v4l2_subdev_state_get_format(sru->entity.state, SRU_PAD_SINK); + output = v4l2_subdev_state_get_format(sru->entity.state, + SRU_PAD_SOURCE); if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32) ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3 @@ -307,10 +304,9 @@ static unsigned int sru_max_width(struct vsp1_entity *entity, struct v4l2_mbus_framefmt *input; struct v4l2_mbus_framefmt *output; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SOURCE); + input = v4l2_subdev_state_get_format(sru->entity.state, SRU_PAD_SINK); + output = v4l2_subdev_state_get_format(sru->entity.state, + SRU_PAD_SOURCE); /* * The maximum input width of the SRU is 288 input pixels, but 32 @@ -333,10 +329,9 @@ static void sru_partition(struct vsp1_entity *entity, struct v4l2_mbus_framefmt *input; struct v4l2_mbus_framefmt *output; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SOURCE); + input = v4l2_subdev_state_get_format(sru->entity.state, SRU_PAD_SINK); + output = v4l2_subdev_state_get_format(sru->entity.state, + SRU_PAD_SOURCE); /* Adapt if SRUx2 is enabled. */ if (input->width != output->width) { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c index d89f1197b86c..887b1f70611a 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c @@ -136,7 +136,7 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(&uds->entity, state, UDS_PAD_SINK); + format = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); mutex_lock(&uds->entity.lock); @@ -183,8 +183,7 @@ static void uds_try_format(struct vsp1_uds *uds, case UDS_PAD_SOURCE: /* The UDS scales but can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&uds->entity, sd_state, - UDS_PAD_SINK); + format = v4l2_subdev_state_get_format(sd_state, UDS_PAD_SINK); fmt->code = format->code; uds_output_limits(format->width, &minimum, &maximum); @@ -217,13 +216,12 @@ static int uds_set_format(struct v4l2_subdev *subdev, uds_try_format(uds, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&uds->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); *format = fmt->format; if (fmt->pad == UDS_PAD_SINK) { /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&uds->entity, state, - UDS_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE); *format = fmt->format; uds_try_format(uds, state, UDS_PAD_SOURCE, format); @@ -265,10 +263,9 @@ static void uds_configure_stream(struct vsp1_entity *entity, unsigned int vscale; bool multitap; - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SOURCE); + input = v4l2_subdev_state_get_format(uds->entity.state, UDS_PAD_SINK); + output = v4l2_subdev_state_get_format(uds->entity.state, + UDS_PAD_SOURCE); hscale = uds_compute_ratio(input->width, output->width); vscale = uds_compute_ratio(input->height, output->height); @@ -310,8 +307,8 @@ static void uds_configure_partition(struct vsp1_entity *entity, struct vsp1_partition *partition = pipe->partition; const struct v4l2_mbus_framefmt *output; - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SOURCE); + output = v4l2_subdev_state_get_format(uds->entity.state, + UDS_PAD_SOURCE); /* Input size clipping. */ vsp1_uds_write(uds, dlb, VI6_UDS_HSZCLIP, VI6_UDS_HSZCLIP_HCEN | @@ -335,10 +332,9 @@ static unsigned int uds_max_width(struct vsp1_entity *entity, const struct v4l2_mbus_framefmt *input; unsigned int hscale; - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SOURCE); + input = v4l2_subdev_state_get_format(uds->entity.state, UDS_PAD_SINK); + output = v4l2_subdev_state_get_format(uds->entity.state, + UDS_PAD_SOURCE); hscale = output->width / input->width; /* @@ -377,10 +373,9 @@ static void uds_partition(struct vsp1_entity *entity, partition->uds_sink = *window; partition->uds_source = *window; - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SOURCE); + input = v4l2_subdev_state_get_format(uds->entity.state, UDS_PAD_SINK); + output = v4l2_subdev_state_get_format(uds->entity.state, + UDS_PAD_SOURCE); partition->uds_sink.width = window->width * input->width / output->width; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c index f66936a28a2a..ee5b6ba22898 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c @@ -104,8 +104,7 @@ static int uif_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: - format = vsp1_entity_get_pad_format(&uif->entity, state, - UIF_PAD_SINK); + format = v4l2_subdev_state_get_format(state, UIF_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = format->width; @@ -150,7 +149,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev, } /* The crop rectangle must be inside the input frame. */ - format = vsp1_entity_get_pad_format(&uif->entity, state, UIF_PAD_SINK); + format = v4l2_subdev_state_get_format(state, UIF_PAD_SINK); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index 28cee0304e77..4801fd0088d8 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -203,9 +203,8 @@ static void vsp1_video_calculate_partition(struct vsp1_pipeline *pipe, * Partitions are computed on the size before rotation, use the format * at the WPF sink. */ - format = vsp1_entity_get_pad_format(&pipe->output->entity, - pipe->output->entity.state, - RWPF_PAD_SINK); + format = v4l2_subdev_state_get_format(pipe->output->entity.state, + RWPF_PAD_SINK); /* A single partition simply processes the output size in full. */ if (pipe->partitions <= 1) { @@ -268,9 +267,8 @@ static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) * Partitions are computed on the size before rotation, use the format * at the WPF sink. */ - format = vsp1_entity_get_pad_format(&pipe->output->entity, - pipe->output->entity.state, - RWPF_PAD_SINK); + format = v4l2_subdev_state_get_format(pipe->output->entity.state, + RWPF_PAD_SINK); div_size = format->width; /* diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index 9693aeab1cac..5129181b8217 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -65,12 +65,10 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation) goto done; } - sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SINK); - source_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SOURCE); + sink_format = v4l2_subdev_state_get_format(wpf->entity.state, + RWPF_PAD_SINK); + source_format = v4l2_subdev_state_get_format(wpf->entity.state, + RWPF_PAD_SOURCE); mutex_lock(&wpf->entity.lock); @@ -245,12 +243,10 @@ static void wpf_configure_stream(struct vsp1_entity *entity, u32 srcrpf = 0; int ret; - sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SINK); - source_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SOURCE); + sink_format = v4l2_subdev_state_get_format(wpf->entity.state, + RWPF_PAD_SINK); + source_format = v4l2_subdev_state_get_format(wpf->entity.state, + RWPF_PAD_SOURCE); /* Format */ if (!pipe->lif || wpf->writeback) { @@ -383,9 +379,8 @@ static void wpf_configure_partition(struct vsp1_entity *entity, unsigned int flip; unsigned int i; - sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SINK); + sink_format = v4l2_subdev_state_get_format(wpf->entity.state, + RWPF_PAD_SINK); width = sink_format->width; height = sink_format->height; left = 0; -- cgit v1.2.3 From 769d5fe4eb8e8fc5aac421151c645329086df114 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 12 Nov 2023 04:14:05 +0200 Subject: media: renesas: vsp1: Drop vsp1_entity_get_pad_selection() wrapper The vsp1_entity_get_pad_selection() function is just a wrapper around v4l2_subdev_state_get_crop() or v4l2_subdev_state_get_compose() without any added value. Drop it and call the functions it wraps directly. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- drivers/media/platform/renesas/vsp1/vsp1_entity.c | 32 ++--------------------- drivers/media/platform/renesas/vsp1/vsp1_entity.h | 4 --- drivers/media/platform/renesas/vsp1/vsp1_hgo.c | 7 ++--- drivers/media/platform/renesas/vsp1/vsp1_hgt.c | 7 ++--- drivers/media/platform/renesas/vsp1/vsp1_histo.c | 31 +++++++--------------- drivers/media/platform/renesas/vsp1/vsp1_rpf.c | 6 ++--- drivers/media/platform/renesas/vsp1/vsp1_uif.c | 9 +++---- 7 files changed, 20 insertions(+), 76 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index fa748cf89d44..8d39f1ee00ab 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -127,32 +127,6 @@ vsp1_entity_get_state(struct vsp1_entity *entity, } } -/** - * vsp1_entity_get_pad_selection - Get a pad selection from storage for entity - * @entity: the entity - * @sd_state: the state storage - * @pad: the pad number - * @target: the selection target - * - * Return the selection rectangle stored in the given configuration for an - * entity's pad. The configuration can be an ACTIVE or TRY configuration. The - * selection target can be COMPOSE or CROP. - */ -struct v4l2_rect * -vsp1_entity_get_pad_selection(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - unsigned int pad, unsigned int target) -{ - switch (target) { - case V4L2_SEL_TGT_COMPOSE: - return v4l2_subdev_state_get_compose(sd_state, pad); - case V4L2_SEL_TGT_CROP: - return v4l2_subdev_state_get_crop(sd_state, pad); - default: - return NULL; - } -} - /* * vsp1_subdev_get_pad_format - Subdev pad get_fmt handler * @subdev: V4L2 subdevice @@ -361,15 +335,13 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, *format = fmt->format; /* Reset the crop and compose rectangles. */ - selection = vsp1_entity_get_pad_selection(entity, state, fmt->pad, - V4L2_SEL_TGT_CROP); + selection = v4l2_subdev_state_get_crop(state, fmt->pad); selection->left = 0; selection->top = 0; selection->width = format->width; selection->height = format->height; - selection = vsp1_entity_get_pad_selection(entity, state, fmt->pad, - V4L2_SEL_TGT_COMPOSE); + selection = v4l2_subdev_state_get_compose(state, fmt->pad); selection->left = 0; selection->top = 0; selection->width = format->width; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h index e913befe7fc8..802c0c2acab0 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h @@ -138,10 +138,6 @@ struct v4l2_subdev_state * vsp1_entity_get_state(struct vsp1_entity *entity, struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which); -struct v4l2_rect * -vsp1_entity_get_pad_selection(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - unsigned int pad, unsigned int target); void vsp1_entity_route_setup(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c index 40c571a987ef..4ee5f0e5e9c3 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c @@ -140,11 +140,8 @@ static void hgo_configure_stream(struct vsp1_entity *entity, unsigned int hratio; unsigned int vratio; - crop = vsp1_entity_get_pad_selection(entity, entity->state, - HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); - compose = vsp1_entity_get_pad_selection(entity, entity->state, - HISTO_PAD_SINK, - V4L2_SEL_TGT_COMPOSE); + crop = v4l2_subdev_state_get_crop(entity->state, HISTO_PAD_SINK); + compose = v4l2_subdev_state_get_compose(entity->state, HISTO_PAD_SINK); vsp1_hgo_write(hgo, dlb, VI6_HGO_REGRST, VI6_HGO_REGRST_RCLEA); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c index 8281b86874ab..b739d8045576 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c @@ -139,11 +139,8 @@ static void hgt_configure_stream(struct vsp1_entity *entity, u8 upper; unsigned int i; - crop = vsp1_entity_get_pad_selection(entity, entity->state, - HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); - compose = vsp1_entity_get_pad_selection(entity, entity->state, - HISTO_PAD_SINK, - V4L2_SEL_TGT_COMPOSE); + crop = v4l2_subdev_state_get_crop(entity->state, HISTO_PAD_SINK); + compose = v4l2_subdev_state_get_compose(entity->state, HISTO_PAD_SINK); vsp1_hgt_write(hgt, dlb, VI6_HGT_REGRST, VI6_HGT_REGRST_RCLEA); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c index 0d7e4afae0f8..85d2fc538327 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_histo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c @@ -218,9 +218,7 @@ static int histo_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_COMPOSE_BOUNDS: case V4L2_SEL_TGT_COMPOSE_DEFAULT: - crop = vsp1_entity_get_pad_selection(&histo->entity, state, - HISTO_PAD_SINK, - V4L2_SEL_TGT_CROP); + crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = crop->width; @@ -237,9 +235,11 @@ static int histo_get_selection(struct v4l2_subdev *subdev, break; case V4L2_SEL_TGT_COMPOSE: + sel->r = *v4l2_subdev_state_get_compose(state, sel->pad); + break; + case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_entity_get_pad_selection(&histo->entity, state, - sel->pad, sel->target); + sel->r = *v4l2_subdev_state_get_crop(state, sel->pad); break; default: @@ -256,9 +256,7 @@ static int histo_set_crop(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct vsp1_histogram *histo = subdev_to_histo(subdev); struct v4l2_mbus_framefmt *format; - struct v4l2_rect *selection; /* The crop rectangle must be inside the input frame. */ format = v4l2_subdev_state_get_format(sd_state, HISTO_PAD_SINK); @@ -270,14 +268,8 @@ static int histo_set_crop(struct v4l2_subdev *subdev, format->height - sel->r.top); /* Set the crop rectangle and reset the compose rectangle. */ - selection = vsp1_entity_get_pad_selection(&histo->entity, sd_state, - sel->pad, V4L2_SEL_TGT_CROP); - *selection = sel->r; - - selection = vsp1_entity_get_pad_selection(&histo->entity, sd_state, - sel->pad, - V4L2_SEL_TGT_COMPOSE); - *selection = sel->r; + *v4l2_subdev_state_get_crop(sd_state, sel->pad) = sel->r; + *v4l2_subdev_state_get_compose(sd_state, sel->pad) = sel->r; return 0; } @@ -286,7 +278,6 @@ static int histo_set_compose(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct vsp1_histogram *histo = subdev_to_histo(subdev); struct v4l2_rect *compose; struct v4l2_rect *crop; unsigned int ratio; @@ -299,9 +290,7 @@ static int histo_set_compose(struct v4l2_subdev *subdev, sel->r.left = 0; sel->r.top = 0; - crop = vsp1_entity_get_pad_selection(&histo->entity, sd_state, - sel->pad, - V4L2_SEL_TGT_CROP); + crop = v4l2_subdev_state_get_crop(sd_state, sel->pad); /* * Clamp the width and height to acceptable values first and then @@ -326,9 +315,7 @@ static int histo_set_compose(struct v4l2_subdev *subdev, ratio = 1 << (crop->height * 2 / sel->r.height / 3); sel->r.height = crop->height / ratio; - compose = vsp1_entity_get_pad_selection(&histo->entity, sd_state, - sel->pad, - V4L2_SEL_TGT_COMPOSE); + compose = v4l2_subdev_state_get_compose(sd_state, sel->pad); *compose = sel->r; return 0; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c index 19d9f078748c..4efcec5253d6 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c @@ -155,10 +155,8 @@ static void rpf_configure_stream(struct vsp1_entity *entity, if (pipe->brx) { const struct v4l2_rect *compose; - compose = vsp1_entity_get_pad_selection(pipe->brx, - pipe->brx->state, - rpf->brx_input, - V4L2_SEL_TGT_COMPOSE); + compose = v4l2_subdev_state_get_compose(pipe->brx->state, + rpf->brx_input); left = compose->left; top = compose->top; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c index ee5b6ba22898..cecd2f7024f4 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c @@ -112,8 +112,7 @@ static int uif_get_selection(struct v4l2_subdev *subdev, break; case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_entity_get_pad_selection(&uif->entity, state, - sel->pad, sel->target); + sel->r = *v4l2_subdev_state_get_crop(state, sel->pad); break; default: @@ -159,8 +158,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev, format->height - sel->r.top); /* Store the crop rectangle. */ - selection = vsp1_entity_get_pad_selection(&uif->entity, state, - sel->pad, V4L2_SEL_TGT_CROP); + selection = v4l2_subdev_state_get_crop(state, sel->pad); *selection = sel->r; done: @@ -202,8 +200,7 @@ static void uif_configure_stream(struct vsp1_entity *entity, vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMPMR, VI6_UIF_DISCOM_DOCMPMR_SEL(9)); - crop = vsp1_entity_get_pad_selection(entity, entity->state, - UIF_PAD_SINK, V4L2_SEL_TGT_CROP); + crop = v4l2_subdev_state_get_crop(entity->state, UIF_PAD_SINK); left = crop->left; width = crop->width; -- cgit v1.2.3 From 70884bb3f7c20ecb6c9ee1c9c4cfcc26d1967d61 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 12 Nov 2023 04:14:05 +0200 Subject: media: renesas: vsp1: Drop vsp1_rwpf_get_crop() wrapper The vsp1_rwpf_get_crop() function is just a wrapper around v4l2_subdev_state_get_crop() without any added value. Drop it and call v4l2_subdev_state_get_crop() directly. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- drivers/media/platform/renesas/vsp1/vsp1_rpf.c | 2 +- drivers/media/platform/renesas/vsp1/vsp1_rwpf.c | 12 +++--------- drivers/media/platform/renesas/vsp1/vsp1_rwpf.h | 3 --- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c index 4efcec5253d6..3e62638fdce6 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c @@ -298,7 +298,7 @@ static void rpf_configure_partition(struct vsp1_entity *entity, * offsets are needed, as planes 2 and 3 always have identical * strides. */ - crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.state); + crop = *v4l2_subdev_state_get_crop(rpf->entity.state, RWPF_PAD_SINK); /* * Partition Algorithm Control diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c index 574623a48a3d..9d38203e73d0 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c @@ -16,12 +16,6 @@ #define RWPF_MIN_WIDTH 1 #define RWPF_MIN_HEIGHT 1 -struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, - struct v4l2_subdev_state *sd_state) -{ - return v4l2_subdev_state_get_crop(sd_state, RWPF_PAD_SINK); -} - /* ----------------------------------------------------------------------------- * V4L2 Subdevice Operations */ @@ -105,7 +99,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_rect *crop; /* Update the sink crop rectangle. */ - crop = vsp1_rwpf_get_crop(rwpf, state); + crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; @@ -152,7 +146,7 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_rwpf_get_crop(rwpf, state); + sel->r = *v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); break; case V4L2_SEL_TGT_CROP_BOUNDS: @@ -222,7 +216,7 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, sel->r.height = min_t(unsigned int, sel->r.height, format->height - sel->r.top); - crop = vsp1_rwpf_get_crop(rwpf, state); + crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); *crop = sel->r; /* Propagate the format to the source pad. */ diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h index e0d212c70b2f..5ac9f0a6fafc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h @@ -85,7 +85,4 @@ int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols); extern const struct v4l2_subdev_ops vsp1_rwpf_subdev_ops; -struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, - struct v4l2_subdev_state *sd_state); - #endif /* __VSP1_RWPF_H__ */ -- cgit v1.2.3 From bbd53f422d51399d838be90de81d1ea752f0b89a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 12 Nov 2023 04:14:05 +0200 Subject: media: renesas: vsp1: Drop brx_get_compose() wrapper The brx_get_compose() function is just a wrapper around v4l2_subdev_state_get_compose() without any added value. Drop it and call v4l2_subdev_state_get_compose() directly. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- drivers/media/platform/renesas/vsp1/vsp1_brx.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index 0eb4d8fe4285..05940d0427bf 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -96,13 +96,6 @@ static int brx_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static struct v4l2_rect *brx_get_compose(struct vsp1_brx *brx, - struct v4l2_subdev_state *sd_state, - unsigned int pad) -{ - return v4l2_subdev_state_get_compose(sd_state, pad); -} - static void brx_try_format(struct vsp1_brx *brx, struct v4l2_subdev_state *sd_state, unsigned int pad, struct v4l2_mbus_framefmt *fmt) @@ -157,7 +150,7 @@ static int brx_set_format(struct v4l2_subdev *subdev, if (fmt->pad != brx->entity.source_pad) { struct v4l2_rect *compose; - compose = brx_get_compose(brx, state, fmt->pad); + compose = v4l2_subdev_state_get_compose(state, fmt->pad); compose->left = 0; compose->top = 0; compose->width = format->width; @@ -204,7 +197,7 @@ static int brx_get_selection(struct v4l2_subdev *subdev, return -EINVAL; mutex_lock(&brx->entity.lock); - sel->r = *brx_get_compose(brx, state, sel->pad); + sel->r = *v4l2_subdev_state_get_compose(state, sel->pad); mutex_unlock(&brx->entity.lock); return 0; @@ -253,7 +246,7 @@ static int brx_set_selection(struct v4l2_subdev *subdev, sel->r.width = format->width; sel->r.height = format->height; - compose = brx_get_compose(brx, state, sel->pad); + compose = v4l2_subdev_state_get_compose(state, sel->pad); *compose = sel->r; done: -- cgit v1.2.3 From 177bfb680342637715a6a54ba6216e4d3f761d5a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 12 Nov 2023 04:14:05 +0200 Subject: media: renesas: vsp1: Drop custom .get_fmt() handler for histogram The histogram module is the only one that has a custom .get_fmt() handler, to handle the special case of the output format being fixed. This can equally well be handled in the .set_fmt() handler instead. Beside avoiding special cases and using the same .get_fmt() handler in all modules, it ensures that the correct format is stored in the active state for the source pad, including when .set_fmt() is called from vsp1_entity_init_state(). Both are needed to later switch to the V4L2 subdev active state API. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- drivers/media/platform/renesas/vsp1/vsp1_histo.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c index 85d2fc538327..9c2d4c91bfad 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_histo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c @@ -352,31 +352,22 @@ done: return ret; } -static int histo_get_format(struct v4l2_subdev *subdev, +static int histo_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { + struct vsp1_histogram *histo = subdev_to_histo(subdev); + if (fmt->pad == HISTO_PAD_SOURCE) { fmt->format.code = MEDIA_BUS_FMT_FIXED; fmt->format.width = 0; fmt->format.height = 0; fmt->format.field = V4L2_FIELD_NONE; fmt->format.colorspace = V4L2_COLORSPACE_RAW; + return 0; } - return vsp1_subdev_get_pad_format(subdev, sd_state, fmt); -} - -static int histo_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct vsp1_histogram *histo = subdev_to_histo(subdev); - - if (fmt->pad != HISTO_PAD_SINK) - return histo_get_format(subdev, sd_state, fmt); - return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, histo->formats, histo->num_formats, HISTO_MIN_SIZE, HISTO_MIN_SIZE, @@ -386,7 +377,7 @@ static int histo_set_format(struct v4l2_subdev *subdev, static const struct v4l2_subdev_pad_ops histo_pad_ops = { .enum_mbus_code = histo_enum_mbus_code, .enum_frame_size = histo_enum_frame_size, - .get_fmt = histo_get_format, + .get_fmt = vsp1_subdev_get_pad_format, .set_fmt = histo_set_format, .get_selection = histo_get_selection, .set_selection = histo_set_selection, -- cgit v1.2.3 From e575095d28b331179c737cb08a95cde5ab647557 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 16 Nov 2023 03:00:41 +0200 Subject: media: renesas: vsp1: Move partition calculation to vsp1_pipe.c The partition calculation code, located in vsp1_video.c, is not specific to video pipelines. To prepare for its usage in DRM pipelines, move it to vsp1_pipe.c. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- drivers/media/platform/renesas/vsp1/vsp1_pipe.c | 85 +++++++++++- drivers/media/platform/renesas/vsp1/vsp1_pipe.h | 6 +- drivers/media/platform/renesas/vsp1/vsp1_video.c | 169 ++++++----------------- 3 files changed, 130 insertions(+), 130 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index 68d05243c3ee..fc66dcfdb838 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -444,6 +444,10 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, vsp1_uds_set_alpha(pipe->uds, dlb, alpha); } +/* ----------------------------------------------------------------------------- + * VSP1 Partition Algorithm support + */ + /* * Propagate the partition calculations through the pipeline * @@ -452,10 +456,10 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, * source. Each entity must produce the partition required for the previous * entity in the pipeline. */ -void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, - struct vsp1_partition *partition, - unsigned int index, - struct vsp1_partition_window *window) +static void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, + struct vsp1_partition *partition, + unsigned int index, + struct vsp1_partition_window *window) { struct vsp1_entity *entity; @@ -466,3 +470,76 @@ void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, } } +/* + * vsp1_pipeline_calculate_partition - Calculate pipeline configuration for a + * partition + * + * @pipe: the pipeline + * @partition: partition that will hold the calculated values + * @div_size: pre-determined maximum partition division size + * @index: partition index + */ +void vsp1_pipeline_calculate_partition(struct vsp1_pipeline *pipe, + struct vsp1_partition *partition, + unsigned int div_size, + unsigned int index) +{ + const struct v4l2_mbus_framefmt *format; + struct vsp1_partition_window window; + unsigned int modulus; + + /* + * Partitions are computed on the size before rotation, use the format + * at the WPF sink. + */ + format = v4l2_subdev_state_get_format(pipe->output->entity.state, + RWPF_PAD_SINK); + + /* A single partition simply processes the output size in full. */ + if (pipe->partitions <= 1) { + window.left = 0; + window.width = format->width; + + vsp1_pipeline_propagate_partition(pipe, partition, index, + &window); + return; + } + + /* Initialise the partition with sane starting conditions. */ + window.left = index * div_size; + window.width = div_size; + + modulus = format->width % div_size; + + /* + * We need to prevent the last partition from being smaller than the + * *minimum* width of the hardware capabilities. + * + * If the modulus is less than half of the partition size, + * the penultimate partition is reduced to half, which is added + * to the final partition: |1234|1234|1234|12|341| + * to prevent this: |1234|1234|1234|1234|1|. + */ + if (modulus) { + /* + * pipe->partitions is 1 based, whilst index is a 0 based index. + * Normalise this locally. + */ + unsigned int partitions = pipe->partitions - 1; + + if (modulus < div_size / 2) { + if (index == partitions - 1) { + /* Halve the penultimate partition. */ + window.width = div_size / 2; + } else if (index == partitions) { + /* Increase the final partition. */ + window.width = (div_size / 2) + modulus; + window.left -= div_size / 2; + } + } else if (index == partitions) { + window.width = modulus; + } + } + + vsp1_pipeline_propagate_partition(pipe, partition, index, &window); +} diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h index 674b5748d929..02e98d843730 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h @@ -166,10 +166,10 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, struct vsp1_dl_body *dlb, unsigned int alpha); -void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, +void vsp1_pipeline_calculate_partition(struct vsp1_pipeline *pipe, struct vsp1_partition *partition, - unsigned int index, - struct vsp1_partition_window *window); + unsigned int div_size, + unsigned int index); const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, u32 fourcc); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index 4801fd0088d8..b5181bd192e6 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -178,129 +178,6 @@ static int __vsp1_video_try_format(struct vsp1_video *video, return 0; } -/* ----------------------------------------------------------------------------- - * VSP1 Partition Algorithm support - */ - -/** - * vsp1_video_calculate_partition - Calculate the active partition output window - * - * @pipe: the pipeline - * @partition: partition that will hold the calculated values - * @div_size: pre-determined maximum partition division size - * @index: partition index - */ -static void vsp1_video_calculate_partition(struct vsp1_pipeline *pipe, - struct vsp1_partition *partition, - unsigned int div_size, - unsigned int index) -{ - const struct v4l2_mbus_framefmt *format; - struct vsp1_partition_window window; - unsigned int modulus; - - /* - * Partitions are computed on the size before rotation, use the format - * at the WPF sink. - */ - format = v4l2_subdev_state_get_format(pipe->output->entity.state, - RWPF_PAD_SINK); - - /* A single partition simply processes the output size in full. */ - if (pipe->partitions <= 1) { - window.left = 0; - window.width = format->width; - - vsp1_pipeline_propagate_partition(pipe, partition, index, - &window); - return; - } - - /* Initialise the partition with sane starting conditions. */ - window.left = index * div_size; - window.width = div_size; - - modulus = format->width % div_size; - - /* - * We need to prevent the last partition from being smaller than the - * *minimum* width of the hardware capabilities. - * - * If the modulus is less than half of the partition size, - * the penultimate partition is reduced to half, which is added - * to the final partition: |1234|1234|1234|12|341| - * to prevent this: |1234|1234|1234|1234|1|. - */ - if (modulus) { - /* - * pipe->partitions is 1 based, whilst index is a 0 based index. - * Normalise this locally. - */ - unsigned int partitions = pipe->partitions - 1; - - if (modulus < div_size / 2) { - if (index == partitions - 1) { - /* Halve the penultimate partition. */ - window.width = div_size / 2; - } else if (index == partitions) { - /* Increase the final partition. */ - window.width = (div_size / 2) + modulus; - window.left -= div_size / 2; - } - } else if (index == partitions) { - window.width = modulus; - } - } - - vsp1_pipeline_propagate_partition(pipe, partition, index, &window); -} - -static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) -{ - struct vsp1_device *vsp1 = pipe->output->entity.vsp1; - const struct v4l2_mbus_framefmt *format; - struct vsp1_entity *entity; - unsigned int div_size; - unsigned int i; - - /* - * Partitions are computed on the size before rotation, use the format - * at the WPF sink. - */ - format = v4l2_subdev_state_get_format(pipe->output->entity.state, - RWPF_PAD_SINK); - div_size = format->width; - - /* - * Only Gen3+ hardware requires image partitioning, Gen2 will operate - * with a single partition that covers the whole output. - */ - if (vsp1->info->gen >= 3) { - list_for_each_entry(entity, &pipe->entities, list_pipe) { - unsigned int entity_max; - - if (!entity->ops->max_width) - continue; - - entity_max = entity->ops->max_width(entity, pipe); - if (entity_max) - div_size = min(div_size, entity_max); - } - } - - pipe->partitions = DIV_ROUND_UP(format->width, div_size); - pipe->part_table = kcalloc(pipe->partitions, sizeof(*pipe->part_table), - GFP_KERNEL); - if (!pipe->part_table) - return -ENOMEM; - - for (i = 0; i < pipe->partitions; ++i) - vsp1_video_calculate_partition(pipe, &pipe->part_table[i], - div_size, i); - - return 0; -} - /* ----------------------------------------------------------------------------- * Pipeline Management */ @@ -788,6 +665,52 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&pipe->irqlock, flags); } +static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) +{ + struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + const struct v4l2_mbus_framefmt *format; + struct vsp1_entity *entity; + unsigned int div_size; + unsigned int i; + + /* + * Partitions are computed on the size before rotation, use the format + * at the WPF sink. + */ + format = v4l2_subdev_state_get_format(pipe->output->entity.state, + RWPF_PAD_SINK); + div_size = format->width; + + /* + * Only Gen3+ hardware requires image partitioning, Gen2 will operate + * with a single partition that covers the whole output. + */ + if (vsp1->info->gen >= 3) { + list_for_each_entry(entity, &pipe->entities, list_pipe) { + unsigned int entity_max; + + if (!entity->ops->max_width) + continue; + + entity_max = entity->ops->max_width(entity, pipe); + if (entity_max) + div_size = min(div_size, entity_max); + } + } + + pipe->partitions = DIV_ROUND_UP(format->width, div_size); + pipe->part_table = kcalloc(pipe->partitions, sizeof(*pipe->part_table), + GFP_KERNEL); + if (!pipe->part_table) + return -ENOMEM; + + for (i = 0; i < pipe->partitions; ++i) + vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[i], + div_size, i); + + return 0; +} + static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) { struct vsp1_entity *entity; -- cgit v1.2.3 From a143156c85b2118b972156386116e3bdc6564a19 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 16 Nov 2023 03:00:41 +0200 Subject: media: renesas: vsp1: Simplify partition calculation When calculation a partition in vsp1_pipeline_calculate_partition(), there is no need to handle the case where the whole image is covered by a single partition locally. In that case, the index and div_size parameters are 0 and format->width respectively, which makes the general code behave exactly as the special case. Drop the special case. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- drivers/media/platform/renesas/vsp1/vsp1_pipe.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index fc66dcfdb838..ac52ed8f65ba 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -495,16 +495,6 @@ void vsp1_pipeline_calculate_partition(struct vsp1_pipeline *pipe, format = v4l2_subdev_state_get_format(pipe->output->entity.state, RWPF_PAD_SINK); - /* A single partition simply processes the output size in full. */ - if (pipe->partitions <= 1) { - window.left = 0; - window.width = format->width; - - vsp1_pipeline_propagate_partition(pipe, partition, index, - &window); - return; - } - /* Initialise the partition with sane starting conditions. */ window.left = index * div_size; window.width = div_size; -- cgit v1.2.3 From a213bc09b1025c771ee722ee341af1d84375db8a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 19 Nov 2023 03:11:51 +0200 Subject: media: renesas: vsp1: Store RPF partition configuration per RPF instance The vsp1_partition structure stores the RPF partition configuration in a single field for all RPF instances, while each RPF can have its own configuration. Fix it by storing the configuration separately for each RPF instance. Signed-off-by: Laurent Pinchart Fixes: ab45e8585182 ("media: v4l: vsp1: Allow entities to participate in the partition algorithm") Reviewed-by: Jacopo Mondi --- drivers/media/platform/renesas/vsp1/vsp1_pipe.h | 2 +- drivers/media/platform/renesas/vsp1/vsp1_rpf.c | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h index 02e98d843730..840fd3288efb 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h @@ -73,7 +73,7 @@ struct vsp1_partition_window { * @wpf: The WPF partition window configuration */ struct vsp1_partition { - struct vsp1_partition_window rpf; + struct vsp1_partition_window rpf[VSP1_MAX_RPF]; struct vsp1_partition_window uds_sink; struct vsp1_partition_window uds_source; struct vsp1_partition_window sru; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c index 3e62638fdce6..42b0c5655404 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c @@ -311,8 +311,8 @@ static void rpf_configure_partition(struct vsp1_entity *entity, * 'width' need to be adjusted. */ if (pipe->partitions > 1) { - crop.width = pipe->partition->rpf.width; - crop.left += pipe->partition->rpf.left; + crop.width = pipe->partition->rpf[rpf->entity.index].width; + crop.left += pipe->partition->rpf[rpf->entity.index].left; } if (pipe->interlaced) { @@ -367,7 +367,9 @@ static void rpf_partition(struct vsp1_entity *entity, unsigned int partition_idx, struct vsp1_partition_window *window) { - partition->rpf = *window; + struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); + + partition->rpf[rpf->entity.index] = *window; } static const struct vsp1_entity_operations rpf_entity_ops = { -- cgit v1.2.3 From 2d7e5d80f120b7b075c037df463cca6d6a20fe8a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 15 Nov 2023 00:34:56 +0200 Subject: media: renesas: vsp1: Pass partition pointer to .configure_partition() The entity .configure_partition() function operates on a partition, and has to retrieve that partition from the pipeline's current partition field. Pass the partition pointer to the function to make it clearer what partition it operates on, and remove the vsp1_pipeline.partition field. This change clearly shows that the DRM pipeline doesn't use partitions, which makes entity implementation more complex and error-prone. This will be addressed in a further cleanup. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- drivers/media/platform/renesas/vsp1/vsp1_drm.c | 2 +- drivers/media/platform/renesas/vsp1/vsp1_entity.c | 4 +++- drivers/media/platform/renesas/vsp1/vsp1_entity.h | 2 ++ drivers/media/platform/renesas/vsp1/vsp1_pipe.h | 2 -- drivers/media/platform/renesas/vsp1/vsp1_rpf.c | 5 +++-- drivers/media/platform/renesas/vsp1/vsp1_uds.c | 2 +- drivers/media/platform/renesas/vsp1/vsp1_video.c | 5 ++--- drivers/media/platform/renesas/vsp1/vsp1_wpf.c | 5 +++-- 8 files changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index 9b087bd8df7d..3954c138fa7b 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -569,7 +569,7 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) vsp1_entity_route_setup(entity, pipe, dlb); vsp1_entity_configure_stream(entity, pipe, dl, dlb); vsp1_entity_configure_frame(entity, pipe, dl, dlb); - vsp1_entity_configure_partition(entity, pipe, dl, dlb); + vsp1_entity_configure_partition(entity, pipe, NULL, dl, dlb); } vsp1_dl_list_commit(dl, dl_flags); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index 8d39f1ee00ab..e9de75de8bde 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -89,11 +89,13 @@ void vsp1_entity_configure_frame(struct vsp1_entity *entity, void vsp1_entity_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { if (entity->ops->configure_partition) - entity->ops->configure_partition(entity, pipe, dl, dlb); + entity->ops->configure_partition(entity, pipe, partition, + dl, dlb); } /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h index 802c0c2acab0..7b86b2fef3e5 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h @@ -85,6 +85,7 @@ struct vsp1_entity_operations { struct vsp1_dl_list *, struct vsp1_dl_body *); void (*configure_partition)(struct vsp1_entity *, struct vsp1_pipeline *, + const struct vsp1_partition *, struct vsp1_dl_list *, struct vsp1_dl_body *); unsigned int (*max_width)(struct vsp1_entity *, struct vsp1_pipeline *); @@ -155,6 +156,7 @@ void vsp1_entity_configure_frame(struct vsp1_entity *entity, void vsp1_entity_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h index 840fd3288efb..3d2e35ac8fa0 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h @@ -106,7 +106,6 @@ struct vsp1_partition { * @configured: when false the @stream_config shall be written to the hardware * @interlaced: True when the pipeline is configured in interlaced mode * @partitions: The number of partitions used to process one frame - * @partition: The current partition for configuration to process * @part_table: The pre-calculated partitions used by the pipeline */ struct vsp1_pipeline { @@ -146,7 +145,6 @@ struct vsp1_pipeline { bool interlaced; unsigned int partitions; - struct vsp1_partition *partition; struct vsp1_partition *part_table; u32 underrun_count; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c index 42b0c5655404..3b8a62299226 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c @@ -280,6 +280,7 @@ static void rpf_configure_frame(struct vsp1_entity *entity, static void rpf_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { @@ -311,8 +312,8 @@ static void rpf_configure_partition(struct vsp1_entity *entity, * 'width' need to be adjusted. */ if (pipe->partitions > 1) { - crop.width = pipe->partition->rpf[rpf->entity.index].width; - crop.left += pipe->partition->rpf[rpf->entity.index].left; + crop.width = partition->rpf[rpf->entity.index].width; + crop.left += partition->rpf[rpf->entity.index].left; } if (pipe->interlaced) { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c index 887b1f70611a..737362ca2315 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c @@ -300,11 +300,11 @@ static void uds_configure_stream(struct vsp1_entity *entity, static void uds_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { struct vsp1_uds *uds = to_uds(&entity->subdev); - struct vsp1_partition *partition = pipe->partition; const struct v4l2_mbus_framefmt *output; output = v4l2_subdev_state_get_format(uds->entity.state, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index b5181bd192e6..d3222c38709b 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -246,13 +246,12 @@ static void vsp1_video_pipeline_run_partition(struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, unsigned int partition) { + struct vsp1_partition *part = &pipe->part_table[partition]; struct vsp1_dl_body *dlb = vsp1_dl_list_get_body0(dl); struct vsp1_entity *entity; - pipe->partition = &pipe->part_table[partition]; - list_for_each_entry(entity, &pipe->entities, list_pipe) - vsp1_entity_configure_partition(entity, pipe, dl, dlb); + vsp1_entity_configure_partition(entity, pipe, part, dl, dlb); } static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index 5129181b8217..80fe7571f4ff 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -363,6 +363,7 @@ static void wpf_configure_frame(struct vsp1_entity *entity, static void wpf_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { @@ -390,8 +391,8 @@ static void wpf_configure_partition(struct vsp1_entity *entity, * multiple slices. */ if (pipe->partitions > 1) { - width = pipe->partition->wpf.width; - left = pipe->partition->wpf.left; + width = partition->wpf.width; + left = partition->wpf.left; } vsp1_wpf_write(wpf, dlb, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | -- cgit v1.2.3 From 41be7fcc5d632402382a7c2a77ac84baf24d47cc Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 14 Nov 2023 00:53:07 +0200 Subject: media: renesas: vsp1: Replace vsp1_partition_window with v4l2_rect The vsp1_partition_window structure is used to store the horizontal size of a partition window. This is all that is currently needed, as all partitions span the whole image vertically. The horizontal window size is retrieved in the .configure_partition() handler from the vsp1_partition_window structure, and the vertical window size from the subdev state. Accessing the subdev state in the .configure_partition() handler is problematic in the context of moving to the V4L2 subdev active state API, as .configure_partition() is called in non-interruptable context, and the state lock can't be taken. To avoid this, start by storing the vertical size in the window, replacing the custom vsp1_partition_window structure with a v4l2_rect. Retrieving the vertical size from the window in .configure_partition() will be done in a subsequent change. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- drivers/media/platform/renesas/vsp1/vsp1_entity.h | 3 +-- drivers/media/platform/renesas/vsp1/vsp1_pipe.c | 6 ++++-- drivers/media/platform/renesas/vsp1/vsp1_pipe.h | 21 +++++---------------- drivers/media/platform/renesas/vsp1/vsp1_rpf.c | 2 +- drivers/media/platform/renesas/vsp1/vsp1_sru.c | 4 +++- drivers/media/platform/renesas/vsp1/vsp1_uds.c | 10 +++++----- drivers/media/platform/renesas/vsp1/vsp1_wpf.c | 2 +- 7 files changed, 20 insertions(+), 28 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h index 7b86b2fef3e5..f67f60677644 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h @@ -19,7 +19,6 @@ struct vsp1_dl_body; struct vsp1_dl_list; struct vsp1_pipeline; struct vsp1_partition; -struct vsp1_partition_window; enum vsp1_entity_type { VSP1_ENTITY_BRS, @@ -91,7 +90,7 @@ struct vsp1_entity_operations { unsigned int (*max_width)(struct vsp1_entity *, struct vsp1_pipeline *); void (*partition)(struct vsp1_entity *, struct vsp1_pipeline *, struct vsp1_partition *, unsigned int, - struct vsp1_partition_window *); + struct v4l2_rect *); }; struct vsp1_entity { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index ac52ed8f65ba..f7701c5ff492 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -459,7 +459,7 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, static void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int index, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { struct vsp1_entity *entity; @@ -485,7 +485,7 @@ void vsp1_pipeline_calculate_partition(struct vsp1_pipeline *pipe, unsigned int index) { const struct v4l2_mbus_framefmt *format; - struct vsp1_partition_window window; + struct v4l2_rect window; unsigned int modulus; /* @@ -498,6 +498,8 @@ void vsp1_pipeline_calculate_partition(struct vsp1_pipeline *pipe, /* Initialise the partition with sane starting conditions. */ window.left = index * div_size; window.width = div_size; + window.top = 0; + window.height = format->height; modulus = format->width % div_size; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h index 3d2e35ac8fa0..c1f411227de7 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h @@ -53,17 +53,6 @@ enum vsp1_pipeline_state { VSP1_PIPELINE_STOPPING, }; -/* - * struct vsp1_partition_window - Partition window coordinates - * @left: horizontal coordinate of the partition start in pixels relative to the - * left edge of the image - * @width: partition width in pixels - */ -struct vsp1_partition_window { - unsigned int left; - unsigned int width; -}; - /* * struct vsp1_partition - A description of a slice for the partition algorithm * @rpf: The RPF partition window configuration @@ -73,11 +62,11 @@ struct vsp1_partition_window { * @wpf: The WPF partition window configuration */ struct vsp1_partition { - struct vsp1_partition_window rpf[VSP1_MAX_RPF]; - struct vsp1_partition_window uds_sink; - struct vsp1_partition_window uds_source; - struct vsp1_partition_window sru; - struct vsp1_partition_window wpf; + struct v4l2_rect rpf[VSP1_MAX_RPF]; + struct v4l2_rect uds_sink; + struct v4l2_rect uds_source; + struct v4l2_rect sru; + struct v4l2_rect wpf; }; /* diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c index 3b8a62299226..862751616646 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c @@ -366,7 +366,7 @@ static void rpf_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c index 749ba7705000..8ce949de8d9a 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c @@ -323,7 +323,7 @@ static void sru_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { struct vsp1_sru *sru = to_sru(&entity->subdev); struct v4l2_mbus_framefmt *input; @@ -337,6 +337,8 @@ static void sru_partition(struct vsp1_entity *entity, if (input->width != output->width) { window->width /= 2; window->left /= 2; + window->height /= 2; + window->top /= 2; } partition->sru = *window; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c index 737362ca2315..4a14fd3baac1 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c @@ -363,16 +363,12 @@ static void uds_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { struct vsp1_uds *uds = to_uds(&entity->subdev); const struct v4l2_mbus_framefmt *output; const struct v4l2_mbus_framefmt *input; - /* Initialise the partition state. */ - partition->uds_sink = *window; - partition->uds_source = *window; - input = v4l2_subdev_state_get_format(uds->entity.state, UDS_PAD_SINK); output = v4l2_subdev_state_get_format(uds->entity.state, UDS_PAD_SOURCE); @@ -381,6 +377,10 @@ static void uds_partition(struct vsp1_entity *entity, / output->width; partition->uds_sink.left = window->left * input->width / output->width; + partition->uds_sink.height = input->height; + partition->uds_sink.top = 0; + + partition->uds_source = *window; *window = partition->uds_sink; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index 80fe7571f4ff..f8d1e2f47691 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -515,7 +515,7 @@ static void wpf_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { partition->wpf = *window; } -- cgit v1.2.3 From 032000264cbec9b17af2f76dd2899a7cc20ef422 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 19 Nov 2023 00:39:34 +0200 Subject: media: renesas: vsp1: Add and use function to dump a pipeline to the log It is useful for debugging purpose to dump a vsp1_pipeline to the kernel log. Add a new function to do so, and use it when initializing the video and DRM pipelines. As __vsp1_pipeline_dump() needs to construct the log message iteratively, it uses pr_cont(...) (exact equivalent to the more verbose "printk(KERN_CONT ..."). The function thus can't use dev_dbg() to log the initial part of the message, for two reasons: - pr_cont() doesn't seem to work with dev_*(). Even if the format string passed to dev_*() doesn't end with a '\n', pr_cont() starts a new line in the log. This behaviour doesn't seem to be clearly documented, and may or may not be on purpose. - Messages printed by dev_dbg() may be omitted if dynamic debugging is enabled. In that case, the continuation messages will still be printed, leading to confusing log messages. To still benefit from the dynamic debug infrastructure, we declare a vsp1_pipeline_dump() macro that uses _dynamic_func_call() when dynamic debugging is enabled. The whole vsp1_pipeline_dump() call can be selected at runtime. The __vsp1_pipeline_dump() function then uses a plain "printk(KERN_DEBUG ...)" to print the message header using the debug log level, and pr_cont() to print the rest of the message on the same line. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- drivers/media/platform/renesas/vsp1/vsp1_drm.c | 5 +++++ drivers/media/platform/renesas/vsp1/vsp1_pipe.c | 22 ++++++++++++++++++++++ drivers/media/platform/renesas/vsp1/vsp1_pipe.h | 19 +++++++++++++++++++ drivers/media/platform/renesas/vsp1/vsp1_video.c | 10 +++++++++- 4 files changed, 55 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index 3954c138fa7b..1aa59a74672f 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -733,6 +733,8 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, if (ret < 0) goto unlock; + vsp1_pipeline_dump(pipe, "LIF setup"); + /* Enable the VSP1. */ ret = vsp1_device_get(vsp1); if (ret < 0) @@ -906,6 +908,9 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index, } vsp1_du_pipeline_setup_inputs(vsp1, pipe); + + vsp1_pipeline_dump(pipe, "atomic update"); + vsp1_du_pipeline_configure(pipe); done: diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index f7701c5ff492..87cb62cf38fa 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -301,6 +301,28 @@ void vsp1_pipeline_init(struct vsp1_pipeline *pipe) pipe->state = VSP1_PIPELINE_STOPPED; } +void __vsp1_pipeline_dump(struct _ddebug *dbg, struct vsp1_pipeline *pipe, + const char *msg) +{ + struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + struct vsp1_entity *entity; + bool first = true; + + printk(KERN_DEBUG "%s: %s: pipe: ", dev_name(vsp1->dev), msg); + + list_for_each_entry(entity, &pipe->entities, list_pipe) { + const char *name; + + name = strchrnul(entity->subdev.name, ' '); + name = name ? name + 1 : entity->subdev.name; + + pr_cont("%s%s", first ? "" : ", ", name); + first = false; + } + + pr_cont("\n"); +} + /* Must be called with the pipe irqlock held. */ void vsp1_pipeline_run(struct vsp1_pipeline *pipe) { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h index c1f411227de7..1ba7bdbad5a8 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h @@ -9,6 +9,7 @@ #ifndef __VSP1_PIPE_H__ #define __VSP1_PIPE_H__ +#include #include #include #include @@ -142,6 +143,24 @@ struct vsp1_pipeline { void vsp1_pipeline_reset(struct vsp1_pipeline *pipe); void vsp1_pipeline_init(struct vsp1_pipeline *pipe); +void __vsp1_pipeline_dump(struct _ddebug *, struct vsp1_pipeline *pipe, + const char *msg); + +#if defined(CONFIG_DYNAMIC_DEBUG) || \ + (defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE)) +#define vsp1_pipeline_dump(pipe, msg) \ + _dynamic_func_call("vsp1_pipeline_dump()", __vsp1_pipeline_dump, pipe, msg) +#elif defined(DEBUG) +#define vsp1_pipeline_dump(pipe, msg) \ + __vsp1_pipeline_dump(NULL, pipe, msg) +#else +#define vsp1_pipeline_dump(pipe, msg) \ +({ \ + if (0) \ + __vsp1_pipeline_dump(NULL, pipe, msg); \ +}) +#endif + void vsp1_pipeline_run(struct vsp1_pipeline *pipe); bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe); int vsp1_pipeline_stop(struct vsp1_pipeline *pipe); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index d3222c38709b..64d3a69d65b2 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -526,11 +526,19 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe, static int vsp1_video_pipeline_init(struct vsp1_pipeline *pipe, struct vsp1_video *video) { + int ret; + vsp1_pipeline_init(pipe); pipe->frame_end = vsp1_video_pipeline_frame_end; - return vsp1_video_pipeline_build(pipe, video); + ret = vsp1_video_pipeline_build(pipe, video); + if (ret) + return ret; + + vsp1_pipeline_dump(pipe, "video"); + + return 0; } static struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video) -- cgit v1.2.3 From 51648e9605016b2b9d284e890b36bc27a71678dd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 19 Nov 2023 03:11:51 +0200 Subject: media: renesas: vsp1: Keep the DRM pipeline entities sorted Some of the code that handles pipeline configuration assumes that entities in a pipeline's entities list are sorted from sink to source. To prepare for using that code with the DRM pipeline, insert the BRx just before the WPF, and the RPFs at the head of the list. Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham Reviewed-by: Jacopo Mondi --- drivers/media/platform/renesas/vsp1/vsp1_drm.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index 1aa59a74672f..e44359b661b6 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -317,7 +317,10 @@ static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1, list_add_tail(&released_brx->list_pipe, &pipe->entities); - /* Add the BRx to the pipeline. */ + /* + * Add the BRx to the pipeline, inserting it just before the + * WPF. + */ dev_dbg(vsp1->dev, "%s: pipe %u: acquired %s\n", __func__, pipe->lif->index, BRX_NAME(brx)); @@ -326,7 +329,8 @@ static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1, pipe->brx->sink = &pipe->output->entity; pipe->brx->sink_pad = 0; - list_add_tail(&pipe->brx->list_pipe, &pipe->entities); + list_add_tail(&pipe->brx->list_pipe, + &pipe->output->entity.list_pipe); } /* @@ -420,7 +424,7 @@ static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1, if (!rpf->entity.pipe) { rpf->entity.pipe = pipe; - list_add_tail(&rpf->entity.list_pipe, &pipe->entities); + list_add(&rpf->entity.list_pipe, &pipe->entities); } brx->inputs[i].rpf = rpf; -- cgit v1.2.3 From 0656babf3c244b7760a6c005485d4b7b9be639e9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 19 Nov 2023 03:11:51 +0200 Subject: media: renesas: vsp1: Compute partitions for DRM pipelines The DRM pipelines don't partition frames, as the hardware operates synchronously with the display. The entity operations access configuration data from the entity state in that case, instead of accessing the partition structure. This requires special cases in entity-specific code, increasing the driver complexity. To prepare for simplifying the code, initialize a single partition for the DRM pipelines, similarly to how video pipelines create one partition spanning the full image when partitioning isn't needed. The partition is allocated statically in the vsp1_drm_pipeline structure instead of dynamically as for video pipelines, as DRM pipelines are guaranteed to operate on a single partition. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- drivers/media/platform/renesas/vsp1/vsp1_drm.c | 9 ++++++++- drivers/media/platform/renesas/vsp1/vsp1_drm.h | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index e44359b661b6..11313e26a298 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -550,6 +550,9 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) struct vsp1_dl_body *dlb; unsigned int dl_flags = 0; + vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[0], + drm_pipe->width, 0); + if (drm_pipe->force_brx_release) dl_flags |= VSP1_DL_FRAME_END_INTERNAL; if (pipe->output->writeback) @@ -573,7 +576,8 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) vsp1_entity_route_setup(entity, pipe, dlb); vsp1_entity_configure_stream(entity, pipe, dl, dlb); vsp1_entity_configure_frame(entity, pipe, dl, dlb); - vsp1_entity_configure_partition(entity, pipe, NULL, dl, dlb); + vsp1_entity_configure_partition(entity, pipe, + &pipe->part_table[0], dl, dlb); } vsp1_dl_list_commit(dl, dl_flags); @@ -968,6 +972,9 @@ int vsp1_drm_init(struct vsp1_device *vsp1) vsp1_pipeline_init(pipe); + pipe->partitions = 1; + pipe->part_table = &drm_pipe->partition; + pipe->frame_end = vsp1_du_pipeline_frame_end; /* diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.h b/drivers/media/platform/renesas/vsp1/vsp1_drm.h index ab8b7e3161a2..3fd95b53f27e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.h @@ -20,6 +20,7 @@ /** * struct vsp1_drm_pipeline - State for the API exposed to the DRM driver * @pipe: the VSP1 pipeline used for display + * @partition: the pre-calculated partition used by the pipeline * @width: output display width * @height: output display height * @force_brx_release: when set, release the BRx during the next reconfiguration @@ -31,6 +32,7 @@ */ struct vsp1_drm_pipeline { struct vsp1_pipeline pipe; + struct vsp1_partition partition; unsigned int width; unsigned int height; -- cgit v1.2.3 From 4467bd9e448905c10b00b42af29019a0a54b725f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 14 Nov 2023 00:53:07 +0200 Subject: media: renesas: vsp1: Get configuration from partition instead of state Entities access various piece of information from the subdev state when configuring a partition. The same data is available through the partition structure passed to the .configure_partition() operation. Use it to avoid accessing the state, which will simplify moving to the V4L2 subdev active state API. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- drivers/media/platform/renesas/vsp1/vsp1_rpf.c | 35 +++++++++++++------------- drivers/media/platform/renesas/vsp1/vsp1_uds.c | 6 +---- drivers/media/platform/renesas/vsp1/vsp1_wpf.c | 18 ++++--------- 3 files changed, 23 insertions(+), 36 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c index 862751616646..b4558670b46f 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c @@ -289,7 +289,7 @@ static void rpf_configure_partition(struct vsp1_entity *entity, struct vsp1_device *vsp1 = rpf->entity.vsp1; const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; const struct v4l2_pix_format_mplane *format = &rpf->format; - struct v4l2_rect crop; + struct v4l2_rect crop = partition->rpf[rpf->entity.index]; /* * Source size and crop offsets. @@ -299,22 +299,6 @@ static void rpf_configure_partition(struct vsp1_entity *entity, * offsets are needed, as planes 2 and 3 always have identical * strides. */ - crop = *v4l2_subdev_state_get_crop(rpf->entity.state, RWPF_PAD_SINK); - - /* - * Partition Algorithm Control - * - * The partition algorithm can split this frame into multiple - * slices. We must scale our partition window based on the pipe - * configuration to match the destination partition window. - * To achieve this, we adjust our crop to provide a 'sub-crop' - * matching the expected partition window. Only 'left' and - * 'width' need to be adjusted. - */ - if (pipe->partitions > 1) { - crop.width = partition->rpf[rpf->entity.index].width; - crop.left += partition->rpf[rpf->entity.index].left; - } if (pipe->interlaced) { crop.height = round_down(crop.height / 2, fmtinfo->vsub); @@ -369,8 +353,23 @@ static void rpf_partition(struct vsp1_entity *entity, struct v4l2_rect *window) { struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); + struct v4l2_rect *rpf_rect = &partition->rpf[rpf->entity.index]; - partition->rpf[rpf->entity.index] = *window; + /* + * Partition Algorithm Control + * + * The partition algorithm can split this frame into multiple slices. We + * must adjust our partition window based on the pipe configuration to + * match the destination partition window. To achieve this, we adjust + * our crop to provide a 'sub-crop' matching the expected partition + * window. + */ + *rpf_rect = *v4l2_subdev_state_get_crop(entity->state, RWPF_PAD_SINK); + + if (pipe->partitions > 1) { + rpf_rect->width = window->width; + rpf_rect->left += window->left; + } } static const struct vsp1_entity_operations rpf_entity_ops = { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c index 4a14fd3baac1..e5953d86c17c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c @@ -305,10 +305,6 @@ static void uds_configure_partition(struct vsp1_entity *entity, struct vsp1_dl_body *dlb) { struct vsp1_uds *uds = to_uds(&entity->subdev); - const struct v4l2_mbus_framefmt *output; - - output = v4l2_subdev_state_get_format(uds->entity.state, - UDS_PAD_SOURCE); /* Input size clipping. */ vsp1_uds_write(uds, dlb, VI6_UDS_HSZCLIP, VI6_UDS_HSZCLIP_HCEN | @@ -320,7 +316,7 @@ static void uds_configure_partition(struct vsp1_entity *entity, vsp1_uds_write(uds, dlb, VI6_UDS_CLIP_SIZE, (partition->uds_source.width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | - (output->height + (partition->uds_source.height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index f8d1e2f47691..5c363ff1d36c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -370,7 +370,6 @@ static void wpf_configure_partition(struct vsp1_entity *entity, struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); struct vsp1_device *vsp1 = wpf->entity.vsp1; struct vsp1_rwpf_memory mem = wpf->mem; - const struct v4l2_mbus_framefmt *sink_format; const struct v4l2_pix_format_mplane *format = &wpf->format; const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; unsigned int width; @@ -380,20 +379,13 @@ static void wpf_configure_partition(struct vsp1_entity *entity, unsigned int flip; unsigned int i; - sink_format = v4l2_subdev_state_get_format(wpf->entity.state, - RWPF_PAD_SINK); - width = sink_format->width; - height = sink_format->height; - left = 0; - /* - * Cropping. The partition algorithm can split the image into - * multiple slices. + * Cropping. The partition algorithm can split the image into multiple + * slices. */ - if (pipe->partitions > 1) { - width = partition->wpf.width; - left = partition->wpf.left; - } + width = partition->wpf.width; + left = partition->wpf.left; + height = partition->wpf.height; vsp1_wpf_write(wpf, dlb, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | -- cgit v1.2.3 From a2bbb988704d7ffc6860eb3966ec40693b817547 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 14 Nov 2023 00:53:07 +0200 Subject: media: renesas: vsp1: Name parameters to entity operations checkpatch.pl complains when function arguments are not named: WARNING: function definition argument 'struct vsp1_entity *' should also have an identifier name + void (*configure_stream)(struct vsp1_entity *, In preparation for reworking some of the vsp1_entity_operations functions, fix the warnings for the existing ones. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- drivers/media/platform/renesas/vsp1/vsp1_entity.h | 35 ++++++++++++++--------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h index f67f60677644..42000d6e2530 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h @@ -77,20 +77,27 @@ struct vsp1_route { * configuration. */ struct vsp1_entity_operations { - void (*destroy)(struct vsp1_entity *); - void (*configure_stream)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_dl_list *, struct vsp1_dl_body *); - void (*configure_frame)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_dl_list *, struct vsp1_dl_body *); - void (*configure_partition)(struct vsp1_entity *, - struct vsp1_pipeline *, - const struct vsp1_partition *, - struct vsp1_dl_list *, - struct vsp1_dl_body *); - unsigned int (*max_width)(struct vsp1_entity *, struct vsp1_pipeline *); - void (*partition)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_partition *, unsigned int, - struct v4l2_rect *); + void (*destroy)(struct vsp1_entity *entity); + void (*configure_stream)(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb); + void (*configure_frame)(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb); + void (*configure_partition)(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb); + unsigned int (*max_width)(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe); + void (*partition)(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_partition *partition, + unsigned int index, + struct v4l2_rect *window); }; struct vsp1_entity { -- cgit v1.2.3 From 4be710a3f1b94930cdc4b569145d89a9783065db Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 12 Nov 2023 04:14:05 +0200 Subject: media: renesas: vsp1: Pass subdev state to entity operations To prepare for the removal of the vsp1_entity.state field, pass the state to all entity operations that needs to access it, instead of accessing the state from the entity inside the operation handlers. This lowers the number of accesses to the field. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- drivers/media/platform/renesas/vsp1/vsp1_brx.c | 4 ++-- drivers/media/platform/renesas/vsp1/vsp1_clu.c | 3 ++- drivers/media/platform/renesas/vsp1/vsp1_drm.c | 3 ++- drivers/media/platform/renesas/vsp1/vsp1_entity.c | 3 ++- drivers/media/platform/renesas/vsp1/vsp1_entity.h | 4 ++++ drivers/media/platform/renesas/vsp1/vsp1_hgo.c | 5 +++-- drivers/media/platform/renesas/vsp1/vsp1_hgt.c | 5 +++-- drivers/media/platform/renesas/vsp1/vsp1_hsit.c | 1 + drivers/media/platform/renesas/vsp1/vsp1_lif.c | 4 ++-- drivers/media/platform/renesas/vsp1/vsp1_lut.c | 1 + drivers/media/platform/renesas/vsp1/vsp1_pipe.c | 4 ++-- drivers/media/platform/renesas/vsp1/vsp1_rpf.c | 10 +++++----- drivers/media/platform/renesas/vsp1/vsp1_sru.c | 20 +++++++++----------- drivers/media/platform/renesas/vsp1/vsp1_uds.c | 20 +++++++++----------- drivers/media/platform/renesas/vsp1/vsp1_uif.c | 3 ++- drivers/media/platform/renesas/vsp1/vsp1_video.c | 6 ++++-- drivers/media/platform/renesas/vsp1/vsp1_wpf.c | 9 +++++---- 17 files changed, 58 insertions(+), 47 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index 05940d0427bf..5dee0490c593 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -272,6 +272,7 @@ static const struct v4l2_subdev_ops brx_ops = { */ static void brx_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -281,8 +282,7 @@ static void brx_configure_stream(struct vsp1_entity *entity, unsigned int flags; unsigned int i; - format = v4l2_subdev_state_get_format(brx->entity.state, - brx->entity.source_pad); + format = v4l2_subdev_state_get_format(state, brx->entity.source_pad); /* * The hardware is extremely flexible but we have no userspace API to diff --git a/drivers/media/platform/renesas/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c index 1e57676a420c..98645bd2a983 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_clu.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c @@ -170,6 +170,7 @@ static const struct v4l2_subdev_ops clu_ops = { */ static void clu_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -181,7 +182,7 @@ static void clu_configure_stream(struct vsp1_entity *entity, * The yuv_mode can't be changed during streaming. Cache it internally * for future runtime configuration calls. */ - format = v4l2_subdev_state_get_format(clu->entity.state, CLU_PAD_SINK); + format = v4l2_subdev_state_get_format(state, CLU_PAD_SINK); clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index 11313e26a298..b5d1f238f7be 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -574,7 +574,8 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) } vsp1_entity_route_setup(entity, pipe, dlb); - vsp1_entity_configure_stream(entity, pipe, dl, dlb); + vsp1_entity_configure_stream(entity, entity->state, pipe, + dl, dlb); vsp1_entity_configure_frame(entity, pipe, dl, dlb); vsp1_entity_configure_partition(entity, pipe, &pipe->part_table[0], dl, dlb); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index e9de75de8bde..8b8945bd8f10 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -70,12 +70,13 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity, } void vsp1_entity_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { if (entity->ops->configure_stream) - entity->ops->configure_stream(entity, pipe, dl, dlb); + entity->ops->configure_stream(entity, state, pipe, dl, dlb); } void vsp1_entity_configure_frame(struct vsp1_entity *entity, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h index 42000d6e2530..1bcc9e27dfdc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h @@ -79,6 +79,7 @@ struct vsp1_route { struct vsp1_entity_operations { void (*destroy)(struct vsp1_entity *entity); void (*configure_stream)(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb); @@ -92,8 +93,10 @@ struct vsp1_entity_operations { struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb); unsigned int (*max_width)(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe); void (*partition)(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int index, @@ -151,6 +154,7 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity, struct vsp1_dl_body *dlb); void vsp1_entity_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c index 4ee5f0e5e9c3..0ea87014a701 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c @@ -130,6 +130,7 @@ static const struct v4l2_ctrl_config hgo_num_bins_control = { */ static void hgo_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -140,8 +141,8 @@ static void hgo_configure_stream(struct vsp1_entity *entity, unsigned int hratio; unsigned int vratio; - crop = v4l2_subdev_state_get_crop(entity->state, HISTO_PAD_SINK); - compose = v4l2_subdev_state_get_compose(entity->state, HISTO_PAD_SINK); + crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK); + compose = v4l2_subdev_state_get_compose(state, HISTO_PAD_SINK); vsp1_hgo_write(hgo, dlb, VI6_HGO_REGRST, VI6_HGO_REGRST_RCLEA); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c index b739d8045576..496354c8df0e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c @@ -126,6 +126,7 @@ static const struct v4l2_ctrl_config hgt_hue_areas = { */ static void hgt_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -139,8 +140,8 @@ static void hgt_configure_stream(struct vsp1_entity *entity, u8 upper; unsigned int i; - crop = v4l2_subdev_state_get_crop(entity->state, HISTO_PAD_SINK); - compose = v4l2_subdev_state_get_compose(entity->state, HISTO_PAD_SINK); + crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK); + compose = v4l2_subdev_state_get_compose(state, HISTO_PAD_SINK); vsp1_hgt_write(hgt, dlb, VI6_HGT_REGRST, VI6_HGT_REGRST_RCLEA); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c index 4a8cce808c93..8ba2a7c7305c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c @@ -127,6 +127,7 @@ static const struct v4l2_subdev_ops hsit_ops = { */ static void hsit_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lif.c b/drivers/media/platform/renesas/vsp1/vsp1_lif.c index 29d4c1521e6a..b3d83d1c5306 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lif.c @@ -83,6 +83,7 @@ static const struct v4l2_subdev_ops lif_ops = { */ static void lif_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -93,8 +94,7 @@ static void lif_configure_stream(struct vsp1_entity *entity, unsigned int obth; unsigned int lbth; - format = v4l2_subdev_state_get_format(lif->entity.state, - LIF_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, LIF_PAD_SOURCE); switch (entity->vsp1->version & VI6_IP_VERSION_MODEL_MASK) { case VI6_IP_VERSION_MODEL_VSPD_GEN2: diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lut.c b/drivers/media/platform/renesas/vsp1/vsp1_lut.c index 451d24ab0b56..dd264e6532e0 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lut.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.c @@ -146,6 +146,7 @@ static const struct v4l2_subdev_ops lut_ops = { */ static void lut_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index 87cb62cf38fa..bb0739f684f3 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -487,8 +487,8 @@ static void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, list_for_each_entry_reverse(entity, &pipe->entities, list_pipe) { if (entity->ops->partition) - entity->ops->partition(entity, pipe, partition, index, - window); + entity->ops->partition(entity, entity->state, pipe, + partition, index, window); } } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c index b4558670b46f..5c8b3ba1bd3c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c @@ -48,6 +48,7 @@ static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, */ static void rpf_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -80,10 +81,8 @@ static void rpf_configure_stream(struct vsp1_entity *entity, vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_PSTRIDE, pstride); /* Format */ - sink_format = v4l2_subdev_state_get_format(rpf->entity.state, - RWPF_PAD_SINK); - source_format = v4l2_subdev_state_get_format(rpf->entity.state, - RWPF_PAD_SOURCE); + sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); + source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); infmt = VI6_RPF_INFMT_CIPM | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT); @@ -347,6 +346,7 @@ static void rpf_configure_partition(struct vsp1_entity *entity, } static void rpf_partition(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, @@ -364,7 +364,7 @@ static void rpf_partition(struct vsp1_entity *entity, * our crop to provide a 'sub-crop' matching the expected partition * window. */ - *rpf_rect = *v4l2_subdev_state_get_crop(entity->state, RWPF_PAD_SINK); + *rpf_rect = *v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); if (pipe->partitions > 1) { rpf_rect->width = window->width; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c index 8ce949de8d9a..1759ce642e6e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c @@ -265,6 +265,7 @@ static const struct v4l2_subdev_ops sru_ops = { */ static void sru_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -275,9 +276,8 @@ static void sru_configure_stream(struct vsp1_entity *entity, struct v4l2_mbus_framefmt *output; u32 ctrl0; - input = v4l2_subdev_state_get_format(sru->entity.state, SRU_PAD_SINK); - output = v4l2_subdev_state_get_format(sru->entity.state, - SRU_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); + output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32) ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3 @@ -298,15 +298,14 @@ static void sru_configure_stream(struct vsp1_entity *entity, } static unsigned int sru_max_width(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe) { - struct vsp1_sru *sru = to_sru(&entity->subdev); struct v4l2_mbus_framefmt *input; struct v4l2_mbus_framefmt *output; - input = v4l2_subdev_state_get_format(sru->entity.state, SRU_PAD_SINK); - output = v4l2_subdev_state_get_format(sru->entity.state, - SRU_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); + output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); /* * The maximum input width of the SRU is 288 input pixels, but 32 @@ -320,18 +319,17 @@ static unsigned int sru_max_width(struct vsp1_entity *entity, } static void sru_partition(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, struct v4l2_rect *window) { - struct vsp1_sru *sru = to_sru(&entity->subdev); struct v4l2_mbus_framefmt *input; struct v4l2_mbus_framefmt *output; - input = v4l2_subdev_state_get_format(sru->entity.state, SRU_PAD_SINK); - output = v4l2_subdev_state_get_format(sru->entity.state, - SRU_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); + output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); /* Adapt if SRUx2 is enabled. */ if (input->width != output->width) { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c index e5953d86c17c..c5a38478cf8c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c @@ -252,6 +252,7 @@ static const struct v4l2_subdev_ops uds_ops = { */ static void uds_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -263,9 +264,8 @@ static void uds_configure_stream(struct vsp1_entity *entity, unsigned int vscale; bool multitap; - input = v4l2_subdev_state_get_format(uds->entity.state, UDS_PAD_SINK); - output = v4l2_subdev_state_get_format(uds->entity.state, - UDS_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); + output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE); hscale = uds_compute_ratio(input->width, output->width); vscale = uds_compute_ratio(input->height, output->height); @@ -321,16 +321,15 @@ static void uds_configure_partition(struct vsp1_entity *entity, } static unsigned int uds_max_width(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe) { - struct vsp1_uds *uds = to_uds(&entity->subdev); const struct v4l2_mbus_framefmt *output; const struct v4l2_mbus_framefmt *input; unsigned int hscale; - input = v4l2_subdev_state_get_format(uds->entity.state, UDS_PAD_SINK); - output = v4l2_subdev_state_get_format(uds->entity.state, - UDS_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); + output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE); hscale = output->width / input->width; /* @@ -356,18 +355,17 @@ static unsigned int uds_max_width(struct vsp1_entity *entity, */ static void uds_partition(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, struct v4l2_rect *window) { - struct vsp1_uds *uds = to_uds(&entity->subdev); const struct v4l2_mbus_framefmt *output; const struct v4l2_mbus_framefmt *input; - input = v4l2_subdev_state_get_format(uds->entity.state, UDS_PAD_SINK); - output = v4l2_subdev_state_get_format(uds->entity.state, - UDS_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); + output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE); partition->uds_sink.width = window->width * input->width / output->width; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c index cecd2f7024f4..edaf28b544d2 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c @@ -188,6 +188,7 @@ static const struct v4l2_subdev_ops uif_ops = { */ static void uif_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -200,7 +201,7 @@ static void uif_configure_stream(struct vsp1_entity *entity, vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMPMR, VI6_UIF_DISCOM_DOCMPMR_SEL(9)); - crop = v4l2_subdev_state_get_crop(entity->state, UIF_PAD_SINK); + crop = v4l2_subdev_state_get_crop(state, UIF_PAD_SINK); left = crop->left; width = crop->width; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index 64d3a69d65b2..fdb46ec0c872 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -699,7 +699,9 @@ static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) if (!entity->ops->max_width) continue; - entity_max = entity->ops->max_width(entity, pipe); + entity_max = entity->ops->max_width(entity, + entity->state, + pipe); if (entity_max) div_size = min(div_size, entity_max); } @@ -760,7 +762,7 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) list_for_each_entry(entity, &pipe->entities, list_pipe) { vsp1_entity_route_setup(entity, pipe, pipe->stream_config); - vsp1_entity_configure_stream(entity, pipe, NULL, + vsp1_entity_configure_stream(entity, entity->state, pipe, NULL, pipe->stream_config); } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index 5c363ff1d36c..f176750ccd98 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -229,6 +229,7 @@ static int wpf_configure_writeback_chain(struct vsp1_rwpf *wpf, } static void wpf_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -243,10 +244,8 @@ static void wpf_configure_stream(struct vsp1_entity *entity, u32 srcrpf = 0; int ret; - sink_format = v4l2_subdev_state_get_format(wpf->entity.state, - RWPF_PAD_SINK); - source_format = v4l2_subdev_state_get_format(wpf->entity.state, - RWPF_PAD_SOURCE); + sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); + source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); /* Format */ if (!pipe->lif || wpf->writeback) { @@ -496,6 +495,7 @@ static void wpf_configure_partition(struct vsp1_entity *entity, } static unsigned int wpf_max_width(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe) { struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); @@ -504,6 +504,7 @@ static unsigned int wpf_max_width(struct vsp1_entity *entity, } static void wpf_partition(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, -- cgit v1.2.3 From 1b9fd2f0b5133974917efafd066f79e0e309e602 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 13 Nov 2023 18:41:44 +0200 Subject: media: renesas: vsp1: Initialize control handler after subdev Some VSP modules initialize their control handler after initializing the subdev, while some initialize it before. This makes the code inconsistent and more error prone. Standardize on control initialization after initializing the subdev. Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- drivers/media/platform/renesas/vsp1/vsp1_hgo.c | 20 ++++++++++---------- drivers/media/platform/renesas/vsp1/vsp1_hgt.c | 12 ++++++------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c index 0ea87014a701..2c8ce7175a4e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c @@ -192,6 +192,16 @@ struct vsp1_hgo *vsp1_hgo_create(struct vsp1_device *vsp1) if (hgo == NULL) return ERR_PTR(-ENOMEM); + /* Initialize the video device and queue for statistics data. */ + ret = vsp1_histogram_init(vsp1, &hgo->histo, VSP1_ENTITY_HGO, "hgo", + &hgo_entity_ops, hgo_mbus_formats, + ARRAY_SIZE(hgo_mbus_formats), + HGO_DATA_SIZE, V4L2_META_FMT_VSP1_HGO); + if (ret < 0) { + vsp1_entity_destroy(&hgo->histo.entity); + return ERR_PTR(ret); + } + /* Initialize the control handler. */ v4l2_ctrl_handler_init(&hgo->ctrls.handler, vsp1->info->gen >= 3 ? 2 : 1); @@ -207,15 +217,5 @@ struct vsp1_hgo *vsp1_hgo_create(struct vsp1_device *vsp1) hgo->histo.entity.subdev.ctrl_handler = &hgo->ctrls.handler; - /* Initialize the video device and queue for statistics data. */ - ret = vsp1_histogram_init(vsp1, &hgo->histo, VSP1_ENTITY_HGO, "hgo", - &hgo_entity_ops, hgo_mbus_formats, - ARRAY_SIZE(hgo_mbus_formats), - HGO_DATA_SIZE, V4L2_META_FMT_VSP1_HGO); - if (ret < 0) { - vsp1_entity_destroy(&hgo->histo.entity); - return ERR_PTR(ret); - } - return hgo; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c index 496354c8df0e..858f330d44fa 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c @@ -191,12 +191,6 @@ struct vsp1_hgt *vsp1_hgt_create(struct vsp1_device *vsp1) if (hgt == NULL) return ERR_PTR(-ENOMEM); - /* Initialize the control handler. */ - v4l2_ctrl_handler_init(&hgt->ctrls, 1); - v4l2_ctrl_new_custom(&hgt->ctrls, &hgt_hue_areas, NULL); - - hgt->histo.entity.subdev.ctrl_handler = &hgt->ctrls; - /* Initialize the video device and queue for statistics data. */ ret = vsp1_histogram_init(vsp1, &hgt->histo, VSP1_ENTITY_HGT, "hgt", &hgt_entity_ops, hgt_mbus_formats, @@ -207,6 +201,12 @@ struct vsp1_hgt *vsp1_hgt_create(struct vsp1_device *vsp1) return ERR_PTR(ret); } + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(&hgt->ctrls, 1); + v4l2_ctrl_new_custom(&hgt->ctrls, &hgt_hue_areas, NULL); + + hgt->histo.entity.subdev.ctrl_handler = &hgt->ctrls; + v4l2_ctrl_handler_setup(&hgt->ctrls); return hgt; -- cgit v1.2.3 From 9ccbcd53833538aeb17db4506891e00020051150 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Wed, 12 Jun 2024 15:58:25 -0700 Subject: media: rc: add missing MODULE_DESCRIPTION() macro With ARCH=x86, make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/rc/rc-core.o Add the missing invocation of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Signed-off-by: Sean Young Signed-off-by: Hans Verkuil --- drivers/media/rc/rc-main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 6bdad6341844..a4c539b17cf3 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -2092,4 +2092,5 @@ subsys_initcall(rc_core_init); module_exit(rc_core_exit); MODULE_AUTHOR("Mauro Carvalho Chehab"); +MODULE_DESCRIPTION("Remote Controller core module"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 8da17fe240f573101672cf3fe7c8b9fe0acda80a Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Fri, 22 Mar 2024 10:28:44 +0100 Subject: dt-bindings: media: mediatek: mdp3: Add support for MT8188 RDMA Add a compatible for MediaTek MT8188 RDMA, which supports only a subset of the MDP3 components of its similar MT8195 counterpart. Signed-off-by: AngeloGioacchino Del Regno Acked-by: Conor Dooley Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- Documentation/devicetree/bindings/media/mediatek,mdp3-rdma.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/media/mediatek,mdp3-rdma.yaml b/Documentation/devicetree/bindings/media/mediatek,mdp3-rdma.yaml index 59db8306485b..18603f6c5e06 100644 --- a/Documentation/devicetree/bindings/media/mediatek,mdp3-rdma.yaml +++ b/Documentation/devicetree/bindings/media/mediatek,mdp3-rdma.yaml @@ -23,6 +23,7 @@ properties: oneOf: - enum: - mediatek,mt8183-mdp3-rdma + - mediatek,mt8188-mdp3-rdma - mediatek,mt8195-mdp3-rdma - mediatek,mt8195-vdo1-rdma - items: -- cgit v1.2.3 From 7fc65b78b465b8511a503491e7c3116d46dc6c72 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Fri, 22 Mar 2024 10:28:45 +0100 Subject: media: platform: mtk-mdp3: Add support for MT8188 MDP3 components MT8195 and MT8188 share a similar MDP3 macro-block, with minor differences - as in, the latter supports a subset of the number of components supported by the former, but are otherwise handled in the same way. Add driver data for MT8188, reusing the already present MT8195 data where possible. Signed-off-by: AngeloGioacchino Del Regno Reviewed-by: Fei Shao Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- .../media/platform/mediatek/mdp3/mdp_cfg_data.c | 280 +++++++++++++++++++++ drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h | 1 + .../media/platform/mediatek/mdp3/mtk-mdp3-cfg.h | 1 + .../media/platform/mediatek/mdp3/mtk-mdp3-core.c | 3 + 4 files changed, 285 insertions(+) diff --git a/drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c b/drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c index ecca52b45307..0b4c50bc1776 100644 --- a/drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c +++ b/drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c @@ -46,6 +46,53 @@ enum mt8183_mdp_comp_id { MT8183_MDP_COMP_WROT1, /* 25 */ }; +enum mt8188_mdp_comp_id { + /* MT8188 Comp id */ + /* ISP */ + MT8188_MDP_COMP_WPEI = 0, + MT8188_MDP_COMP_WPEO, /* 1 */ + + /* MDP */ + MT8188_MDP_COMP_CAMIN, /* 2 */ + MT8188_MDP_COMP_RDMA0, /* 3 */ + MT8188_MDP_COMP_RDMA2, /* 4 */ + MT8188_MDP_COMP_RDMA3, /* 5 */ + MT8188_MDP_COMP_FG0, /* 6 */ + MT8188_MDP_COMP_FG2, /* 7 */ + MT8188_MDP_COMP_FG3, /* 8 */ + MT8188_MDP_COMP_TO_SVPP2MOUT, /* 9 */ + MT8188_MDP_COMP_TO_SVPP3MOUT, /* 10 */ + MT8188_MDP_COMP_TO_WARP0MOUT, /* 11 */ + MT8188_MDP_COMP_VPP0_SOUT, /* 12 */ + MT8188_MDP_COMP_VPP1_SOUT, /* 13 */ + MT8188_MDP_COMP_PQ0_SOUT, /* 14 */ + MT8188_MDP_COMP_HDR0, /* 15 */ + MT8188_MDP_COMP_HDR2, /* 16 */ + MT8188_MDP_COMP_HDR3, /* 17 */ + MT8188_MDP_COMP_AAL0, /* 18 */ + MT8188_MDP_COMP_AAL2, /* 19 */ + MT8188_MDP_COMP_AAL3, /* 20 */ + MT8188_MDP_COMP_RSZ0, /* 21 */ + MT8188_MDP_COMP_RSZ2, /* 22 */ + MT8188_MDP_COMP_RSZ3, /* 23 */ + MT8188_MDP_COMP_TDSHP0, /* 24 */ + MT8188_MDP_COMP_TDSHP2, /* 25 */ + MT8188_MDP_COMP_TDSHP3, /* 26 */ + MT8188_MDP_COMP_COLOR0, /* 27 */ + MT8188_MDP_COMP_COLOR2, /* 28 */ + MT8188_MDP_COMP_COLOR3, /* 29 */ + MT8188_MDP_COMP_OVL0, /* 30 */ + MT8188_MDP_COMP_PAD0, /* 31 */ + MT8188_MDP_COMP_PAD2, /* 32 */ + MT8188_MDP_COMP_PAD3, /* 33 */ + MT8188_MDP_COMP_TCC0, /* 34 */ + MT8188_MDP_COMP_WROT0, /* 35 */ + MT8188_MDP_COMP_WROT2, /* 36 */ + MT8188_MDP_COMP_WROT3, /* 37 */ + MT8188_MDP_COMP_MERGE2, /* 38 */ + MT8188_MDP_COMP_MERGE3, /* 39 */ +}; + enum mt8195_mdp_comp_id { /* MT8195 Comp id */ /* ISP */ @@ -123,6 +170,13 @@ static const struct of_device_id mt8183_mdp_probe_infra[MDP_INFRA_MAX] = { [MDP_INFRA_SCP] = { .compatible = "mediatek,mt8183-scp" } }; +static const struct of_device_id mt8188_mdp_probe_infra[MDP_INFRA_MAX] = { + [MDP_INFRA_MMSYS] = { .compatible = "mediatek,mt8188-vppsys0" }, + [MDP_INFRA_MMSYS2] = { .compatible = "mediatek,mt8188-vppsys1" }, + [MDP_INFRA_MUTEX] = { .compatible = "mediatek,mt8188-vpp-mutex" }, + [MDP_INFRA_MUTEX2] = { .compatible = "mediatek,mt8188-vpp-mutex" }, +}; + static const struct of_device_id mt8195_mdp_probe_infra[MDP_INFRA_MAX] = { [MDP_INFRA_MMSYS] = { .compatible = "mediatek,mt8195-vppsys0" }, [MDP_INFRA_MMSYS2] = { .compatible = "mediatek,mt8195-vppsys1" }, @@ -167,6 +221,40 @@ static const u32 mt8183_mutex_idx[MDP_MAX_COMP_COUNT] = { [MDP_COMP_CCORR0] = MUTEX_MOD_IDX_MDP_CCORR0, }; +static const u32 mt8188_mutex_idx[MDP_MAX_COMP_COUNT] = { + [MDP_COMP_RDMA0] = MUTEX_MOD_IDX_MDP_RDMA0, + [MDP_COMP_RDMA2] = MUTEX_MOD_IDX_MDP_RDMA2, + [MDP_COMP_RDMA3] = MUTEX_MOD_IDX_MDP_RDMA3, + [MDP_COMP_FG0] = MUTEX_MOD_IDX_MDP_FG0, + [MDP_COMP_FG2] = MUTEX_MOD_IDX_MDP_FG2, + [MDP_COMP_FG3] = MUTEX_MOD_IDX_MDP_FG3, + [MDP_COMP_HDR0] = MUTEX_MOD_IDX_MDP_HDR0, + [MDP_COMP_HDR2] = MUTEX_MOD_IDX_MDP_HDR2, + [MDP_COMP_HDR3] = MUTEX_MOD_IDX_MDP_HDR3, + [MDP_COMP_AAL0] = MUTEX_MOD_IDX_MDP_AAL0, + [MDP_COMP_AAL2] = MUTEX_MOD_IDX_MDP_AAL2, + [MDP_COMP_AAL3] = MUTEX_MOD_IDX_MDP_AAL3, + [MDP_COMP_RSZ0] = MUTEX_MOD_IDX_MDP_RSZ0, + [MDP_COMP_RSZ2] = MUTEX_MOD_IDX_MDP_RSZ2, + [MDP_COMP_RSZ3] = MUTEX_MOD_IDX_MDP_RSZ3, + [MDP_COMP_MERGE2] = MUTEX_MOD_IDX_MDP_MERGE2, + [MDP_COMP_MERGE3] = MUTEX_MOD_IDX_MDP_MERGE3, + [MDP_COMP_TDSHP0] = MUTEX_MOD_IDX_MDP_TDSHP0, + [MDP_COMP_TDSHP2] = MUTEX_MOD_IDX_MDP_TDSHP2, + [MDP_COMP_TDSHP3] = MUTEX_MOD_IDX_MDP_TDSHP3, + [MDP_COMP_COLOR0] = MUTEX_MOD_IDX_MDP_COLOR0, + [MDP_COMP_COLOR2] = MUTEX_MOD_IDX_MDP_COLOR2, + [MDP_COMP_COLOR3] = MUTEX_MOD_IDX_MDP_COLOR3, + [MDP_COMP_OVL0] = MUTEX_MOD_IDX_MDP_OVL0, + [MDP_COMP_PAD0] = MUTEX_MOD_IDX_MDP_PAD0, + [MDP_COMP_PAD2] = MUTEX_MOD_IDX_MDP_PAD2, + [MDP_COMP_PAD3] = MUTEX_MOD_IDX_MDP_PAD3, + [MDP_COMP_TCC0] = MUTEX_MOD_IDX_MDP_TCC0, + [MDP_COMP_WROT0] = MUTEX_MOD_IDX_MDP_WROT0, + [MDP_COMP_WROT2] = MUTEX_MOD_IDX_MDP_WROT2, + [MDP_COMP_WROT3] = MUTEX_MOD_IDX_MDP_WROT3, +}; + static const u32 mt8195_mutex_idx[MDP_MAX_COMP_COUNT] = { [MDP_COMP_RDMA0] = MUTEX_MOD_IDX_MDP_RDMA0, [MDP_COMP_RDMA1] = MUTEX_MOD_IDX_MDP_RDMA1, @@ -288,6 +376,171 @@ static const struct mdp_comp_data mt8183_mdp_comp_data[MDP_MAX_COMP_COUNT] = { }, }; +static const struct mdp_comp_data mt8188_mdp_comp_data[MDP_MAX_COMP_COUNT] = { + [MDP_COMP_WPEI] = { + {MDP_COMP_TYPE_WPEI, 0, MT8188_MDP_COMP_WPEI, MDP_MM_SUBSYS_0}, + {0, 0, 0} + }, + [MDP_COMP_WPEO] = { + {MDP_COMP_TYPE_EXTO, 0, MT8188_MDP_COMP_WPEO, MDP_MM_SUBSYS_0}, + {0, 0, 0} + }, + [MDP_COMP_CAMIN] = { + {MDP_COMP_TYPE_DL_PATH, 0, MT8188_MDP_COMP_CAMIN, MDP_MM_SUBSYS_0}, + {3, 3, 0} + }, + [MDP_COMP_RDMA0] = { + {MDP_COMP_TYPE_RDMA, 0, MT8188_MDP_COMP_RDMA0, MDP_MM_SUBSYS_0}, + {3, 0, 0} + }, + [MDP_COMP_RDMA2] = { + {MDP_COMP_TYPE_RDMA, 1, MT8188_MDP_COMP_RDMA2, MDP_MM_SUBSYS_1}, + {3, 0, 0} + }, + [MDP_COMP_RDMA3] = { + {MDP_COMP_TYPE_RDMA, 2, MT8188_MDP_COMP_RDMA3, MDP_MM_SUBSYS_1}, + {3, 0, 0} + }, + [MDP_COMP_FG0] = { + {MDP_COMP_TYPE_FG, 0, MT8188_MDP_COMP_FG0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_FG2] = { + {MDP_COMP_TYPE_FG, 1, MT8188_MDP_COMP_FG2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_FG3] = { + {MDP_COMP_TYPE_FG, 2, MT8188_MDP_COMP_FG3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_HDR0] = { + {MDP_COMP_TYPE_HDR, 0, MT8188_MDP_COMP_HDR0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_HDR2] = { + {MDP_COMP_TYPE_HDR, 1, MT8188_MDP_COMP_HDR2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_HDR3] = { + {MDP_COMP_TYPE_HDR, 2, MT8188_MDP_COMP_HDR3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_AAL0] = { + {MDP_COMP_TYPE_AAL, 0, MT8188_MDP_COMP_AAL0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_AAL2] = { + {MDP_COMP_TYPE_AAL, 1, MT8188_MDP_COMP_AAL2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_AAL3] = { + {MDP_COMP_TYPE_AAL, 2, MT8188_MDP_COMP_AAL3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_RSZ0] = { + {MDP_COMP_TYPE_RSZ, 0, MT8188_MDP_COMP_RSZ0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_RSZ2] = { + {MDP_COMP_TYPE_RSZ, 1, MT8188_MDP_COMP_RSZ2, MDP_MM_SUBSYS_1}, + {2, 0, 0}, + {MDP_COMP_MERGE2, true, true} + }, + [MDP_COMP_RSZ3] = { + {MDP_COMP_TYPE_RSZ, 2, MT8188_MDP_COMP_RSZ3, MDP_MM_SUBSYS_1}, + {2, 0, 0}, + {MDP_COMP_MERGE3, true, true} + }, + [MDP_COMP_TDSHP0] = { + {MDP_COMP_TYPE_TDSHP, 0, MT8188_MDP_COMP_TDSHP0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_TDSHP2] = { + {MDP_COMP_TYPE_TDSHP, 1, MT8188_MDP_COMP_TDSHP2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_TDSHP3] = { + {MDP_COMP_TYPE_TDSHP, 2, MT8188_MDP_COMP_TDSHP3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_COLOR0] = { + {MDP_COMP_TYPE_COLOR, 0, MT8188_MDP_COMP_COLOR0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_COLOR2] = { + {MDP_COMP_TYPE_COLOR, 1, MT8188_MDP_COMP_COLOR2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_COLOR3] = { + {MDP_COMP_TYPE_COLOR, 2, MT8188_MDP_COMP_COLOR3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_OVL0] = { + {MDP_COMP_TYPE_OVL, 0, MT8188_MDP_COMP_OVL0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_PAD0] = { + {MDP_COMP_TYPE_PAD, 0, MT8188_MDP_COMP_PAD0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_PAD2] = { + {MDP_COMP_TYPE_PAD, 1, MT8188_MDP_COMP_PAD2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_PAD3] = { + {MDP_COMP_TYPE_PAD, 2, MT8188_MDP_COMP_PAD3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_TCC0] = { + {MDP_COMP_TYPE_TCC, 0, MT8188_MDP_COMP_TCC0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_WROT0] = { + {MDP_COMP_TYPE_WROT, 0, MT8188_MDP_COMP_WROT0, MDP_MM_SUBSYS_0}, + {1, 0, 0} + }, + [MDP_COMP_WROT2] = { + {MDP_COMP_TYPE_WROT, 1, MT8188_MDP_COMP_WROT2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_WROT3] = { + {MDP_COMP_TYPE_WROT, 2, MT8188_MDP_COMP_WROT3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_MERGE2] = { + {MDP_COMP_TYPE_MERGE, 0, MT8188_MDP_COMP_MERGE2, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_MERGE3] = { + {MDP_COMP_TYPE_MERGE, 1, MT8188_MDP_COMP_MERGE3, MDP_MM_SUBSYS_1}, + {1, 0, 0} + }, + [MDP_COMP_PQ0_SOUT] = { + {MDP_COMP_TYPE_DUMMY, 0, MT8188_MDP_COMP_PQ0_SOUT, MDP_MM_SUBSYS_0}, + {0, 0, 0} + }, + [MDP_COMP_TO_WARP0MOUT] = { + {MDP_COMP_TYPE_DUMMY, 1, MT8188_MDP_COMP_TO_WARP0MOUT, MDP_MM_SUBSYS_0}, + {0, 0, 0} + }, + [MDP_COMP_TO_SVPP2MOUT] = { + {MDP_COMP_TYPE_DUMMY, 2, MT8188_MDP_COMP_TO_SVPP2MOUT, MDP_MM_SUBSYS_1}, + {0, 0, 0} + }, + [MDP_COMP_TO_SVPP3MOUT] = { + {MDP_COMP_TYPE_DUMMY, 3, MT8188_MDP_COMP_TO_SVPP3MOUT, MDP_MM_SUBSYS_1}, + {0, 0, 0} + }, + [MDP_COMP_VPP0_SOUT] = { + {MDP_COMP_TYPE_PATH, 0, MT8188_MDP_COMP_VPP0_SOUT, MDP_MM_SUBSYS_1}, + {2, 6, 0} + }, + [MDP_COMP_VPP1_SOUT] = { + {MDP_COMP_TYPE_PATH, 1, MT8188_MDP_COMP_VPP1_SOUT, MDP_MM_SUBSYS_0}, + {2, 8, 0} + }, +}; + static const struct mdp_comp_data mt8195_mdp_comp_data[MDP_MAX_COMP_COUNT] = { [MDP_COMP_WPEI] = { {MDP_COMP_TYPE_WPEI, 0, MT8195_MDP_COMP_WPEI, MDP_MM_SUBSYS_0}, @@ -1046,6 +1299,15 @@ static const struct mdp_pipe_info mt8183_pipe_info[] = { [MDP_PIPE_RDMA0] = {MDP_PIPE_RDMA0, MDP_MM_SUBSYS_0, 3} }; +static const struct mdp_pipe_info mt8188_pipe_info[] = { + [MDP_PIPE_WPEI] = {MDP_PIPE_WPEI, MDP_MM_SUBSYS_0, 0}, + [MDP_PIPE_RDMA0] = {MDP_PIPE_RDMA0, MDP_MM_SUBSYS_0, 1}, + [MDP_PIPE_RDMA2] = {MDP_PIPE_RDMA2, MDP_MM_SUBSYS_1, 0}, + [MDP_PIPE_RDMA3] = {MDP_PIPE_RDMA3, MDP_MM_SUBSYS_1, 1}, + [MDP_PIPE_VPP1_SOUT] = {MDP_PIPE_VPP1_SOUT, MDP_MM_SUBSYS_0, 2}, + [MDP_PIPE_VPP0_SOUT] = {MDP_PIPE_VPP0_SOUT, MDP_MM_SUBSYS_1, 2}, +}; + static const struct mdp_pipe_info mt8195_pipe_info[] = { [MDP_PIPE_WPEI] = {MDP_PIPE_WPEI, MDP_MM_SUBSYS_0, 0}, [MDP_PIPE_WPEI2] = {MDP_PIPE_WPEI2, MDP_MM_SUBSYS_0, 1}, @@ -1082,6 +1344,24 @@ const struct mtk_mdp_driver_data mt8183_mdp_driver_data = { .pp_used = MDP_PP_USED_1, }; +const struct mtk_mdp_driver_data mt8188_mdp_driver_data = { + .mdp_plat_id = MT8188, + .mdp_con_res = 0x14001000, + .mdp_probe_infra = mt8188_mdp_probe_infra, + .mdp_sub_comp_dt_ids = mt8195_sub_comp_dt_ids, + .mdp_cfg = &mt8195_plat_cfg, + .mdp_mutex_table_idx = mt8188_mutex_idx, + .comp_data = mt8188_mdp_comp_data, + .comp_data_len = ARRAY_SIZE(mt8188_mdp_comp_data), + .format = mt8195_formats, + .format_len = ARRAY_SIZE(mt8195_formats), + .def_limit = &mt8195_mdp_def_limit, + .pipe_info = mt8188_pipe_info, + .pipe_info_len = ARRAY_SIZE(mt8188_pipe_info), + .pp_criteria = &mt8195_mdp_pp_criteria, + .pp_used = MDP_PP_USED_2, +}; + const struct mtk_mdp_driver_data mt8195_mdp_driver_data = { .mdp_plat_id = MT8195, .mdp_con_res = 0x14001000, diff --git a/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h b/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h index f83ac408306e..4764c5b5107b 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h +++ b/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h @@ -116,6 +116,7 @@ struct img_frameparam { /* Platform config indicator */ #define MT8183 8183 +#define MT8188 8195 #define MT8195 8195 #define CFG_CHECK(plat, p_id) ((plat) == (p_id)) diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h index 49cdf45f6e59..7f7625299ce7 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h @@ -10,6 +10,7 @@ #include extern const struct mtk_mdp_driver_data mt8183_mdp_driver_data; +extern const struct mtk_mdp_driver_data mt8188_mdp_driver_data; extern const struct mtk_mdp_driver_data mt8195_mdp_driver_data; struct mdp_dev; diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c index c1f3bf98120a..37e7b985d52c 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c @@ -21,6 +21,9 @@ static const struct of_device_id mdp_of_ids[] = { { .compatible = "mediatek,mt8183-mdp3-rdma", .data = &mt8183_mdp_driver_data, }, + { .compatible = "mediatek,mt8188-mdp3-rdma", + .data = &mt8188_mdp_driver_data, + }, { .compatible = "mediatek,mt8195-mdp3-rdma", .data = &mt8195_mdp_driver_data, }, -- cgit v1.2.3 From 68594cec291ff9523b9feb3f43fd853dcddd1f60 Mon Sep 17 00:00:00 2001 From: Chi Zhiling Date: Fri, 14 Jun 2024 08:22:25 -0700 Subject: media: xc2028: avoid use-after-free in load_firmware_cb() syzkaller reported use-after-free in load_firmware_cb() [1]. The reason is because the module allocated a struct tuner in tuner_probe(), and then the module initialization failed, the struct tuner was released. A worker which created during module initialization accesses this struct tuner later, it caused use-after-free. The process is as follows: task-6504 worker_thread tuner_probe <= alloc dvb_frontend [2] ... request_firmware_nowait <= create a worker ... tuner_remove <= free dvb_frontend ... request_firmware_work_func <= the firmware is ready load_firmware_cb <= but now the dvb_frontend has been freed To fix the issue, check the dvd_frontend in load_firmware_cb(), if it is null, report a warning and just return. [1]: ================================================================== BUG: KASAN: use-after-free in load_firmware_cb+0x1310/0x17a0 Read of size 8 at addr ffff8000d7ca2308 by task kworker/2:3/6504 Call trace: load_firmware_cb+0x1310/0x17a0 request_firmware_work_func+0x128/0x220 process_one_work+0x770/0x1824 worker_thread+0x488/0xea0 kthread+0x300/0x430 ret_from_fork+0x10/0x20 Allocated by task 6504: kzalloc tuner_probe+0xb0/0x1430 i2c_device_probe+0x92c/0xaf0 really_probe+0x678/0xcd0 driver_probe_device+0x280/0x370 __device_attach_driver+0x220/0x330 bus_for_each_drv+0x134/0x1c0 __device_attach+0x1f4/0x410 device_initial_probe+0x20/0x30 bus_probe_device+0x184/0x200 device_add+0x924/0x12c0 device_register+0x24/0x30 i2c_new_device+0x4e0/0xc44 v4l2_i2c_new_subdev_board+0xbc/0x290 v4l2_i2c_new_subdev+0xc8/0x104 em28xx_v4l2_init+0x1dd0/0x3770 Freed by task 6504: kfree+0x238/0x4e4 tuner_remove+0x144/0x1c0 i2c_device_remove+0xc8/0x290 __device_release_driver+0x314/0x5fc device_release_driver+0x30/0x44 bus_remove_device+0x244/0x490 device_del+0x350/0x900 device_unregister+0x28/0xd0 i2c_unregister_device+0x174/0x1d0 v4l2_device_unregister+0x224/0x380 em28xx_v4l2_init+0x1d90/0x3770 The buggy address belongs to the object at ffff8000d7ca2000 which belongs to the cache kmalloc-2k of size 2048 The buggy address is located 776 bytes inside of 2048-byte region [ffff8000d7ca2000, ffff8000d7ca2800) The buggy address belongs to the page: page:ffff7fe00035f280 count:1 mapcount:0 mapping:ffff8000c001f000 index:0x0 flags: 0x7ff800000000100(slab) raw: 07ff800000000100 ffff7fe00049d880 0000000300000003 ffff8000c001f000 raw: 0000000000000000 0000000080100010 00000001ffffffff 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff8000d7ca2200: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff8000d7ca2280: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb >ffff8000d7ca2300: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ^ ffff8000d7ca2380: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff8000d7ca2400: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ================================================================== [2] Actually, it is allocated for struct tuner, and dvb_frontend is inside. Signed-off-by: Chi Zhiling Signed-off-by: Hans Verkuil --- drivers/media/tuners/xc2028.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/tuners/xc2028.c b/drivers/media/tuners/xc2028.c index 5a967edceca9..352b8a3679b7 100644 --- a/drivers/media/tuners/xc2028.c +++ b/drivers/media/tuners/xc2028.c @@ -1361,9 +1361,16 @@ static void load_firmware_cb(const struct firmware *fw, void *context) { struct dvb_frontend *fe = context; - struct xc2028_data *priv = fe->tuner_priv; + struct xc2028_data *priv; int rc; + if (!fe) { + pr_warn("xc2028: No frontend in %s\n", __func__); + return; + } + + priv = fe->tuner_priv; + tuner_dbg("request_firmware_nowait(): %s\n", fw ? "OK" : "error"); if (!fw) { tuner_err("Could not load firmware %s.\n", priv->fname); -- cgit v1.2.3 From d2ae63c2f6a34e0104c046dcf5e03675867e0ad3 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 16 Jun 2024 08:32:53 +0200 Subject: media: cx231xx: Constify struct vb2_ops "struct vb2_ops" are not modified in this driver. Constifying this structure moves some data to a read-only section, so increase overall security. On a x86_64, with allmodconfig: Before: ====== text data bss dec hex filename 36607 1156 0 37763 9383 drivers/media/usb/cx231xx/cx231xx-417.o After: ===== text data bss dec hex filename 36735 1016 0 37751 9377 drivers/media/usb/cx231xx/cx231xx-417.o Signed-off-by: Christophe JAILLET Signed-off-by: Hans Verkuil --- drivers/media/usb/cx231xx/cx231xx-417.c | 2 +- drivers/media/usb/cx231xx/cx231xx-video.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index 343a4433ed24..abb967c8bd35 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -1453,7 +1453,7 @@ static void stop_streaming(struct vb2_queue *vq) return_all_buffers(dev, VB2_BUF_STATE_ERROR); } -static struct vb2_ops cx231xx_video_qops = { +static const struct vb2_ops cx231xx_video_qops = { .queue_setup = queue_setup, .buf_queue = buffer_queue, .start_streaming = start_streaming, diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 8f347bbeeb32..435eb0b32cb1 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -795,7 +795,7 @@ static void stop_streaming(struct vb2_queue *vq) return_all_buffers(dev, VB2_BUF_STATE_ERROR); } -static struct vb2_ops cx231xx_video_qops = { +static const struct vb2_ops cx231xx_video_qops = { .queue_setup = queue_setup, .buf_queue = buffer_queue, .start_streaming = start_streaming, -- cgit v1.2.3 From 1021dd010d212ccd770b89c7aff2e2031dc97619 Mon Sep 17 00:00:00 2001 From: Allen Pais Date: Tue, 18 Jun 2024 15:52:27 -0700 Subject: media: Convert from tasklet to BH workqueue The only generic interface to execute asynchronously in the BH context is tasklet; however, it's marked deprecated and has some design flaws. To replace tasklets, BH workqueue support was recently added. A BH workqueue behaves similarly to regular workqueues except that the queued work items are executed in the BH context. This patch converts drivers/media/* from tasklet to BH workqueue. Based on the work done by Tejun Heo Signed-off-by: Allen Pais Signed-off-by: Hans Verkuil --- drivers/media/pci/bt8xx/bt878.c | 8 ++--- drivers/media/pci/bt8xx/bt878.h | 3 +- drivers/media/pci/bt8xx/dvb-bt8xx.c | 8 ++--- drivers/media/pci/ddbridge/ddbridge.h | 2 +- drivers/media/pci/mantis/hopper_cards.c | 2 +- drivers/media/pci/mantis/mantis_cards.c | 2 +- drivers/media/pci/mantis/mantis_common.h | 2 +- drivers/media/pci/mantis/mantis_dma.c | 4 +-- drivers/media/pci/mantis/mantis_dma.h | 2 +- drivers/media/pci/mantis/mantis_dvb.c | 12 +++---- drivers/media/pci/ngene/ngene-core.c | 22 ++++++------ drivers/media/pci/ngene/ngene.h | 5 +-- drivers/media/pci/smipcie/smipcie-main.c | 18 +++++----- drivers/media/pci/smipcie/smipcie.h | 3 +- drivers/media/pci/ttpci/budget-av.c | 3 +- drivers/media/pci/ttpci/budget-ci.c | 27 ++++++++------- drivers/media/pci/ttpci/budget-core.c | 10 +++--- drivers/media/pci/ttpci/budget.h | 5 +-- drivers/media/pci/tw5864/tw5864-core.c | 2 +- drivers/media/pci/tw5864/tw5864-video.c | 13 +++---- drivers/media/pci/tw5864/tw5864.h | 7 ++-- drivers/media/platform/intel/pxa_camera.c | 15 ++++---- drivers/media/platform/marvell/mcam-core.c | 8 ++--- drivers/media/platform/marvell/mcam-core.h | 3 +- .../platform/st/sti/c8sectpfe/c8sectpfe-core.c | 14 ++++---- .../platform/st/sti/c8sectpfe/c8sectpfe-core.h | 2 +- drivers/media/radio/wl128x/fmdrv.h | 5 +-- drivers/media/radio/wl128x/fmdrv_common.c | 40 +++++++++++----------- drivers/media/rc/mceusb.c | 2 +- drivers/media/usb/ttusb-dec/ttusb_dec.c | 21 ++++++------ 30 files changed, 141 insertions(+), 129 deletions(-) diff --git a/drivers/media/pci/bt8xx/bt878.c b/drivers/media/pci/bt8xx/bt878.c index 5c9ba4bfc1be..62a6c4a80bed 100644 --- a/drivers/media/pci/bt8xx/bt878.c +++ b/drivers/media/pci/bt8xx/bt878.c @@ -300,8 +300,8 @@ static irqreturn_t bt878_irq(int irq, void *dev_id) } if (astat & BT878_ARISCI) { bt->finished_block = (stat & BT878_ARISCS) >> 28; - if (bt->tasklet.callback) - tasklet_schedule(&bt->tasklet); + if (bt->bh_work.func) + queue_work(system_bh_wq, &bt->bh_work); break; } count++; @@ -478,8 +478,8 @@ static int bt878_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) btwrite(0, BT878_AINT_MASK); bt878_num++; - if (!bt->tasklet.func) - tasklet_disable(&bt->tasklet); + if (!bt->bh_work.func) + disable_work_sync(&bt->bh_work); return 0; diff --git a/drivers/media/pci/bt8xx/bt878.h b/drivers/media/pci/bt8xx/bt878.h index fde8db293c54..5b1c7f56e553 100644 --- a/drivers/media/pci/bt8xx/bt878.h +++ b/drivers/media/pci/bt8xx/bt878.h @@ -14,6 +14,7 @@ #include #include #include +#include #include "bt848.h" #include "bttv.h" @@ -120,7 +121,7 @@ struct bt878 { dma_addr_t risc_dma; u32 risc_pos; - struct tasklet_struct tasklet; + struct work_struct bh_work; int shutdown; }; diff --git a/drivers/media/pci/bt8xx/dvb-bt8xx.c b/drivers/media/pci/bt8xx/dvb-bt8xx.c index 390cbba6c065..f0fbb468aea2 100644 --- a/drivers/media/pci/bt8xx/dvb-bt8xx.c +++ b/drivers/media/pci/bt8xx/dvb-bt8xx.c @@ -39,9 +39,9 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); #define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */ -static void dvb_bt8xx_task(struct tasklet_struct *t) +static void dvb_bt8xx_work(struct work_struct *t) { - struct bt878 *bt = from_tasklet(bt, t, tasklet); + struct bt878 *bt = from_work(bt, t, bh_work); struct dvb_bt8xx_card *card = dev_get_drvdata(&bt->adapter->dev); dprintk("%d\n", card->bt->finished_block); @@ -782,7 +782,7 @@ static int dvb_bt8xx_load_card(struct dvb_bt8xx_card *card, u32 type) goto err_disconnect_frontend; } - tasklet_setup(&card->bt->tasklet, dvb_bt8xx_task); + INIT_WORK(&card->bt->bh_work, dvb_bt8xx_work); frontend_init(card, type); @@ -922,7 +922,7 @@ static void dvb_bt8xx_remove(struct bttv_sub_device *sub) dprintk("dvb_bt8xx: unloading card%d\n", card->bttv_nr); bt878_stop(card->bt); - tasklet_kill(&card->bt->tasklet); + cancel_work_sync(&card->bt->bh_work); dvb_net_release(&card->dvbnet); card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_mem); card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw); diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h index f3699dbd193f..f01ecdb0b627 100644 --- a/drivers/media/pci/ddbridge/ddbridge.h +++ b/drivers/media/pci/ddbridge/ddbridge.h @@ -298,7 +298,7 @@ struct ddb_link { spinlock_t lock; /* lock link access */ struct mutex flash_mutex; /* lock flash access */ struct ddb_lnb lnb; - struct tasklet_struct tasklet; + struct work_struct bh_work; struct ddb_ids ids; spinlock_t temp_lock; /* lock temp chip access */ diff --git a/drivers/media/pci/mantis/hopper_cards.c b/drivers/media/pci/mantis/hopper_cards.c index c0bd5d7e148b..b85aef4e2b24 100644 --- a/drivers/media/pci/mantis/hopper_cards.c +++ b/drivers/media/pci/mantis/hopper_cards.c @@ -116,7 +116,7 @@ static irqreturn_t hopper_irq_handler(int irq, void *dev_id) if (stat & MANTIS_INT_RISCI) { dprintk(MANTIS_DEBUG, 0, "<%s>", label[8]); mantis->busy_block = (stat & MANTIS_INT_RISCSTAT) >> 28; - tasklet_schedule(&mantis->tasklet); + queue_work(system_bh_wq, &mantis->bh_work); } if (stat & MANTIS_INT_I2CDONE) { dprintk(MANTIS_DEBUG, 0, "<%s>", label[9]); diff --git a/drivers/media/pci/mantis/mantis_cards.c b/drivers/media/pci/mantis/mantis_cards.c index 906e4500d87d..b44b4cf42f86 100644 --- a/drivers/media/pci/mantis/mantis_cards.c +++ b/drivers/media/pci/mantis/mantis_cards.c @@ -125,7 +125,7 @@ static irqreturn_t mantis_irq_handler(int irq, void *dev_id) if (stat & MANTIS_INT_RISCI) { dprintk(MANTIS_DEBUG, 0, "<%s>", label[8]); mantis->busy_block = (stat & MANTIS_INT_RISCSTAT) >> 28; - tasklet_schedule(&mantis->tasklet); + queue_work(system_bh_wq, &mantis->bh_work); } if (stat & MANTIS_INT_I2CDONE) { dprintk(MANTIS_DEBUG, 0, "<%s>", label[9]); diff --git a/drivers/media/pci/mantis/mantis_common.h b/drivers/media/pci/mantis/mantis_common.h index d88ac280226c..6e563ecd94e8 100644 --- a/drivers/media/pci/mantis/mantis_common.h +++ b/drivers/media/pci/mantis/mantis_common.h @@ -125,7 +125,7 @@ struct mantis_pci { __le32 *risc_cpu; dma_addr_t risc_dma; - struct tasklet_struct tasklet; + struct work_struct bh_work; spinlock_t intmask_lock; struct i2c_adapter adapter; diff --git a/drivers/media/pci/mantis/mantis_dma.c b/drivers/media/pci/mantis/mantis_dma.c index 80c843936493..eeb210a2862f 100644 --- a/drivers/media/pci/mantis/mantis_dma.c +++ b/drivers/media/pci/mantis/mantis_dma.c @@ -200,9 +200,9 @@ void mantis_dma_stop(struct mantis_pci *mantis) } -void mantis_dma_xfer(struct tasklet_struct *t) +void mantis_dma_xfer(struct work_struct *t) { - struct mantis_pci *mantis = from_tasklet(mantis, t, tasklet); + struct mantis_pci *mantis = from_work(mantis, t, bh_work); struct mantis_hwconfig *config = mantis->hwconfig; while (mantis->last_block != mantis->busy_block) { diff --git a/drivers/media/pci/mantis/mantis_dma.h b/drivers/media/pci/mantis/mantis_dma.h index 37da982c9c29..5db0d3728f15 100644 --- a/drivers/media/pci/mantis/mantis_dma.h +++ b/drivers/media/pci/mantis/mantis_dma.h @@ -13,6 +13,6 @@ extern int mantis_dma_init(struct mantis_pci *mantis); extern int mantis_dma_exit(struct mantis_pci *mantis); extern void mantis_dma_start(struct mantis_pci *mantis); extern void mantis_dma_stop(struct mantis_pci *mantis); -extern void mantis_dma_xfer(struct tasklet_struct *t); +extern void mantis_dma_xfer(struct work_struct *t); #endif /* __MANTIS_DMA_H */ diff --git a/drivers/media/pci/mantis/mantis_dvb.c b/drivers/media/pci/mantis/mantis_dvb.c index c7ba4a76e608..398e32f44692 100644 --- a/drivers/media/pci/mantis/mantis_dvb.c +++ b/drivers/media/pci/mantis/mantis_dvb.c @@ -105,7 +105,7 @@ static int mantis_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed) if (mantis->feeds == 1) { dprintk(MANTIS_DEBUG, 1, "mantis start feed & dma"); mantis_dma_start(mantis); - tasklet_enable(&mantis->tasklet); + enable_and_queue_work(system_bh_wq, &mantis->bh_work); } return mantis->feeds; @@ -125,7 +125,7 @@ static int mantis_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) mantis->feeds--; if (mantis->feeds == 0) { dprintk(MANTIS_DEBUG, 1, "mantis stop feed and dma"); - tasklet_disable(&mantis->tasklet); + disable_work_sync(&mantis->bh_work); mantis_dma_stop(mantis); } @@ -205,8 +205,8 @@ int mantis_dvb_init(struct mantis_pci *mantis) } dvb_net_init(&mantis->dvb_adapter, &mantis->dvbnet, &mantis->demux.dmx); - tasklet_setup(&mantis->tasklet, mantis_dma_xfer); - tasklet_disable(&mantis->tasklet); + INIT_WORK(&mantis->bh_work, mantis_dma_xfer); + disable_work_sync(&mantis->bh_work); if (mantis->hwconfig) { result = config->frontend_init(mantis, mantis->fe); if (result < 0) { @@ -235,7 +235,7 @@ int mantis_dvb_init(struct mantis_pci *mantis) /* Error conditions .. */ err5: - tasklet_kill(&mantis->tasklet); + cancel_work_sync(&mantis->bh_work); dvb_net_release(&mantis->dvbnet); if (mantis->fe) { dvb_unregister_frontend(mantis->fe); @@ -273,7 +273,7 @@ int mantis_dvb_exit(struct mantis_pci *mantis) dvb_frontend_detach(mantis->fe); } - tasklet_kill(&mantis->tasklet); + cancel_work_sync(&mantis->bh_work); dvb_net_release(&mantis->dvbnet); mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem); diff --git a/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c index 24ec576dc3bf..db6796240bce 100644 --- a/drivers/media/pci/ngene/ngene-core.c +++ b/drivers/media/pci/ngene/ngene-core.c @@ -50,9 +50,9 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); /* nGene interrupt handler **************************************************/ /****************************************************************************/ -static void event_tasklet(struct tasklet_struct *t) +static void event_bh_work(struct work_struct *t) { - struct ngene *dev = from_tasklet(dev, t, event_tasklet); + struct ngene *dev = from_work(dev, t, event_bh_work); while (dev->EventQueueReadIndex != dev->EventQueueWriteIndex) { struct EVENT_BUFFER Event = @@ -68,9 +68,9 @@ static void event_tasklet(struct tasklet_struct *t) } } -static void demux_tasklet(struct tasklet_struct *t) +static void demux_bh_work(struct work_struct *t) { - struct ngene_channel *chan = from_tasklet(chan, t, demux_tasklet); + struct ngene_channel *chan = from_work(chan, t, demux_bh_work); struct device *pdev = &chan->dev->pci_dev->dev; struct SBufferHeader *Cur = chan->nextBuffer; @@ -204,7 +204,7 @@ static irqreturn_t irq_handler(int irq, void *dev_id) dev->EventQueueOverflowFlag = 1; } dev->EventBuffer->EventStatus &= ~0x80; - tasklet_schedule(&dev->event_tasklet); + queue_work(system_bh_wq, &dev->event_bh_work); rc = IRQ_HANDLED; } @@ -217,8 +217,8 @@ static irqreturn_t irq_handler(int irq, void *dev_id) ngeneBuffer.SR.Flags & 0xC0) == 0x80) { dev->channel[i].nextBuffer-> ngeneBuffer.SR.Flags |= 0x40; - tasklet_schedule( - &dev->channel[i].demux_tasklet); + queue_work(system_bh_wq, + &dev->channel[i].demux_bh_work); rc = IRQ_HANDLED; } } @@ -1181,7 +1181,7 @@ static void ngene_init(struct ngene *dev) struct device *pdev = &dev->pci_dev->dev; int i; - tasklet_setup(&dev->event_tasklet, event_tasklet); + INIT_WORK(&dev->event_bh_work, event_bh_work); memset_io(dev->iomem + 0xc000, 0x00, 0x220); memset_io(dev->iomem + 0xc400, 0x00, 0x100); @@ -1395,7 +1395,7 @@ static void release_channel(struct ngene_channel *chan) if (chan->running) set_transfer(chan, 0); - tasklet_kill(&chan->demux_tasklet); + cancel_work_sync(&chan->demux_bh_work); if (chan->ci_dev) { dvb_unregister_device(chan->ci_dev); @@ -1445,7 +1445,7 @@ static int init_channel(struct ngene_channel *chan) struct ngene_info *ni = dev->card_info; int io = ni->io_type[nr]; - tasklet_setup(&chan->demux_tasklet, demux_tasklet); + INIT_WORK(&chan->demux_bh_work, demux_bh_work); chan->users = 0; chan->type = io; chan->mode = chan->type; /* for now only one mode */ @@ -1649,7 +1649,7 @@ void ngene_remove(struct pci_dev *pdev) struct ngene *dev = pci_get_drvdata(pdev); int i; - tasklet_kill(&dev->event_tasklet); + cancel_work_sync(&dev->event_bh_work); for (i = MAX_STREAM - 1; i >= 0; i--) release_channel(&dev->channel[i]); if (dev->ci.en) diff --git a/drivers/media/pci/ngene/ngene.h b/drivers/media/pci/ngene/ngene.h index d1d7da84cd9d..9f989420b9e7 100644 --- a/drivers/media/pci/ngene/ngene.h +++ b/drivers/media/pci/ngene/ngene.h @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -621,7 +622,7 @@ struct ngene_channel { int users; struct video_device *v4l_dev; struct dvb_device *ci_dev; - struct tasklet_struct demux_tasklet; + struct work_struct demux_bh_work; struct SBufferHeader *nextBuffer; enum KSSTATE State; @@ -717,7 +718,7 @@ struct ngene { struct EVENT_BUFFER EventQueue[EVENT_QUEUE_SIZE]; int EventQueueOverflowCount; int EventQueueOverflowFlag; - struct tasklet_struct event_tasklet; + struct work_struct event_bh_work; struct EVENT_BUFFER *EventBuffer; int EventQueueWriteIndex; int EventQueueReadIndex; diff --git a/drivers/media/pci/smipcie/smipcie-main.c b/drivers/media/pci/smipcie/smipcie-main.c index 0c300d019d9c..7db6d443fc54 100644 --- a/drivers/media/pci/smipcie/smipcie-main.c +++ b/drivers/media/pci/smipcie/smipcie-main.c @@ -279,10 +279,10 @@ static void smi_port_clearInterrupt(struct smi_port *port) (port->_dmaInterruptCH0 | port->_dmaInterruptCH1)); } -/* tasklet handler: DMA data to dmx.*/ -static void smi_dma_xfer(struct tasklet_struct *t) +/* BH work handler: DMA data to dmx.*/ +static void smi_dma_xfer(struct work_struct *t) { - struct smi_port *port = from_tasklet(port, t, tasklet); + struct smi_port *port = from_work(port, t, bh_work); struct smi_dev *dev = port->dev; u32 intr_status, finishedData, dmaManagement; u8 dmaChan0State, dmaChan1State; @@ -426,8 +426,8 @@ static int smi_port_init(struct smi_port *port, int dmaChanUsed) } smi_port_disableInterrupt(port); - tasklet_setup(&port->tasklet, smi_dma_xfer); - tasklet_disable(&port->tasklet); + INIT_WORK(&port->bh_work, smi_dma_xfer); + disable_work_sync(&port->bh_work); port->enable = 1; return 0; err: @@ -438,7 +438,7 @@ err: static void smi_port_exit(struct smi_port *port) { smi_port_disableInterrupt(port); - tasklet_kill(&port->tasklet); + cancel_work_sync(&port->bh_work); smi_port_dma_free(port); port->enable = 0; } @@ -452,7 +452,7 @@ static int smi_port_irq(struct smi_port *port, u32 int_status) smi_port_disableInterrupt(port); port->_int_status = int_status; smi_port_clearInterrupt(port); - tasklet_schedule(&port->tasklet); + queue_work(system_bh_wq, &port->bh_work); handled = 1; } return handled; @@ -823,7 +823,7 @@ static int smi_start_feed(struct dvb_demux_feed *dvbdmxfeed) smi_port_clearInterrupt(port); smi_port_enableInterrupt(port); smi_write(port->DMA_MANAGEMENT, dmaManagement); - tasklet_enable(&port->tasklet); + enable_and_queue_work(system_bh_wq, &port->bh_work); } return port->users; } @@ -837,7 +837,7 @@ static int smi_stop_feed(struct dvb_demux_feed *dvbdmxfeed) if (--port->users) return port->users; - tasklet_disable(&port->tasklet); + disable_work_sync(&port->bh_work); smi_port_disableInterrupt(port); smi_clear(port->DMA_MANAGEMENT, 0x30003); return 0; diff --git a/drivers/media/pci/smipcie/smipcie.h b/drivers/media/pci/smipcie/smipcie.h index 2b5e0154814c..98e44edaab9e 100644 --- a/drivers/media/pci/smipcie/smipcie.h +++ b/drivers/media/pci/smipcie/smipcie.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -257,7 +258,7 @@ struct smi_port { u32 _dmaInterruptCH0; u32 _dmaInterruptCH1; u32 _int_status; - struct tasklet_struct tasklet; + struct work_struct bh_work; /* dvb */ struct dmx_frontend hw_frontend; struct dmx_frontend mem_frontend; diff --git a/drivers/media/pci/ttpci/budget-av.c b/drivers/media/pci/ttpci/budget-av.c index 2e62c938e2cb..69f5e810f9b5 100644 --- a/drivers/media/pci/ttpci/budget-av.c +++ b/drivers/media/pci/ttpci/budget-av.c @@ -36,6 +36,7 @@ #include #include #include +#include #include @@ -54,7 +55,7 @@ struct budget_av { struct video_device vd; int cur_input; int has_saa7113; - struct tasklet_struct ciintf_irq_tasklet; + struct work_struct ciintf_irq_bh_work; int slot_status; struct dvb_ca_en50221 ca; u8 reinitialise_demod:1; diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c index 76de40e3c802..33f08adf4feb 100644 --- a/drivers/media/pci/ttpci/budget-ci.c +++ b/drivers/media/pci/ttpci/budget-ci.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "budget.h" @@ -81,7 +82,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); struct budget_ci_ir { struct rc_dev *dev; - struct tasklet_struct msp430_irq_tasklet; + struct work_struct msp430_irq_bh_work; char name[72]; /* 40 + 32 for (struct saa7146_dev).name */ char phys[32]; int rc5_device; @@ -92,7 +93,7 @@ struct budget_ci_ir { struct budget_ci { struct budget budget; - struct tasklet_struct ciintf_irq_tasklet; + struct work_struct ciintf_irq_bh_work; int slot_status; int ci_irq; struct dvb_ca_en50221 ca; @@ -100,9 +101,9 @@ struct budget_ci { u8 tuner_pll_address; /* used for philips_tdm1316l configs */ }; -static void msp430_ir_interrupt(struct tasklet_struct *t) +static void msp430_ir_interrupt(struct work_struct *t) { - struct budget_ci_ir *ir = from_tasklet(ir, t, msp430_irq_tasklet); + struct budget_ci_ir *ir = from_work(ir, t, msp430_irq_bh_work); struct budget_ci *budget_ci = container_of(ir, typeof(*budget_ci), ir); struct rc_dev *dev = budget_ci->ir.dev; u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8; @@ -231,7 +232,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci) budget_ci->ir.dev = dev; - tasklet_setup(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt); + INIT_WORK(&budget_ci->ir.msp430_irq_bh_work, msp430_ir_interrupt); SAA7146_IER_ENABLE(saa, MASK_06); saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI); @@ -245,7 +246,7 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci) SAA7146_IER_DISABLE(saa, MASK_06); saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); - tasklet_kill(&budget_ci->ir.msp430_irq_tasklet); + cancel_work_sync(&budget_ci->ir.msp430_irq_bh_work); rc_unregister_device(budget_ci->ir.dev); } @@ -349,10 +350,10 @@ static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) return 0; } -static void ciintf_interrupt(struct tasklet_struct *t) +static void ciintf_interrupt(struct work_struct *t) { - struct budget_ci *budget_ci = from_tasklet(budget_ci, t, - ciintf_irq_tasklet); + struct budget_ci *budget_ci = from_work(budget_ci, t, + ciintf_irq_bh_work); struct saa7146_dev *saa = budget_ci->budget.dev; unsigned int flags; @@ -491,7 +492,7 @@ static int ciintf_init(struct budget_ci *budget_ci) // Setup CI slot IRQ if (budget_ci->ci_irq) { - tasklet_setup(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt); + INIT_WORK(&budget_ci->ciintf_irq_bh_work, ciintf_interrupt); if (budget_ci->slot_status != SLOTSTATUS_NONE) saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); else @@ -530,7 +531,7 @@ static void ciintf_deinit(struct budget_ci *budget_ci) if (budget_ci->ci_irq) { SAA7146_IER_DISABLE(saa, MASK_03); saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); - tasklet_kill(&budget_ci->ciintf_irq_tasklet); + cancel_work_sync(&budget_ci->ciintf_irq_bh_work); } // reset interface @@ -556,13 +557,13 @@ static void budget_ci_irq(struct saa7146_dev *dev, u32 *isr) dprintk(8, "dev: %p, budget_ci: %p\n", dev, budget_ci); if (*isr & MASK_06) - tasklet_schedule(&budget_ci->ir.msp430_irq_tasklet); + queue_work(system_bh_wq, &budget_ci->ir.msp430_irq_bh_work); if (*isr & MASK_10) ttpci_budget_irq10_handler(dev, isr); if ((*isr & MASK_03) && (budget_ci->budget.ci_present) && (budget_ci->ci_irq)) - tasklet_schedule(&budget_ci->ciintf_irq_tasklet); + queue_work(system_bh_wq, &budget_ci->ciintf_irq_bh_work); } static u8 philips_su1278_tt_inittab[] = { diff --git a/drivers/media/pci/ttpci/budget-core.c b/drivers/media/pci/ttpci/budget-core.c index 6d41fc997655..29531d9c9db0 100644 --- a/drivers/media/pci/ttpci/budget-core.c +++ b/drivers/media/pci/ttpci/budget-core.c @@ -172,9 +172,9 @@ static int budget_read_fe_status(struct dvb_frontend *fe, return ret; } -static void vpeirq(struct tasklet_struct *t) +static void vpeirq(struct work_struct *t) { - struct budget *budget = from_tasklet(budget, t, vpe_tasklet); + struct budget *budget = from_work(budget, t, vpe_bh_work); u8 *mem = (u8 *) (budget->grabbing); u32 olddma = budget->ttbp; u32 newdma = saa7146_read(budget->dev, PCI_VDP3); @@ -525,7 +525,7 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, /* upload all */ saa7146_write(dev, GPIO_CTRL, 0x000000); - tasklet_setup(&budget->vpe_tasklet, vpeirq); + INIT_WORK(&budget->vpe_bh_work, vpeirq); /* frontend power on */ if (bi->type != BUDGET_FS_ACTIVY) @@ -565,7 +565,7 @@ int ttpci_budget_deinit(struct budget *budget) budget_unregister(budget); - tasklet_kill(&budget->vpe_tasklet); + cancel_work_sync(&budget->vpe_bh_work); saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt); @@ -584,7 +584,7 @@ void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 *isr) dprintk(8, "dev: %p, budget: %p\n", dev, budget); if (*isr & MASK_10) - tasklet_schedule(&budget->vpe_tasklet); + queue_work(system_bh_wq, &budget->vpe_bh_work); } EXPORT_SYMBOL_GPL(ttpci_budget_irq10_handler); diff --git a/drivers/media/pci/ttpci/budget.h b/drivers/media/pci/ttpci/budget.h index 83ead34dc766..e933764ac4e3 100644 --- a/drivers/media/pci/ttpci/budget.h +++ b/drivers/media/pci/ttpci/budget.h @@ -18,6 +18,7 @@ #include #include +#include #include @@ -54,8 +55,8 @@ struct budget { unsigned char *grabbing; struct saa7146_pgtable pt; - struct tasklet_struct fidb_tasklet; - struct tasklet_struct vpe_tasklet; + struct work_struct fidb_bh_work; + struct work_struct vpe_bh_work; struct dmxdev dmxdev; struct dvb_demux demux; diff --git a/drivers/media/pci/tw5864/tw5864-core.c b/drivers/media/pci/tw5864/tw5864-core.c index 560ff1ddcc83..4d33caf83307 100644 --- a/drivers/media/pci/tw5864/tw5864-core.c +++ b/drivers/media/pci/tw5864/tw5864-core.c @@ -144,7 +144,7 @@ static void tw5864_h264_isr(struct tw5864_dev *dev) cur_frame->gop_seqno = input->frame_gop_seqno; dev->h264_buf_w_index = next_frame_index; - tasklet_schedule(&dev->tasklet); + queue_work(system_bh_wq, &dev->bh_work); cur_frame = next_frame; diff --git a/drivers/media/pci/tw5864/tw5864-video.c b/drivers/media/pci/tw5864/tw5864-video.c index 8b1aae4b6319..4f35c159efe5 100644 --- a/drivers/media/pci/tw5864/tw5864-video.c +++ b/drivers/media/pci/tw5864/tw5864-video.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -175,7 +176,7 @@ static const unsigned int intra4x4_lambda3[] = { static v4l2_std_id tw5864_get_v4l2_std(enum tw5864_vid_std std); static enum tw5864_vid_std tw5864_from_v4l2_std(v4l2_std_id v4l2_std); -static void tw5864_handle_frame_task(struct tasklet_struct *t); +static void tw5864_handle_frame_work(struct work_struct *t); static void tw5864_handle_frame(struct tw5864_h264_frame *frame); static void tw5864_frame_interval_set(struct tw5864_input *input); @@ -1062,7 +1063,7 @@ int tw5864_video_init(struct tw5864_dev *dev, int *video_nr) dev->irqmask |= TW5864_INTR_VLC_DONE | TW5864_INTR_TIMER; tw5864_irqmask_apply(dev); - tasklet_setup(&dev->tasklet, tw5864_handle_frame_task); + INIT_WORK(&dev->bh_work, tw5864_handle_frame_work); for (i = 0; i < TW5864_INPUTS; i++) { dev->inputs[i].root = dev; @@ -1079,7 +1080,7 @@ fini_video_inputs: for (i = last_input_nr_registered; i >= 0; i--) tw5864_video_input_fini(&dev->inputs[i]); - tasklet_kill(&dev->tasklet); + cancel_work_sync(&dev->bh_work); free_dma: for (i = last_dma_allocated; i >= 0; i--) { @@ -1198,7 +1199,7 @@ void tw5864_video_fini(struct tw5864_dev *dev) { int i; - tasklet_kill(&dev->tasklet); + cancel_work_sync(&dev->bh_work); for (i = 0; i < TW5864_INPUTS; i++) tw5864_video_input_fini(&dev->inputs[i]); @@ -1315,9 +1316,9 @@ static int tw5864_is_motion_triggered(struct tw5864_h264_frame *frame) return detected; } -static void tw5864_handle_frame_task(struct tasklet_struct *t) +static void tw5864_handle_frame_work(struct work_struct *t) { - struct tw5864_dev *dev = from_tasklet(dev, t, tasklet); + struct tw5864_dev *dev = from_work(dev, t, bh_work); unsigned long flags; int batch_size = H264_BUF_CNT; diff --git a/drivers/media/pci/tw5864/tw5864.h b/drivers/media/pci/tw5864/tw5864.h index a8b6fbd5b710..2da5f4215fd9 100644 --- a/drivers/media/pci/tw5864/tw5864.h +++ b/drivers/media/pci/tw5864/tw5864.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -85,7 +86,7 @@ struct tw5864_input { int nr; /* input number */ struct tw5864_dev *root; struct mutex lock; /* used for vidq and vdev */ - spinlock_t slock; /* used for sync between ISR, tasklet & V4L2 API */ + spinlock_t slock; /* used for sync between ISR, bh_work & V4L2 API */ struct video_device vdev; struct v4l2_ctrl_handler hdl; struct vb2_queue vidq; @@ -142,7 +143,7 @@ struct tw5864_h264_frame { /* global device status */ struct tw5864_dev { - spinlock_t slock; /* used for sync between ISR, tasklet & V4L2 API */ + spinlock_t slock; /* used for sync between ISR, bh_work & V4L2 API */ struct v4l2_device v4l2_dev; struct tw5864_input inputs[TW5864_INPUTS]; #define H264_BUF_CNT 4 @@ -150,7 +151,7 @@ struct tw5864_dev { int h264_buf_r_index; int h264_buf_w_index; - struct tasklet_struct tasklet; + struct work_struct bh_work; int encoder_busy; /* Input number to check next for ready raw picture (in RR fashion) */ diff --git a/drivers/media/platform/intel/pxa_camera.c b/drivers/media/platform/intel/pxa_camera.c index d904952bf00e..f118aaac0b38 100644 --- a/drivers/media/platform/intel/pxa_camera.c +++ b/drivers/media/platform/intel/pxa_camera.c @@ -43,6 +43,7 @@ #include #include +#include #define PXA_CAM_VERSION "0.0.6" #define PXA_CAM_DRV_NAME "pxa27x-camera" @@ -683,7 +684,7 @@ struct pxa_camera_dev { unsigned int buf_sequence; struct pxa_buffer *active; - struct tasklet_struct task_eof; + struct work_struct eof_bh_work; u32 save_cicr[5]; }; @@ -1146,9 +1147,9 @@ static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev) clk_disable_unprepare(pcdev->clk); } -static void pxa_camera_eof(struct tasklet_struct *t) +static void pxa_camera_eof_bh_work(struct work_struct *t) { - struct pxa_camera_dev *pcdev = from_tasklet(pcdev, t, task_eof); + struct pxa_camera_dev *pcdev = from_work(pcdev, t, eof_bh_work); unsigned long cifr; struct pxa_buffer *buf; @@ -1185,7 +1186,7 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) if (status & CISR_EOF) { cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_EOFM; __raw_writel(cicr0, pcdev->base + CICR0); - tasklet_schedule(&pcdev->task_eof); + queue_work(system_bh_wq, &pcdev->eof_bh_work); } return IRQ_HANDLED; @@ -2383,7 +2384,7 @@ static int pxa_camera_probe(struct platform_device *pdev) } } - tasklet_setup(&pcdev->task_eof, pxa_camera_eof); + INIT_WORK(&pcdev->eof_bh_work, pxa_camera_eof_bh_work); pxa_camera_activate(pcdev); @@ -2409,7 +2410,7 @@ static int pxa_camera_probe(struct platform_device *pdev) return 0; exit_deactivate: pxa_camera_deactivate(pcdev); - tasklet_kill(&pcdev->task_eof); + cancel_work_sync(&pcdev->eof_bh_work); exit_free_dma: dma_release_channel(pcdev->dma_chans[2]); exit_free_dma_u: @@ -2428,7 +2429,7 @@ static void pxa_camera_remove(struct platform_device *pdev) struct pxa_camera_dev *pcdev = platform_get_drvdata(pdev); pxa_camera_deactivate(pcdev); - tasklet_kill(&pcdev->task_eof); + cancel_work_sync(&pcdev->eof_bh_work); dma_release_channel(pcdev->dma_chans[0]); dma_release_channel(pcdev->dma_chans[1]); dma_release_channel(pcdev->dma_chans[2]); diff --git a/drivers/media/platform/marvell/mcam-core.c b/drivers/media/platform/marvell/mcam-core.c index 9dfaea56a46d..c81593c969e0 100644 --- a/drivers/media/platform/marvell/mcam-core.c +++ b/drivers/media/platform/marvell/mcam-core.c @@ -439,9 +439,9 @@ static void mcam_ctlr_dma_vmalloc(struct mcam_camera *cam) /* * Copy data out to user space in the vmalloc case */ -static void mcam_frame_tasklet(struct tasklet_struct *t) +static void mcam_frame_work(struct work_struct *t) { - struct mcam_camera *cam = from_tasklet(cam, t, s_tasklet); + struct mcam_camera *cam = from_work(cam, t, s_bh_work); int i; unsigned long flags; struct mcam_vb_buffer *buf; @@ -493,7 +493,7 @@ static int mcam_check_dma_buffers(struct mcam_camera *cam) static void mcam_vmalloc_done(struct mcam_camera *cam, int frame) { - tasklet_schedule(&cam->s_tasklet); + queue_work(system_bh_wq, &cam->s_bh_work); } #else /* MCAM_MODE_VMALLOC */ @@ -1305,7 +1305,7 @@ static int mcam_setup_vb2(struct mcam_camera *cam) break; case B_vmalloc: #ifdef MCAM_MODE_VMALLOC - tasklet_setup(&cam->s_tasklet, mcam_frame_tasklet); + INIT_WORK(&cam->s_bh_work, mcam_frame_work); vq->ops = &mcam_vb2_ops; vq->mem_ops = &vb2_vmalloc_memops; cam->dma_setup = mcam_ctlr_dma_vmalloc; diff --git a/drivers/media/platform/marvell/mcam-core.h b/drivers/media/platform/marvell/mcam-core.h index 51e66db45af6..989dc6859a53 100644 --- a/drivers/media/platform/marvell/mcam-core.h +++ b/drivers/media/platform/marvell/mcam-core.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -167,7 +168,7 @@ struct mcam_camera { unsigned int dma_buf_size; /* allocated size */ void *dma_bufs[MAX_DMA_BUFS]; /* Internal buffer addresses */ dma_addr_t dma_handles[MAX_DMA_BUFS]; /* Buffer bus addresses */ - struct tasklet_struct s_tasklet; + struct work_struct s_bh_work; #endif unsigned int sequence; /* Frame sequence number */ unsigned int buf_seq[MAX_DMA_BUFS]; /* Sequence for individual bufs */ diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c index 2f58a0d0df85..67d3d6e50d2e 100644 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c @@ -72,16 +72,16 @@ static void c8sectpfe_timer_interrupt(struct timer_list *t) /* is this descriptor initialised and TP enabled */ if (channel->irec && readl(channel->irec + DMA_PRDS_TPENABLE)) - tasklet_schedule(&channel->tsklet); + queue_work(system_bh_wq, &channel->bh_work); } fei->timer.expires = jiffies + msecs_to_jiffies(POLL_MSECS); add_timer(&fei->timer); } -static void channel_swdemux_tsklet(struct tasklet_struct *t) +static void channel_swdemux_bh_work(struct work_struct *t) { - struct channel_info *channel = from_tasklet(channel, t, tsklet); + struct channel_info *channel = from_work(channel, t, bh_work); struct c8sectpfei *fei; unsigned long wp, rp; int pos, num_packets, n, size; @@ -210,7 +210,7 @@ static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed) dev_dbg(fei->dev, "Starting channel=%p\n", channel); - tasklet_setup(&channel->tsklet, channel_swdemux_tsklet); + INIT_WORK(&channel->bh_work, channel_swdemux_bh_work); /* Reset the internal inputblock sram pointers */ writel(channel->fifo, @@ -303,7 +303,7 @@ static int c8sectpfe_stop_feed(struct dvb_demux_feed *dvbdmxfeed) /* disable this channels descriptor */ writel(0, channel->irec + DMA_PRDS_TPENABLE); - tasklet_disable(&channel->tsklet); + disable_work_sync(&channel->bh_work); /* now request memdma channel goes idle */ idlereq = (1 << channel->tsin_id) | IDLEREQ; @@ -630,8 +630,8 @@ static int configure_memdma_and_inputblock(struct c8sectpfei *fei, 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); + /* initialize bh work */ + INIT_WORK(&tsin->bh_work, channel_swdemux_bh_work); return 0; diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h index bf377cc82225..c1b124c6ef12 100644 --- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h +++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h @@ -51,7 +51,7 @@ struct channel_info { unsigned long fifo; struct completion idle_completion; - struct tasklet_struct tsklet; + struct work_struct bh_work; struct c8sectpfei *fei; void __iomem *irec; diff --git a/drivers/media/radio/wl128x/fmdrv.h b/drivers/media/radio/wl128x/fmdrv.h index da8920169df8..03117a41dbd4 100644 --- a/drivers/media/radio/wl128x/fmdrv.h +++ b/drivers/media/radio/wl128x/fmdrv.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -200,10 +201,10 @@ struct fmdev { int streg_cbdata; /* status of ST registration */ struct sk_buff_head rx_q; /* RX queue */ - struct tasklet_struct rx_task; /* RX Tasklet */ + struct work_struct rx_bh_work; /* RX BH Work */ struct sk_buff_head tx_q; /* TX queue */ - struct tasklet_struct tx_task; /* TX Tasklet */ + struct work_struct tx_bh_work; /* TX BH Work */ unsigned long last_tx_jiffies; /* Timestamp of last pkt sent */ atomic_t tx_cnt; /* Number of packets can send at a time */ diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c index 3da8e5102bec..3d36f323a8f8 100644 --- a/drivers/media/radio/wl128x/fmdrv_common.c +++ b/drivers/media/radio/wl128x/fmdrv_common.c @@ -9,7 +9,7 @@ * one Channel-8 command to be sent to the chip). * 2) Sending each Channel-8 command to the chip and reading * response back over Shared Transport. - * 3) Managing TX and RX Queues and Tasklets. + * 3) Managing TX and RX Queues and BH bh Works. * 4) Handling FM Interrupt packet and taking appropriate action. * 5) Loading FM firmware to the chip (common, FM TX, and FM RX * firmware files based on mode selection) @@ -244,10 +244,10 @@ void fmc_update_region_info(struct fmdev *fmdev, u8 region_to_set) } /* - * FM common sub-module will schedule this tasklet whenever it receives + * FM common sub-module will queue this bh work whenever it receives * FM packet from ST driver. */ -static void recv_tasklet(struct tasklet_struct *t) +static void recv_bh_work(struct work_struct *t) { struct fmdev *fmdev; struct fm_irq *irq_info; @@ -256,7 +256,7 @@ static void recv_tasklet(struct tasklet_struct *t) u8 num_fm_hci_cmds; unsigned long flags; - fmdev = from_tasklet(fmdev, t, tx_task); + fmdev = from_work(fmdev, t, tx_bh_work); irq_info = &fmdev->irq_info; /* Process all packets in the RX queue */ while ((skb = skb_dequeue(&fmdev->rx_q))) { @@ -322,22 +322,22 @@ static void recv_tasklet(struct tasklet_struct *t) /* * Check flow control field. If Num_FM_HCI_Commands field is - * not zero, schedule FM TX tasklet. + * not zero, queue FM TX bh work. */ if (num_fm_hci_cmds && atomic_read(&fmdev->tx_cnt)) if (!skb_queue_empty(&fmdev->tx_q)) - tasklet_schedule(&fmdev->tx_task); + queue_work(system_bh_wq, &fmdev->tx_bh_work); } } -/* FM send tasklet: is scheduled when FM packet has to be sent to chip */ -static void send_tasklet(struct tasklet_struct *t) +/* FM send_bh_work: is scheduled when FM packet has to be sent to chip */ +static void send_bh_work(struct work_struct *t) { struct fmdev *fmdev; struct sk_buff *skb; int len; - fmdev = from_tasklet(fmdev, t, tx_task); + fmdev = from_work(fmdev, t, tx_bh_work); if (!atomic_read(&fmdev->tx_cnt)) return; @@ -366,7 +366,7 @@ static void send_tasklet(struct tasklet_struct *t) if (len < 0) { kfree_skb(skb); fmdev->resp_comp = NULL; - fmerr("TX tasklet failed to send skb(%p)\n", skb); + fmerr("TX bh work failed to send skb(%p)\n", skb); atomic_set(&fmdev->tx_cnt, 1); } else { fmdev->last_tx_jiffies = jiffies; @@ -374,7 +374,7 @@ static void send_tasklet(struct tasklet_struct *t) } /* - * Queues FM Channel-8 packet to FM TX queue and schedules FM TX tasklet for + * Queues FM Channel-8 packet to FM TX queue and schedules FM TX bh work for * transmission */ static int fm_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type, void *payload, @@ -440,7 +440,7 @@ static int fm_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type, void *payload, fm_cb(skb)->completion = wait_completion; skb_queue_tail(&fmdev->tx_q, skb); - tasklet_schedule(&fmdev->tx_task); + queue_work(system_bh_wq, &fmdev->tx_bh_work); return 0; } @@ -462,7 +462,7 @@ int fmc_send_cmd(struct fmdev *fmdev, u8 fm_op, u16 type, void *payload, if (!wait_for_completion_timeout(&fmdev->maintask_comp, FM_DRV_TX_TIMEOUT)) { - fmerr("Timeout(%d sec),didn't get regcompletion signal from RX tasklet\n", + fmerr("Timeout(%d sec),didn't get regcompletion signal from RX bh work\n", jiffies_to_msecs(FM_DRV_TX_TIMEOUT) / 1000); return -ETIMEDOUT; } @@ -1455,7 +1455,7 @@ static long fm_st_receive(void *arg, struct sk_buff *skb) memcpy(skb_push(skb, 1), &skb->cb[0], 1); skb_queue_tail(&fmdev->rx_q, skb); - tasklet_schedule(&fmdev->rx_task); + queue_work(system_bh_wq, &fmdev->rx_bh_work); return 0; } @@ -1537,13 +1537,13 @@ int fmc_prepare(struct fmdev *fmdev) spin_lock_init(&fmdev->rds_buff_lock); spin_lock_init(&fmdev->resp_skb_lock); - /* Initialize TX queue and TX tasklet */ + /* Initialize TX queue and TX bh work */ skb_queue_head_init(&fmdev->tx_q); - tasklet_setup(&fmdev->tx_task, send_tasklet); + INIT_WORK(&fmdev->tx_bh_work, send_bh_work); - /* Initialize RX Queue and RX tasklet */ + /* Initialize RX Queue and RX bh work */ skb_queue_head_init(&fmdev->rx_q); - tasklet_setup(&fmdev->rx_task, recv_tasklet); + INIT_WORK(&fmdev->rx_bh_work, recv_bh_work); fmdev->irq_info.stage = 0; atomic_set(&fmdev->tx_cnt, 1); @@ -1589,8 +1589,8 @@ int fmc_release(struct fmdev *fmdev) /* Service pending read */ wake_up_interruptible(&fmdev->rx.rds.read_queue); - tasklet_kill(&fmdev->tx_task); - tasklet_kill(&fmdev->rx_task); + cancel_work_sync(&fmdev->tx_bh_work); + cancel_work_sync(&fmdev->rx_bh_work); skb_queue_purge(&fmdev->tx_q); skb_queue_purge(&fmdev->rx_q); diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 615f48898300..cd7af4d88b7f 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -773,7 +773,7 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, u8 *buf, int buf_len, /* * Schedule work that can't be done in interrupt handlers - * (mceusb_dev_recv() and mce_write_callback()) nor tasklets. + * (mceusb_dev_recv() and mce_write_callback()) nor BH work. * Invokes mceusb_deferred_kevent() for recovering from * error events specified by the kevent bit field. */ diff --git a/drivers/media/usb/ttusb-dec/ttusb_dec.c b/drivers/media/usb/ttusb-dec/ttusb_dec.c index 79faa2560613..b4575fe89c95 100644 --- a/drivers/media/usb/ttusb-dec/ttusb_dec.c +++ b/drivers/media/usb/ttusb-dec/ttusb_dec.c @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -139,7 +140,7 @@ struct ttusb_dec { int v_pes_postbytes; struct list_head urb_frame_list; - struct tasklet_struct urb_tasklet; + struct work_struct urb_bh_work; spinlock_t urb_frame_list_lock; struct dvb_demux_filter *audio_filter; @@ -766,9 +767,9 @@ static void ttusb_dec_process_urb_frame(struct ttusb_dec *dec, u8 *b, } } -static void ttusb_dec_process_urb_frame_list(struct tasklet_struct *t) +static void ttusb_dec_process_urb_frame_list(struct work_struct *t) { - struct ttusb_dec *dec = from_tasklet(dec, t, urb_tasklet); + struct ttusb_dec *dec = from_work(dec, t, urb_bh_work); struct list_head *item; struct urb_frame *frame; unsigned long flags; @@ -822,7 +823,7 @@ static void ttusb_dec_process_urb(struct urb *urb) spin_unlock_irqrestore(&dec->urb_frame_list_lock, flags); - tasklet_schedule(&dec->urb_tasklet); + queue_work(system_bh_wq, &dec->urb_bh_work); } } } else { @@ -1198,11 +1199,11 @@ static int ttusb_dec_alloc_iso_urbs(struct ttusb_dec *dec) return 0; } -static void ttusb_dec_init_tasklet(struct ttusb_dec *dec) +static void ttusb_dec_init_bh_work(struct ttusb_dec *dec) { spin_lock_init(&dec->urb_frame_list_lock); INIT_LIST_HEAD(&dec->urb_frame_list); - tasklet_setup(&dec->urb_tasklet, ttusb_dec_process_urb_frame_list); + INIT_WORK(&dec->urb_bh_work, ttusb_dec_process_urb_frame_list); } static int ttusb_init_rc( struct ttusb_dec *dec) @@ -1588,12 +1589,12 @@ static void ttusb_dec_exit_usb(struct ttusb_dec *dec) ttusb_dec_free_iso_urbs(dec); } -static void ttusb_dec_exit_tasklet(struct ttusb_dec *dec) +static void ttusb_dec_exit_bh_work(struct ttusb_dec *dec) { struct list_head *item; struct urb_frame *frame; - tasklet_kill(&dec->urb_tasklet); + cancel_work_sync(&dec->urb_bh_work); while ((item = dec->urb_frame_list.next) != &dec->urb_frame_list) { frame = list_entry(item, struct urb_frame, urb_frame_list); @@ -1703,7 +1704,7 @@ static int ttusb_dec_probe(struct usb_interface *intf, ttusb_dec_init_v_pes(dec); ttusb_dec_init_filters(dec); - ttusb_dec_init_tasklet(dec); + ttusb_dec_init_bh_work(dec); dec->active = 1; @@ -1729,7 +1730,7 @@ static void ttusb_dec_disconnect(struct usb_interface *intf) dprintk("%s\n", __func__); if (dec->active) { - ttusb_dec_exit_tasklet(dec); + ttusb_dec_exit_bh_work(dec); ttusb_dec_exit_filters(dec); if(enable_rc) ttusb_dec_exit_rc(dec); -- cgit v1.2.3 From f10edd839eb8cef29c1929a2e224b0a9d4132074 Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Thu, 19 Oct 2023 16:37:35 +0800 Subject: media: imx-jpeg: Remove some redundant error logs If the picture size parsed by decoder is different from those previously established, it's a normal flow of dynamic resolution change, not an error case, the log may mislead that some error occurs in decoding, so remove the error log in this case. Fixes: 2db16c6ed72c ("media: imx-jpeg: Add V4L2 driver for i.MX8 JPEG Encoder/Decoder") Signed-off-by: Ming Qian Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index cc97790ed30f..de07d2716fc3 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -1840,17 +1840,6 @@ static int mxc_jpeg_parse(struct mxc_jpeg_ctx *ctx, struct vb2_buffer *vb) q_data_out = mxc_jpeg_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - if (q_data_out->w == 0 && q_data_out->h == 0) { - dev_warn(dev, "Invalid user resolution 0x0"); - dev_warn(dev, "Keeping resolution from JPEG: %dx%d", - header.frame.width, header.frame.height); - } else if (header.frame.width != q_data_out->w || - header.frame.height != q_data_out->h) { - dev_err(dev, - "Resolution mismatch: %dx%d (JPEG) versus %dx%d(user)", - header.frame.width, header.frame.height, - q_data_out->w, q_data_out->h); - } q_data_out->w = header.frame.width; q_data_out->h = header.frame.height; if (header.frame.width > MXC_JPEG_MAX_WIDTH || -- cgit v1.2.3 From a8fb5fce7a441d37d106c82235e1f1b57f70f5b9 Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Thu, 19 Oct 2023 16:36:36 +0800 Subject: media: imx-jpeg: Drop initial source change event if capture has been setup In section 4.5.1.5. Initialization, the step 4 may be skipped and continue with the Capture Setup sequence, so if the capture has been setup, there is no need to trigger the initial source change event, just start decoding, and follow the dynamic resolution change flow if the configured values do not match those parsed by the decoder. And it won't fail the gstreamer pipeline. Fixes: b833b178498d ("media: imx-jpeg: notify source chagne event when the first picture parsed") Signed-off-by: Ming Qian Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c index de07d2716fc3..1d8913813037 100644 --- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c +++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c @@ -1634,6 +1634,9 @@ static int mxc_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) dev_dbg(ctx->mxc_jpeg->dev, "Start streaming ctx=%p", ctx); q_data->sequence = 0; + if (V4L2_TYPE_IS_CAPTURE(q->type)) + ctx->need_initial_source_change_evt = false; + ret = pm_runtime_resume_and_get(ctx->mxc_jpeg->dev); if (ret < 0) { dev_err(ctx->mxc_jpeg->dev, "Failed to power up jpeg\n"); -- cgit v1.2.3 From 143e7ab4d9a0dd84acbd8f666993d6ea921e076b Mon Sep 17 00:00:00 2001 From: Ivan Bornyakov Date: Mon, 15 Apr 2024 13:07:19 +0300 Subject: media: chips-media: wave5: support decoding HEVC Main10 profile Add support for decoding HEVC Main10 profile by scaling the FBC buffer stride and size by a factor of (bitdepth / 8). Signed-off-by: Ivan Bornyakov Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- .../platform/chips-media/wave5/wave5-vpu-dec.c | 30 ++++++++++++---------- .../platform/chips-media/wave5/wave5-vpuapi.h | 1 + 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c index c8624c681fa6..8e1dab9434ca 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c @@ -1055,6 +1055,22 @@ static int wave5_prepare_fb(struct vpu_instance *inst) int ret, i; struct v4l2_m2m_buffer *buf, *n; struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; + u32 bitdepth = inst->codec_info->dec_info.initial_info.luma_bitdepth; + + switch (bitdepth) { + case 8: + break; + case 10: + if (inst->std == W_HEVC_DEC && + inst->dev->attr.support_hevc10bit_dec) + break; + + fallthrough; + default: + dev_err(inst->dev->dev, "no support for %d bit depth\n", bitdepth); + + return -EINVAL; + } linear_num = v4l2_m2m_num_dst_bufs_ready(m2m_ctx); non_linear_num = inst->fbc_buf_count; @@ -1063,7 +1079,7 @@ static int wave5_prepare_fb(struct vpu_instance *inst) struct frame_buffer *frame = &inst->frame_buf[i]; struct vpu_buf *vframe = &inst->frame_vbuf[i]; - fb_stride = inst->dst_fmt.width; + fb_stride = ALIGN(inst->dst_fmt.width * bitdepth / 8, 32); fb_height = ALIGN(inst->dst_fmt.height, 32); luma_size = fb_stride * fb_height; @@ -1408,22 +1424,10 @@ static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count if (ret) goto free_bitstream_vbuf; } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - struct dec_initial_info *initial_info = - &inst->codec_info->dec_info.initial_info; - if (inst->state == VPU_INST_STATE_STOP) ret = switch_state(inst, VPU_INST_STATE_INIT_SEQ); if (ret) goto return_buffers; - - if (inst->state == VPU_INST_STATE_INIT_SEQ) { - if (initial_info->luma_bitdepth != 8) { - dev_info(inst->dev->dev, "%s: no support for %d bit depth", - __func__, initial_info->luma_bitdepth); - ret = -EINVAL; - goto return_buffers; - } - } } return ret; diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h index edc50450ddb8..fa4dc53949e6 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h @@ -327,6 +327,7 @@ struct vpu_attr { u32 support_backbone: 1; u32 support_avc10bit_enc: 1; u32 support_hevc10bit_enc: 1; + u32 support_hevc10bit_dec: 1; u32 support_vcore_backbone: 1; u32 support_vcpu_backbone: 1; }; -- cgit v1.2.3 From 2b9188426b79a0039ff73763f71fce41fbdc1f83 Mon Sep 17 00:00:00 2001 From: Ivan Bornyakov Date: Mon, 15 Apr 2024 13:07:20 +0300 Subject: media: chips-media: wave5: support reset lines Add initial support for optional reset lines. For now, simply deassert resets while probing the driver and assert them back when removing the driver. Signed-off-by: Ivan Bornyakov Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/chips-media/wave5/wave5-vpu.c | 16 +++++++++++++++- drivers/media/platform/chips-media/wave5/wave5-vpuapi.h | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c b/drivers/media/platform/chips-media/wave5/wave5-vpu.c index 68a519ac412d..54d1d44ed35a 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "wave5-vpu.h" #include "wave5-regdefine.h" #include "wave5-vpuconfig.h" @@ -179,6 +180,16 @@ static int wave5_vpu_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, dev); dev->dev = &pdev->dev; + dev->resets = devm_reset_control_array_get_optional_exclusive(&pdev->dev); + if (IS_ERR(dev->resets)) { + return dev_err_probe(&pdev->dev, PTR_ERR(dev->resets), + "Failed to get reset control\n"); + } + + ret = reset_control_deassert(dev->resets); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to deassert resets\n"); + ret = devm_clk_bulk_get_all(&pdev->dev, &dev->clks); /* continue without clock, assume externally managed */ @@ -191,7 +202,7 @@ static int wave5_vpu_probe(struct platform_device *pdev) ret = clk_bulk_prepare_enable(dev->num_clks, dev->clks); if (ret) { dev_err(&pdev->dev, "Enabling clocks, fail: %d\n", ret); - return ret; + goto err_reset_assert; } ret = of_property_read_u32(pdev->dev.of_node, "sram-size", @@ -282,6 +293,8 @@ err_vdi_release: wave5_vdi_release(&pdev->dev); err_clk_dis: clk_bulk_disable_unprepare(dev->num_clks, dev->clks); +err_reset_assert: + reset_control_assert(dev->resets); return ret; } @@ -297,6 +310,7 @@ static void wave5_vpu_remove(struct platform_device *pdev) mutex_destroy(&dev->dev_lock); mutex_destroy(&dev->hw_lock); + reset_control_assert(dev->resets); clk_bulk_disable_unprepare(dev->num_clks, dev->clks); wave5_vpu_enc_unregister_device(dev); wave5_vpu_dec_unregister_device(dev); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h index fa4dc53949e6..d5c9480242b6 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h @@ -762,6 +762,7 @@ struct vpu_device { struct kthread_worker *worker; int vpu_poll_interval; int num_clks; + struct reset_control *resets; }; struct vpu_instance; -- cgit v1.2.3 From 749476d44d2c903a62bcae4e26c4d54d609070f8 Mon Sep 17 00:00:00 2001 From: Ivan Bornyakov Date: Mon, 15 Apr 2024 13:07:21 +0300 Subject: media: chips-media: wave5: separate irq setup routine Implement a separate setup routine for interrupts to reduce code duplication. Also enable interrupts based on vpu_attr->support_encoders and vpu_attr->support_decoders fields to facilitate support for other Wave5xx IPs, because not all of them are both encoders and decoders. Signed-off-by: Ivan Bornyakov Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- .../media/platform/chips-media/wave5/wave5-hw.c | 53 ++++++++++------------ 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-hw.c b/drivers/media/platform/chips-media/wave5/wave5-hw.c index 2d82791f575e..cdd0a0948a94 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-hw.c +++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c @@ -299,6 +299,27 @@ static int wave5_send_query(struct vpu_device *vpu_dev, struct vpu_instance *ins return wave5_vpu_firmware_command_queue_error_check(vpu_dev, NULL); } +static void setup_wave5_interrupts(struct vpu_device *vpu_dev) +{ + u32 reg_val = 0; + + if (vpu_dev->attr.support_encoders) { + /* Encoder interrupt */ + reg_val |= BIT(INT_WAVE5_ENC_SET_PARAM); + reg_val |= BIT(INT_WAVE5_ENC_PIC); + reg_val |= BIT(INT_WAVE5_BSBUF_FULL); + } + + if (vpu_dev->attr.support_decoders) { + /* Decoder interrupt */ + reg_val |= BIT(INT_WAVE5_INIT_SEQ); + reg_val |= BIT(INT_WAVE5_DEC_PIC); + reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY); + } + + return vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val); +} + static int setup_wave5_properties(struct device *dev) { struct vpu_device *vpu_dev = dev_get_drvdata(dev); @@ -340,6 +361,8 @@ static int setup_wave5_properties(struct device *dev) p_attr->support_vcpu_backbone = FIELD_GET(FEATURE_VCPU_BACKBONE, hw_config_def0); p_attr->support_vcore_backbone = FIELD_GET(FEATURE_VCORE_BACKBONE, hw_config_def0); + setup_wave5_interrupts(vpu_dev); + return 0; } @@ -417,16 +440,6 @@ int wave5_vpu_init(struct device *dev, u8 *fw, size_t size) wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); - /* Encoder interrupt */ - reg_val = BIT(INT_WAVE5_ENC_SET_PARAM); - reg_val |= BIT(INT_WAVE5_ENC_PIC); - reg_val |= BIT(INT_WAVE5_BSBUF_FULL); - /* Decoder interrupt */ - reg_val |= BIT(INT_WAVE5_INIT_SEQ); - reg_val |= BIT(INT_WAVE5_DEC_PIC); - reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY); - vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val); - reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0); if (FIELD_GET(FEATURE_BACKBONE, reg_val)) { reg_val = ((WAVE5_PROC_AXI_ID << 28) | @@ -1034,16 +1047,6 @@ int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size) wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); - /* Encoder interrupt */ - reg_val = BIT(INT_WAVE5_ENC_SET_PARAM); - reg_val |= BIT(INT_WAVE5_ENC_PIC); - reg_val |= BIT(INT_WAVE5_BSBUF_FULL); - /* Decoder interrupt */ - reg_val |= BIT(INT_WAVE5_INIT_SEQ); - reg_val |= BIT(INT_WAVE5_DEC_PIC); - reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY); - vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val); - reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0); if (FIELD_GET(FEATURE_BACKBONE, reg_val)) { reg_val = ((WAVE5_PROC_AXI_ID << 28) | @@ -1134,15 +1137,7 @@ static int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uin wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); - /* Encoder interrupt */ - reg_val = BIT(INT_WAVE5_ENC_SET_PARAM); - reg_val |= BIT(INT_WAVE5_ENC_PIC); - reg_val |= BIT(INT_WAVE5_BSBUF_FULL); - /* Decoder interrupt */ - reg_val |= BIT(INT_WAVE5_INIT_SEQ); - reg_val |= BIT(INT_WAVE5_DEC_PIC); - reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY); - vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val); + setup_wave5_interrupts(vpu_dev); reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0); if (FIELD_GET(FEATURE_BACKBONE, reg_val)) { -- cgit v1.2.3 From a83d4a689e3b8966cb1dff0a997946399e76e900 Mon Sep 17 00:00:00 2001 From: Ivan Bornyakov Date: Mon, 15 Apr 2024 13:07:22 +0300 Subject: media: chips-media: wave5: drop "sram-size" DT property Move the excessive "sram-size" device-tree property to the device match data. Also change the SRAM memory allocation strategy, instead of allocating exactly sram_size bytes, allocate all available SRAM memory up to sram_size. Add the placeholders wave5_vpu_dec_validate_sec_axi() and wave5_vpu_enc_validate_sec_axi() to validate that the allocated SRAM memory is sufficient to decode/encode bitstream with a given resolution. Signed-off-by: Ivan Bornyakov Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- .../media/platform/chips-media/wave5/wave5-hw.c | 62 ++++++++++++++++++++-- .../media/platform/chips-media/wave5/wave5-vdi.c | 21 ++++---- .../media/platform/chips-media/wave5/wave5-vpu.c | 11 ++-- 3 files changed, 72 insertions(+), 22 deletions(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-hw.c b/drivers/media/platform/chips-media/wave5/wave5-hw.c index cdd0a0948a94..36f2fc818013 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-hw.c +++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c @@ -843,6 +843,36 @@ free_mv_buffers: return ret; } +static u32 wave5_vpu_dec_validate_sec_axi(struct vpu_instance *inst) +{ + struct dec_info *p_dec_info = &inst->codec_info->dec_info; + u32 bit_size = 0, ip_size = 0, lf_size = 0, ret = 0; + u32 sram_size = inst->dev->sram_size; + + if (!sram_size) + return 0; + + /* + * TODO: calculate bit_size, ip_size, lf_size from inst->src_fmt.width + * and inst->codec_info->dec_info.initial_info.luma_bitdepth + */ + + if (p_dec_info->sec_axi_info.use_bit_enable && sram_size >= bit_size) { + ret |= BIT(0); + sram_size -= bit_size; + } + + if (p_dec_info->sec_axi_info.use_ip_enable && sram_size >= ip_size) { + ret |= BIT(9); + sram_size -= ip_size; + } + + if (p_dec_info->sec_axi_info.use_lf_row_enable && sram_size >= lf_size) + ret |= BIT(15); + + return ret; +} + int wave5_vpu_decode(struct vpu_instance *inst, u32 *fail_res) { u32 reg_val; @@ -855,9 +885,7 @@ int wave5_vpu_decode(struct vpu_instance *inst, u32 *fail_res) vpu_write_reg(inst->dev, W5_BS_OPTION, get_bitstream_options(p_dec_info)); /* secondary AXI */ - reg_val = p_dec_info->sec_axi_info.use_bit_enable | - (p_dec_info->sec_axi_info.use_ip_enable << 9) | - (p_dec_info->sec_axi_info.use_lf_row_enable << 15); + reg_val = wave5_vpu_dec_validate_sec_axi(inst); vpu_write_reg(inst->dev, W5_USE_SEC_AXI, reg_val); /* set attributes of user buffer */ @@ -1938,6 +1966,31 @@ free_vb_fbc_y_tbl: return ret; } +static u32 wave5_vpu_enc_validate_sec_axi(struct vpu_instance *inst) +{ + struct enc_info *p_enc_info = &inst->codec_info->enc_info; + u32 rdo_size = 0, lf_size = 0, ret = 0; + u32 sram_size = inst->dev->sram_size; + + if (!sram_size) + return 0; + + /* + * TODO: calculate rdo_size and lf_size from inst->src_fmt.width and + * inst->codec_info->enc_info.open_param.wave_param.internal_bit_depth + */ + + if (p_enc_info->sec_axi_info.use_enc_rdo_enable && sram_size >= rdo_size) { + ret |= BIT(11); + sram_size -= rdo_size; + } + + if (p_enc_info->sec_axi_info.use_enc_lf_enable && sram_size >= lf_size) + ret |= BIT(15); + + return ret; +} + int wave5_vpu_encode(struct vpu_instance *inst, struct enc_param *option, u32 *fail_res) { u32 src_frame_format; @@ -1959,8 +2012,7 @@ int wave5_vpu_encode(struct vpu_instance *inst, struct enc_param *option, u32 *f vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_AXI_SEL, DEFAULT_SRC_AXI); /* secondary AXI */ - reg_val = (p_enc_info->sec_axi_info.use_enc_rdo_enable << 11) | - (p_enc_info->sec_axi_info.use_enc_lf_enable << 15); + reg_val = wave5_vpu_enc_validate_sec_axi(inst); vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_USE_SEC_AXI, reg_val); vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_REPORT_PARAM, 0); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vdi.c b/drivers/media/platform/chips-media/wave5/wave5-vdi.c index 3809f70bc0b4..556de2f043fe 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vdi.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vdi.c @@ -174,16 +174,19 @@ int wave5_vdi_allocate_array(struct vpu_device *vpu_dev, struct vpu_buf *array, void wave5_vdi_allocate_sram(struct vpu_device *vpu_dev) { struct vpu_buf *vb = &vpu_dev->sram_buf; + dma_addr_t daddr; + void *vaddr; + size_t size; - if (!vpu_dev->sram_pool || !vpu_dev->sram_size) + if (!vpu_dev->sram_pool || vb->vaddr) return; - if (!vb->vaddr) { - vb->size = vpu_dev->sram_size; - vb->vaddr = gen_pool_dma_alloc(vpu_dev->sram_pool, vb->size, - &vb->daddr); - if (!vb->vaddr) - vb->size = 0; + size = min_t(size_t, vpu_dev->sram_size, gen_pool_avail(vpu_dev->sram_pool)); + vaddr = gen_pool_dma_alloc(vpu_dev->sram_pool, size, &daddr); + if (vaddr) { + vb->vaddr = vaddr; + vb->daddr = daddr; + vb->size = size; } dev_dbg(vpu_dev->dev, "%s: sram daddr: %pad, size: %zu, vaddr: 0x%p\n", @@ -197,9 +200,7 @@ void wave5_vdi_free_sram(struct vpu_device *vpu_dev) if (!vb->size || !vb->vaddr) return; - if (vb->vaddr) - gen_pool_free(vpu_dev->sram_pool, (unsigned long)vb->vaddr, - vb->size); + gen_pool_free(vpu_dev->sram_pool, (unsigned long)vb->vaddr, vb->size); memset(vb, 0, sizeof(*vb)); } diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c b/drivers/media/platform/chips-media/wave5/wave5-vpu.c index 54d1d44ed35a..901dbfdf1e4f 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c @@ -25,6 +25,7 @@ struct wave5_match_data { int flags; const char *fw_name; + u32 sram_size; }; static int vpu_poll_interval = 5; @@ -205,17 +206,12 @@ static int wave5_vpu_probe(struct platform_device *pdev) goto err_reset_assert; } - ret = of_property_read_u32(pdev->dev.of_node, "sram-size", - &dev->sram_size); - if (ret) { - dev_warn(&pdev->dev, "sram-size not found\n"); - dev->sram_size = 0; - } - dev->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0); if (!dev->sram_pool) dev_warn(&pdev->dev, "sram node not found\n"); + dev->sram_size = match_data->sram_size; + dev->product_code = wave5_vdi_read_register(dev, VPU_PRODUCT_CODE_REGISTER); ret = wave5_vdi_init(&pdev->dev); if (ret < 0) { @@ -322,6 +318,7 @@ static void wave5_vpu_remove(struct platform_device *pdev) static const struct wave5_match_data ti_wave521c_data = { .flags = WAVE5_IS_ENC | WAVE5_IS_DEC, .fw_name = "cnm/wave521c_k3_codec_fw.bin", + .sram_size = (64 * 1024), }; static const struct of_device_id wave5_dt_ids[] = { -- cgit v1.2.3 From 6aa082910445aec6b1dc652a69c5178a555d8ca5 Mon Sep 17 00:00:00 2001 From: Ivan Bornyakov Date: Mon, 15 Apr 2024 13:07:23 +0300 Subject: media: chips-media: wave5: support Wave515 decoder Add initial support for the Wave515 multi-decoder IP. For now it is only able to decode HEVC Main/Main10 profile videos into YUV420. This was tested on FPGA prototype, so wave5_dt_ids[] was not expanded. Users of the real hardware with Wave515 IP will have to * provide firmware specific to their SoC * add struct wave5_match_data like this: static const struct wave5_match_data platform_name_wave515_data = { .flags = WAVE5_IS_DEC, .fw_name = "cnm/wave515_platform_name_fw.bin", .sram_size = (71 * 1024), }; * add item to wave5_dt_ids[] like this: { .compatible = "vendor,soc-wave515", .data = &platform_name_wave515_data, }, * describe new compatible in Documentation/devicetree/bindings/media/cnm,wave521c.yaml Signed-off-by: Ivan Bornyakov Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- .../platform/chips-media/wave5/wave5-helper.c | 8 +- .../media/platform/chips-media/wave5/wave5-hw.c | 286 ++++++++++++++++----- .../platform/chips-media/wave5/wave5-regdefine.h | 5 + .../media/platform/chips-media/wave5/wave5-vdi.c | 6 +- .../platform/chips-media/wave5/wave5-vpu-dec.c | 21 +- .../platform/chips-media/wave5/wave5-vpu-enc.c | 2 +- .../media/platform/chips-media/wave5/wave5-vpu.c | 8 +- .../platform/chips-media/wave5/wave5-vpuapi.h | 1 + .../platform/chips-media/wave5/wave5-vpuconfig.h | 16 +- drivers/media/platform/chips-media/wave5/wave5.h | 6 + 10 files changed, 280 insertions(+), 79 deletions(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.c b/drivers/media/platform/chips-media/wave5/wave5-helper.c index 7e0f34bfa5be..d60841c54a80 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-helper.c +++ b/drivers/media/platform/chips-media/wave5/wave5-helper.c @@ -29,7 +29,13 @@ void wave5_cleanup_instance(struct vpu_instance *inst) { int i; - if (list_is_singular(&inst->list)) + /* + * For Wave515 SRAM memory is allocated at + * wave5_vpu_dec_register_device() and freed at + * wave5_vpu_dec_unregister_device(). + */ + if (list_is_singular(&inst->list) && + inst->dev->product_code != WAVE515_CODE) wave5_vdi_free_sram(inst->dev); for (i = 0; i < inst->fbc_buf_count; i++) diff --git a/drivers/media/platform/chips-media/wave5/wave5-hw.c b/drivers/media/platform/chips-media/wave5/wave5-hw.c index 36f2fc818013..c89aafabc742 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-hw.c +++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c @@ -18,18 +18,20 @@ #define QUEUE_REPORT_MASK 0xffff /* Encoder support fields */ -#define FEATURE_HEVC10BIT_ENC BIT(3) -#define FEATURE_AVC10BIT_ENC BIT(11) -#define FEATURE_AVC_ENCODER BIT(1) -#define FEATURE_HEVC_ENCODER BIT(0) +#define W521_FEATURE_HEVC10BIT_ENC BIT(3) +#define W521_FEATURE_AVC10BIT_ENC BIT(11) +#define W521_FEATURE_AVC_ENCODER BIT(1) +#define W521_FEATURE_HEVC_ENCODER BIT(0) /* Decoder support fields */ -#define FEATURE_AVC_DECODER BIT(3) -#define FEATURE_HEVC_DECODER BIT(2) +#define W521_FEATURE_AVC_DECODER BIT(3) +#define W521_FEATURE_HEVC_DECODER BIT(2) +#define W515_FEATURE_HEVC10BIT_DEC BIT(1) +#define W515_FEATURE_HEVC_DECODER BIT(0) -#define FEATURE_BACKBONE BIT(16) -#define FEATURE_VCORE_BACKBONE BIT(22) -#define FEATURE_VCPU_BACKBONE BIT(28) +#define W521_FEATURE_BACKBONE BIT(16) +#define W521_FEATURE_VCORE_BACKBONE BIT(22) +#define W521_FEATURE_VCPU_BACKBONE BIT(28) #define REMAP_CTRL_MAX_SIZE_BITS ((W5_REMAP_MAX_SIZE >> 12) & 0x1ff) #define REMAP_CTRL_REGISTER_VALUE(index) ( \ @@ -155,6 +157,8 @@ static int wave5_wait_bus_busy(struct vpu_device *vpu_dev, unsigned int addr) { u32 gdi_status_check_value = 0x3f; + if (vpu_dev->product_code == WAVE515_CODE) + gdi_status_check_value = 0x0738; if (vpu_dev->product_code == WAVE521C_CODE || vpu_dev->product_code == WAVE521_CODE || vpu_dev->product_code == WAVE521E1_CODE) @@ -186,6 +190,8 @@ unsigned int wave5_vpu_get_product_id(struct vpu_device *vpu_dev) u32 val = vpu_read_reg(vpu_dev, W5_PRODUCT_NUMBER); switch (val) { + case WAVE515_CODE: + return PRODUCT_ID_515; case WAVE521C_CODE: return PRODUCT_ID_521; case WAVE521_CODE: @@ -349,17 +355,33 @@ static int setup_wave5_properties(struct device *dev) hw_config_def1 = vpu_read_reg(vpu_dev, W5_RET_STD_DEF1); hw_config_feature = vpu_read_reg(vpu_dev, W5_RET_CONF_FEATURE); - p_attr->support_hevc10bit_enc = FIELD_GET(FEATURE_HEVC10BIT_ENC, hw_config_feature); - p_attr->support_avc10bit_enc = FIELD_GET(FEATURE_AVC10BIT_ENC, hw_config_feature); - - p_attr->support_decoders = FIELD_GET(FEATURE_AVC_DECODER, hw_config_def1) << STD_AVC; - p_attr->support_decoders |= FIELD_GET(FEATURE_HEVC_DECODER, hw_config_def1) << STD_HEVC; - p_attr->support_encoders = FIELD_GET(FEATURE_AVC_ENCODER, hw_config_def1) << STD_AVC; - p_attr->support_encoders |= FIELD_GET(FEATURE_HEVC_ENCODER, hw_config_def1) << STD_HEVC; - - p_attr->support_backbone = FIELD_GET(FEATURE_BACKBONE, hw_config_def0); - p_attr->support_vcpu_backbone = FIELD_GET(FEATURE_VCPU_BACKBONE, hw_config_def0); - p_attr->support_vcore_backbone = FIELD_GET(FEATURE_VCORE_BACKBONE, hw_config_def0); + if (vpu_dev->product_code == WAVE515_CODE) { + p_attr->support_hevc10bit_dec = FIELD_GET(W515_FEATURE_HEVC10BIT_DEC, + hw_config_feature); + p_attr->support_decoders = FIELD_GET(W515_FEATURE_HEVC_DECODER, + hw_config_def1) << STD_HEVC; + } else { + p_attr->support_hevc10bit_enc = FIELD_GET(W521_FEATURE_HEVC10BIT_ENC, + hw_config_feature); + p_attr->support_avc10bit_enc = FIELD_GET(W521_FEATURE_AVC10BIT_ENC, + hw_config_feature); + + p_attr->support_decoders = FIELD_GET(W521_FEATURE_AVC_DECODER, + hw_config_def1) << STD_AVC; + p_attr->support_decoders |= FIELD_GET(W521_FEATURE_HEVC_DECODER, + hw_config_def1) << STD_HEVC; + p_attr->support_encoders = FIELD_GET(W521_FEATURE_AVC_ENCODER, + hw_config_def1) << STD_AVC; + p_attr->support_encoders |= FIELD_GET(W521_FEATURE_HEVC_ENCODER, + hw_config_def1) << STD_HEVC; + + p_attr->support_backbone = FIELD_GET(W521_FEATURE_BACKBONE, + hw_config_def0); + p_attr->support_vcpu_backbone = FIELD_GET(W521_FEATURE_VCPU_BACKBONE, + hw_config_def0); + p_attr->support_vcore_backbone = FIELD_GET(W521_FEATURE_VCORE_BACKBONE, + hw_config_def0); + } setup_wave5_interrupts(vpu_dev); @@ -403,12 +425,18 @@ int wave5_vpu_init(struct device *dev, u8 *fw, size_t size) common_vb = &vpu_dev->common_mem; code_base = common_vb->daddr; + + if (vpu_dev->product_code == WAVE515_CODE) + code_size = WAVE515_MAX_CODE_BUF_SIZE; + else + code_size = WAVE521_MAX_CODE_BUF_SIZE; + /* ALIGN TO 4KB */ - code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff); + code_size &= ~0xfff; if (code_size < size * 2) return -EINVAL; - temp_base = common_vb->daddr + WAVE5_TEMPBUF_OFFSET; + temp_base = code_base + code_size; temp_size = WAVE5_TEMPBUF_SIZE; ret = wave5_vdi_write_memory(vpu_dev, common_vb, 0, fw, size); @@ -436,12 +464,15 @@ int wave5_vpu_init(struct device *dev, u8 *fw, size_t size) /* These register must be reset explicitly */ vpu_write_reg(vpu_dev, W5_HW_OPTION, 0); - wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0); - wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); - vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); + + if (vpu_dev->product_code != WAVE515_CODE) { + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0); + wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); + vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); + } reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0); - if (FIELD_GET(FEATURE_BACKBONE, reg_val)) { + if (FIELD_GET(W521_FEATURE_BACKBONE, reg_val)) { reg_val = ((WAVE5_PROC_AXI_ID << 28) | (WAVE5_PRP_AXI_ID << 24) | (WAVE5_FBD_Y_AXI_ID << 20) | @@ -453,6 +484,24 @@ int wave5_vpu_init(struct device *dev, u8 *fw, size_t size) wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val); } + if (vpu_dev->product_code == WAVE515_CODE) { + dma_addr_t task_buf_base; + + vpu_write_reg(vpu_dev, W5_CMD_INIT_NUM_TASK_BUF, WAVE515_COMMAND_QUEUE_DEPTH); + vpu_write_reg(vpu_dev, W5_CMD_INIT_TASK_BUF_SIZE, WAVE515_ONE_TASKBUF_SIZE); + + for (i = 0; i < WAVE515_COMMAND_QUEUE_DEPTH; i++) { + task_buf_base = temp_base + temp_size + + (i * WAVE515_ONE_TASKBUF_SIZE); + vpu_write_reg(vpu_dev, + W5_CMD_INIT_ADDR_TASK_BUF0 + (i * 4), + task_buf_base); + } + + vpu_write_reg(vpu_dev, W515_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr); + vpu_write_reg(vpu_dev, W515_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size); + } + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1); vpu_write_reg(vpu_dev, W5_COMMAND, W5_INIT_VPU); vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1); @@ -493,29 +542,40 @@ int wave5_vpu_build_up_dec_param(struct vpu_instance *inst, return -EINVAL; } - p_dec_info->vb_work.size = WAVE521DEC_WORKBUF_SIZE; + if (vpu_dev->product == PRODUCT_ID_515) + p_dec_info->vb_work.size = WAVE515DEC_WORKBUF_SIZE; + else + p_dec_info->vb_work.size = WAVE521DEC_WORKBUF_SIZE; + ret = wave5_vdi_allocate_dma_memory(inst->dev, &p_dec_info->vb_work); if (ret) return ret; - vpu_write_reg(inst->dev, W5_CMD_DEC_VCORE_INFO, 1); + if (inst->dev->product_code != WAVE515_CODE) + vpu_write_reg(inst->dev, W5_CMD_DEC_VCORE_INFO, 1); wave5_vdi_clear_memory(inst->dev, &p_dec_info->vb_work); vpu_write_reg(inst->dev, W5_ADDR_WORK_BASE, p_dec_info->vb_work.daddr); vpu_write_reg(inst->dev, W5_WORK_SIZE, p_dec_info->vb_work.size); - vpu_write_reg(inst->dev, W5_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr); - vpu_write_reg(inst->dev, W5_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size); + if (inst->dev->product_code != WAVE515_CODE) { + vpu_write_reg(inst->dev, W5_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr); + vpu_write_reg(inst->dev, W5_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size); + } vpu_write_reg(inst->dev, W5_CMD_DEC_BS_START_ADDR, p_dec_info->stream_buf_start_addr); vpu_write_reg(inst->dev, W5_CMD_DEC_BS_SIZE, p_dec_info->stream_buf_size); /* NOTE: SDMA reads MSB first */ vpu_write_reg(inst->dev, W5_CMD_BS_PARAM, BITSTREAM_ENDIANNESS_BIG_ENDIAN); - /* This register must be reset explicitly */ - vpu_write_reg(inst->dev, W5_CMD_EXT_ADDR, 0); - vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, (COMMAND_QUEUE_DEPTH - 1)); + + if (inst->dev->product_code != WAVE515_CODE) { + /* This register must be reset explicitly */ + vpu_write_reg(inst->dev, W5_CMD_EXT_ADDR, 0); + vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, + WAVE521_COMMAND_QUEUE_DEPTH - 1); + } ret = send_firmware_command(inst, W5_CREATE_INSTANCE, true, NULL, NULL); if (ret) { @@ -566,7 +626,7 @@ static u32 get_bitstream_options(struct dec_info *info) int wave5_vpu_dec_init_seq(struct vpu_instance *inst) { struct dec_info *p_dec_info = &inst->codec_info->dec_info; - u32 cmd_option = INIT_SEQ_NORMAL; + u32 bs_option, cmd_option = INIT_SEQ_NORMAL; u32 reg_val, fail_res; int ret; @@ -576,7 +636,13 @@ int wave5_vpu_dec_init_seq(struct vpu_instance *inst) vpu_write_reg(inst->dev, W5_BS_RD_PTR, p_dec_info->stream_rd_ptr); vpu_write_reg(inst->dev, W5_BS_WR_PTR, p_dec_info->stream_wr_ptr); - vpu_write_reg(inst->dev, W5_BS_OPTION, get_bitstream_options(p_dec_info)); + bs_option = get_bitstream_options(p_dec_info); + + /* Without RD_PTR_VALID_FLAG Wave515 ignores RD_PTR value */ + if (inst->dev->product_code == WAVE515_CODE) + bs_option |= BSOPTION_RD_PTR_VALID_FLAG; + + vpu_write_reg(inst->dev, W5_BS_OPTION, bs_option); vpu_write_reg(inst->dev, W5_COMMAND_OPTION, cmd_option); vpu_write_reg(inst->dev, W5_CMD_DEC_USER_MASK, p_dec_info->user_data_enable); @@ -642,10 +708,12 @@ static void wave5_get_dec_seq_result(struct vpu_instance *inst, struct dec_initi info->profile = FIELD_GET(SEQ_PARAM_PROFILE_MASK, reg_val); } - info->vlc_buf_size = vpu_read_reg(inst->dev, W5_RET_VLC_BUF_SIZE); - info->param_buf_size = vpu_read_reg(inst->dev, W5_RET_PARAM_BUF_SIZE); - p_dec_info->vlc_buf_size = info->vlc_buf_size; - p_dec_info->param_buf_size = info->param_buf_size; + if (inst->dev->product_code != WAVE515_CODE) { + info->vlc_buf_size = vpu_read_reg(inst->dev, W5_RET_VLC_BUF_SIZE); + info->param_buf_size = vpu_read_reg(inst->dev, W5_RET_PARAM_BUF_SIZE); + p_dec_info->vlc_buf_size = info->vlc_buf_size; + p_dec_info->param_buf_size = info->param_buf_size; + } } int wave5_vpu_dec_get_seq_info(struct vpu_instance *inst, struct dec_initial_info *info) @@ -747,22 +815,27 @@ int wave5_vpu_dec_register_framebuffer(struct vpu_instance *inst, struct frame_b pic_size = (init_info->pic_width << 16) | (init_info->pic_height); - vb_buf.size = (p_dec_info->vlc_buf_size * VLC_BUF_NUM) + - (p_dec_info->param_buf_size * COMMAND_QUEUE_DEPTH); - vb_buf.daddr = 0; + if (inst->dev->product_code != WAVE515_CODE) { + vb_buf.size = (p_dec_info->vlc_buf_size * VLC_BUF_NUM) + + (p_dec_info->param_buf_size * WAVE521_COMMAND_QUEUE_DEPTH); + vb_buf.daddr = 0; - if (vb_buf.size != p_dec_info->vb_task.size) { - wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_task); - ret = wave5_vdi_allocate_dma_memory(inst->dev, &vb_buf); - if (ret) - goto free_fbc_c_tbl_buffers; + if (vb_buf.size != p_dec_info->vb_task.size) { + wave5_vdi_free_dma_memory(inst->dev, + &p_dec_info->vb_task); + ret = wave5_vdi_allocate_dma_memory(inst->dev, + &vb_buf); + if (ret) + goto free_fbc_c_tbl_buffers; - p_dec_info->vb_task = vb_buf; - } + p_dec_info->vb_task = vb_buf; + } - vpu_write_reg(inst->dev, W5_CMD_SET_FB_ADDR_TASK_BUF, - p_dec_info->vb_task.daddr); - vpu_write_reg(inst->dev, W5_CMD_SET_FB_TASK_BUF_SIZE, vb_buf.size); + vpu_write_reg(inst->dev, W5_CMD_SET_FB_ADDR_TASK_BUF, + p_dec_info->vb_task.daddr); + vpu_write_reg(inst->dev, W5_CMD_SET_FB_TASK_BUF_SIZE, + vb_buf.size); + } } else { pic_size = (init_info->pic_width << 16) | (init_info->pic_height); @@ -845,17 +918,24 @@ free_mv_buffers: static u32 wave5_vpu_dec_validate_sec_axi(struct vpu_instance *inst) { + u32 bitdepth = inst->codec_info->dec_info.initial_info.luma_bitdepth; struct dec_info *p_dec_info = &inst->codec_info->dec_info; u32 bit_size = 0, ip_size = 0, lf_size = 0, ret = 0; u32 sram_size = inst->dev->sram_size; + u32 width = inst->src_fmt.width; if (!sram_size) return 0; /* - * TODO: calculate bit_size, ip_size, lf_size from inst->src_fmt.width - * and inst->codec_info->dec_info.initial_info.luma_bitdepth + * TODO: calculate bit_size, ip_size, lf_size from width and bitdepth + * for Wave521. */ + if (inst->dev->product_code == WAVE515_CODE) { + bit_size = DIV_ROUND_UP(width, 16) * 5 * 8; + ip_size = ALIGN(width, 16) * 2 * bitdepth / 8; + lf_size = ALIGN(width, 16) * 10 * bitdepth / 8; + } if (p_dec_info->sec_axi_info.use_bit_enable && sram_size >= bit_size) { ret |= BIT(0); @@ -1033,11 +1113,18 @@ int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size) common_vb = &vpu_dev->common_mem; code_base = common_vb->daddr; + + if (vpu_dev->product_code == WAVE515_CODE) + code_size = WAVE515_MAX_CODE_BUF_SIZE; + else + code_size = WAVE521_MAX_CODE_BUF_SIZE; + /* ALIGN TO 4KB */ - code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff); + code_size &= ~0xfff; if (code_size < size * 2) return -EINVAL; - temp_base = common_vb->daddr + WAVE5_TEMPBUF_OFFSET; + + temp_base = code_base + code_size; temp_size = WAVE5_TEMPBUF_SIZE; old_code_base = vpu_read_reg(vpu_dev, W5_VPU_REMAP_PADDR); @@ -1071,12 +1158,15 @@ int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size) /* These register must be reset explicitly */ vpu_write_reg(vpu_dev, W5_HW_OPTION, 0); - wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0); - wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); - vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); + + if (vpu_dev->product_code != WAVE515_CODE) { + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0); + wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); + vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); + } reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0); - if (FIELD_GET(FEATURE_BACKBONE, reg_val)) { + if (FIELD_GET(W521_FEATURE_BACKBONE, reg_val)) { reg_val = ((WAVE5_PROC_AXI_ID << 28) | (WAVE5_PRP_AXI_ID << 24) | (WAVE5_FBD_Y_AXI_ID << 20) | @@ -1088,6 +1178,29 @@ int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size) wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val); } + if (vpu_dev->product_code == WAVE515_CODE) { + dma_addr_t task_buf_base; + u32 i; + + vpu_write_reg(vpu_dev, W5_CMD_INIT_NUM_TASK_BUF, + WAVE515_COMMAND_QUEUE_DEPTH); + vpu_write_reg(vpu_dev, W5_CMD_INIT_TASK_BUF_SIZE, + WAVE515_ONE_TASKBUF_SIZE); + + for (i = 0; i < WAVE515_COMMAND_QUEUE_DEPTH; i++) { + task_buf_base = temp_base + temp_size + + (i * WAVE515_ONE_TASKBUF_SIZE); + vpu_write_reg(vpu_dev, + W5_CMD_INIT_ADDR_TASK_BUF0 + (i * 4), + task_buf_base); + } + + vpu_write_reg(vpu_dev, W515_CMD_ADDR_SEC_AXI, + vpu_dev->sram_buf.daddr); + vpu_write_reg(vpu_dev, W515_CMD_SEC_AXI_SIZE, + vpu_dev->sram_buf.size); + } + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1); vpu_write_reg(vpu_dev, W5_COMMAND, W5_INIT_VPU); vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1); @@ -1111,8 +1224,8 @@ static int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uin { u32 reg_val; struct vpu_buf *common_vb; - dma_addr_t code_base; - u32 code_size, reason_code; + dma_addr_t code_base, temp_base; + u32 code_size, temp_size, reason_code; struct vpu_device *vpu_dev = dev_get_drvdata(dev); int ret; @@ -1142,13 +1255,22 @@ static int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uin common_vb = &vpu_dev->common_mem; code_base = common_vb->daddr; + + if (vpu_dev->product_code == WAVE515_CODE) + code_size = WAVE515_MAX_CODE_BUF_SIZE; + else + code_size = WAVE521_MAX_CODE_BUF_SIZE; + /* ALIGN TO 4KB */ - code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff); + code_size &= ~0xfff; if (code_size < size * 2) { dev_err(dev, "size too small\n"); return -EINVAL; } + temp_base = code_base + code_size; + temp_size = WAVE5_TEMPBUF_SIZE; + /* Power on without DEBUG mode */ vpu_write_reg(vpu_dev, W5_PO_CONF, 0); @@ -1161,14 +1283,17 @@ static int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uin /* These register must be reset explicitly */ vpu_write_reg(vpu_dev, W5_HW_OPTION, 0); - wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0); - wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); - vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); + + if (vpu_dev->product_code != WAVE515_CODE) { + wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0); + wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0); + vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0); + } setup_wave5_interrupts(vpu_dev); reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0); - if (FIELD_GET(FEATURE_BACKBONE, reg_val)) { + if (FIELD_GET(W521_FEATURE_BACKBONE, reg_val)) { reg_val = ((WAVE5_PROC_AXI_ID << 28) | (WAVE5_PRP_AXI_ID << 24) | (WAVE5_FBD_Y_AXI_ID << 20) | @@ -1180,6 +1305,29 @@ static int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uin wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val); } + if (vpu_dev->product_code == WAVE515_CODE) { + dma_addr_t task_buf_base; + u32 i; + + vpu_write_reg(vpu_dev, W5_CMD_INIT_NUM_TASK_BUF, + WAVE515_COMMAND_QUEUE_DEPTH); + vpu_write_reg(vpu_dev, W5_CMD_INIT_TASK_BUF_SIZE, + WAVE515_ONE_TASKBUF_SIZE); + + for (i = 0; i < WAVE515_COMMAND_QUEUE_DEPTH; i++) { + task_buf_base = temp_base + temp_size + + (i * WAVE515_ONE_TASKBUF_SIZE); + vpu_write_reg(vpu_dev, + W5_CMD_INIT_ADDR_TASK_BUF0 + (i * 4), + task_buf_base); + } + + vpu_write_reg(vpu_dev, W515_CMD_ADDR_SEC_AXI, + vpu_dev->sram_buf.daddr); + vpu_write_reg(vpu_dev, W515_CMD_SEC_AXI_SIZE, + vpu_dev->sram_buf.size); + } + vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1); vpu_write_reg(vpu_dev, W5_COMMAND, W5_WAKEUP_VPU); /* Start VPU after settings */ @@ -1424,7 +1572,7 @@ int wave5_vpu_build_up_enc_param(struct device *dev, struct vpu_instance *inst, reg_val = (open_param->line_buf_int_en << 6) | BITSTREAM_ENDIANNESS_BIG_ENDIAN; vpu_write_reg(inst->dev, W5_CMD_BS_PARAM, reg_val); vpu_write_reg(inst->dev, W5_CMD_EXT_ADDR, 0); - vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, (COMMAND_QUEUE_DEPTH - 1)); + vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, WAVE521_COMMAND_QUEUE_DEPTH - 1); /* This register must be reset explicitly */ vpu_write_reg(inst->dev, W5_CMD_ENC_SRC_OPTIONS, 0); @@ -1878,7 +2026,7 @@ int wave5_vpu_enc_register_framebuffer(struct device *dev, struct vpu_instance * p_enc_info->vb_sub_sam_buf = vb_sub_sam_buf; vb_task.size = (p_enc_info->vlc_buf_size * VLC_BUF_NUM) + - (p_enc_info->param_buf_size * COMMAND_QUEUE_DEPTH); + (p_enc_info->param_buf_size * WAVE521_COMMAND_QUEUE_DEPTH); vb_task.daddr = 0; if (p_enc_info->vb_task.size == 0) { ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_task); diff --git a/drivers/media/platform/chips-media/wave5/wave5-regdefine.h b/drivers/media/platform/chips-media/wave5/wave5-regdefine.h index a15c6b2c3d8b..557344754c4c 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-regdefine.h +++ b/drivers/media/platform/chips-media/wave5/wave5-regdefine.h @@ -205,6 +205,9 @@ enum query_opt { #define W5_ADDR_TEMP_BASE (W5_REG_BASE + 0x011C) #define W5_TEMP_SIZE (W5_REG_BASE + 0x0120) #define W5_HW_OPTION (W5_REG_BASE + 0x012C) +#define W5_CMD_INIT_NUM_TASK_BUF (W5_REG_BASE + 0x0134) +#define W5_CMD_INIT_ADDR_TASK_BUF0 (W5_REG_BASE + 0x0138) +#define W5_CMD_INIT_TASK_BUF_SIZE (W5_REG_BASE + 0x0178) #define W5_SEC_AXI_PARAM (W5_REG_BASE + 0x0180) /************************************************************************/ @@ -216,7 +219,9 @@ enum query_opt { #define W5_CMD_DEC_BS_SIZE (W5_REG_BASE + 0x0120) #define W5_CMD_BS_PARAM (W5_REG_BASE + 0x0124) #define W5_CMD_ADDR_SEC_AXI (W5_REG_BASE + 0x0130) +#define W515_CMD_ADDR_SEC_AXI (W5_REG_BASE + 0x0124) #define W5_CMD_SEC_AXI_SIZE (W5_REG_BASE + 0x0134) +#define W515_CMD_SEC_AXI_SIZE (W5_REG_BASE + 0x0128) #define W5_CMD_EXT_ADDR (W5_REG_BASE + 0x0138) #define W5_CMD_NUM_CQ_DEPTH_M1 (W5_REG_BASE + 0x013C) #define W5_CMD_ERR_CONCEAL (W5_REG_BASE + 0x0140) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vdi.c b/drivers/media/platform/chips-media/wave5/wave5-vdi.c index 556de2f043fe..bb13267ced38 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vdi.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vdi.c @@ -18,7 +18,11 @@ static int wave5_vdi_allocate_common_memory(struct device *dev) if (!vpu_dev->common_mem.vaddr) { int ret; - vpu_dev->common_mem.size = SIZE_COMMON; + if (vpu_dev->product_code == WAVE515_CODE) + vpu_dev->common_mem.size = WAVE515_SIZE_COMMON; + else + vpu_dev->common_mem.size = WAVE521_SIZE_COMMON; + ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vpu_dev->common_mem); if (ret) { dev_err(dev, "unable to allocate common buffer\n"); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c index 8e1dab9434ca..0c5c9a8de91f 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c @@ -1868,7 +1868,12 @@ static int wave5_vpu_open_dec(struct file *filp) goto cleanup_inst; } - wave5_vdi_allocate_sram(inst->dev); + /* + * For Wave515 SRAM memory was already allocated + * at wave5_vpu_dec_register_device() + */ + if (inst->dev->product_code != WAVE515_CODE) + wave5_vdi_allocate_sram(inst->dev); ret = mutex_lock_interruptible(&dev->dev_lock); if (ret) @@ -1908,6 +1913,13 @@ int wave5_vpu_dec_register_device(struct vpu_device *dev) struct video_device *vdev_dec; int ret; + /* + * Secondary AXI setup for Wave515 is done by INIT_VPU command, + * i.e. wave5_vpu_init(), that's why we allocate SRAM memory early. + */ + if (dev->product_code == WAVE515_CODE) + wave5_vdi_allocate_sram(dev); + vdev_dec = devm_kzalloc(dev->v4l2_dev.dev, sizeof(*vdev_dec), GFP_KERNEL); if (!vdev_dec) return -ENOMEM; @@ -1941,6 +1953,13 @@ int wave5_vpu_dec_register_device(struct vpu_device *dev) void wave5_vpu_dec_unregister_device(struct vpu_device *dev) { + /* + * Here is a freeing pair for Wave515 SRAM memory allocation + * happened at wave5_vpu_dec_register_device(). + */ + if (dev->product_code == WAVE515_CODE) + wave5_vdi_free_sram(dev); + video_unregister_device(dev->video_dev_dec); if (dev->v4l2_m2m_dec_dev) v4l2_m2m_release(dev->v4l2_m2m_dec_dev); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c index a45a2f699000..3e35a05c2d8d 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c @@ -1247,7 +1247,7 @@ static int initialize_sequence(struct vpu_instance *inst) __func__, initial_info.min_frame_buffer_count, initial_info.min_src_frame_count); inst->min_src_buf_count = initial_info.min_src_frame_count + - COMMAND_QUEUE_DEPTH; + WAVE521_COMMAND_QUEUE_DEPTH; ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl, V4L2_CID_MIN_BUFFERS_FOR_OUTPUT); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c b/drivers/media/platform/chips-media/wave5/wave5-vpu.c index 901dbfdf1e4f..7273254ecb03 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c @@ -63,7 +63,13 @@ static void wave5_vpu_handle_irq(void *dev_id) if (irq_reason & BIT(INT_WAVE5_INIT_SEQ) || irq_reason & BIT(INT_WAVE5_ENC_SET_PARAM)) { - if (seq_done & BIT(inst->id)) { + if (dev->product_code == WAVE515_CODE && + (cmd_done & BIT(inst->id))) { + cmd_done &= ~BIT(inst->id); + wave5_vdi_write_register(dev, W5_RET_QUEUE_CMD_DONE_INST, + cmd_done); + complete(&inst->irq_done); + } else if (seq_done & BIT(inst->id)) { seq_done &= ~BIT(inst->id); wave5_vdi_write_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO, seq_done); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h index d5c9480242b6..d2370511faf8 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h @@ -18,6 +18,7 @@ #include "wave5-vdi.h" enum product_id { + PRODUCT_ID_515, PRODUCT_ID_521, PRODUCT_ID_511, PRODUCT_ID_517, diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h index d9751eedb0f9..e4bc2e467cb5 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h @@ -8,6 +8,7 @@ #ifndef _VPU_CONFIG_H_ #define _VPU_CONFIG_H_ +#define WAVE515_CODE 0x5150 #define WAVE517_CODE 0x5170 #define WAVE537_CODE 0x5370 #define WAVE511_CODE 0x5110 @@ -21,12 +22,13 @@ ((c) == WAVE517_CODE || (c) == WAVE537_CODE || \ (c) == WAVE511_CODE || (c) == WAVE521_CODE || \ (c) == WAVE521E1_CODE || (c) == WAVE521C_CODE || \ - (c) == WAVE521C_DUAL_CODE); \ + (c) == WAVE521C_DUAL_CODE) || (c) == WAVE515_CODE; \ }) #define WAVE517_WORKBUF_SIZE (2 * 1024 * 1024) #define WAVE521ENC_WORKBUF_SIZE (128 * 1024) //HEVC 128K, AVC 40K #define WAVE521DEC_WORKBUF_SIZE (1784 * 1024) +#define WAVE515DEC_WORKBUF_SIZE (2 * 1024 * 1024) #define MAX_NUM_INSTANCE 32 @@ -49,17 +51,21 @@ /************************************************************************/ #define VLC_BUF_NUM (2) -#define COMMAND_QUEUE_DEPTH (2) +#define WAVE521_COMMAND_QUEUE_DEPTH (2) +#define WAVE515_COMMAND_QUEUE_DEPTH (4) #define W5_REMAP_INDEX0 0 #define W5_REMAP_INDEX1 1 #define W5_REMAP_MAX_SIZE (1024 * 1024) -#define WAVE5_MAX_CODE_BUF_SIZE (2 * 1024 * 1024) -#define WAVE5_TEMPBUF_OFFSET WAVE5_MAX_CODE_BUF_SIZE +#define WAVE521_MAX_CODE_BUF_SIZE (2 * 1024 * 1024) +#define WAVE515_MAX_CODE_BUF_SIZE (1024 * 1024) #define WAVE5_TEMPBUF_SIZE (1024 * 1024) -#define SIZE_COMMON (WAVE5_MAX_CODE_BUF_SIZE + WAVE5_TEMPBUF_SIZE) +#define WAVE521_SIZE_COMMON (WAVE521_MAX_CODE_BUF_SIZE + WAVE5_TEMPBUF_SIZE) +#define WAVE515_ONE_TASKBUF_SIZE (8 * 1024 * 1024) +#define WAVE515_SIZE_COMMON (WAVE515_MAX_CODE_BUF_SIZE + WAVE5_TEMPBUF_SIZE + \ + WAVE515_COMMAND_QUEUE_DEPTH * WAVE515_ONE_TASKBUF_SIZE) //=====4. VPU REPORT MEMORY ======================// diff --git a/drivers/media/platform/chips-media/wave5/wave5.h b/drivers/media/platform/chips-media/wave5/wave5.h index 063028eccd3b..2a29b9164f97 100644 --- a/drivers/media/platform/chips-media/wave5/wave5.h +++ b/drivers/media/platform/chips-media/wave5/wave5.h @@ -22,6 +22,12 @@ */ #define BSOPTION_ENABLE_EXPLICIT_END BIT(0) #define BSOPTION_HIGHLIGHT_STREAM_END BIT(1) +/* + * When RD_PTR_VALID_FLAG is 0 Wave515 ignores RD_PTR value and starts to + * decode from the access unit end position of the last decoded picture in + * bitstream buffer. + */ +#define BSOPTION_RD_PTR_VALID_FLAG BIT(31) /* * Currently the driver only supports hardware with little endian but for source -- cgit v1.2.3 From dacd54eb2da6e3b5e2ba7ff1ce7286c323f24285 Mon Sep 17 00:00:00 2001 From: Devarsh Thakkar Date: Wed, 19 Jun 2024 01:06:46 +0530 Subject: media: dt-bindings: Add Imagination E5010 JPEG Encoder Add dt-bindings for Imagination E5010 JPEG Encoder [1] which is implemented as stateful V4L2 M2M driver. The device supports baseline encoding with two different quantization tables and compression ratio as demanded. Minimum resolution supported is 64x64 and Maximum resolution supported is 8192x8192. Link: https://www.ti.com/lit/pdf/spruj16 [1] (Section 7.6 JPEG Encoder) Co-developed-by: David Huang Signed-off-by: David Huang Signed-off-by: Devarsh Thakkar Reviewed-by: Rob Herring Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- .../bindings/media/img,e5010-jpeg-enc.yaml | 75 ++++++++++++++++++++++ MAINTAINERS | 5 ++ 2 files changed, 80 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/img,e5010-jpeg-enc.yaml diff --git a/Documentation/devicetree/bindings/media/img,e5010-jpeg-enc.yaml b/Documentation/devicetree/bindings/media/img,e5010-jpeg-enc.yaml new file mode 100644 index 000000000000..085020cb9e61 --- /dev/null +++ b/Documentation/devicetree/bindings/media/img,e5010-jpeg-enc.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/img,e5010-jpeg-enc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Imagination E5010 JPEG Encoder + +maintainers: + - Devarsh Thakkar + +description: | + The E5010 is a JPEG encoder from Imagination Technologies implemented on + TI's AM62A SoC. It is capable of real time encoding of YUV420 and YUV422 + inputs to JPEG and M-JPEG. It supports baseline JPEG Encoding up to + 8Kx8K resolution. + +properties: + compatible: + oneOf: + - items: + - const: ti,am62a-jpeg-enc + - const: img,e5010-jpeg-enc + - const: img,e5010-jpeg-enc + + reg: + items: + - description: The E5010 core register region + - description: The E5010 mmu register region + + reg-names: + items: + - const: core + - const: mmu + + power-domains: + maxItems: 1 + + resets: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - reg-names + - interrupts + - clocks + +additionalProperties: false + +examples: + - | + #include + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + jpeg-encoder@fd20000 { + compatible = "img,e5010-jpeg-enc"; + reg = <0x00 0xfd20000 0x00 0x100>, + <0x00 0xfd20200 0x00 0x200>; + reg-names = "core", "mmu"; + clocks = <&k3_clks 201 0>; + power-domains = <&k3_pds 201 TI_SCI_PD_EXCLUSIVE>; + interrupts = ; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 313624f0bd7e..28021c506cce 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10765,6 +10765,11 @@ S: Maintained F: Documentation/devicetree/bindings/auxdisplay/img,ascii-lcd.yaml F: drivers/auxdisplay/img-ascii-lcd.c +IMGTEC JPEG ENCODER DRIVER +M: Devarsh Thakkar +S: Supported +F: Documentation/devicetree/bindings/media/img,e5010-jpeg-enc.yaml + IMGTEC IR DECODER DRIVER S: Orphan F: drivers/media/rc/img-ir/ -- cgit v1.2.3 From a1e2940458853d00c178c842c889e4ae3ef5eaec Mon Sep 17 00:00:00 2001 From: Devarsh Thakkar Date: Wed, 19 Jun 2024 01:06:47 +0530 Subject: media: imagination: Add E5010 JPEG Encoder driver This adds support for stateful V4L2 M2M based driver for Imagination E5010 JPEG Encoder [1] which supports baseline encoding with two different quantization tables and compression ratio as demanded. Support for both contiguous and non-contiguous YUV420 and YUV422 semiplanar formats is added along with alignment restrictions as required by the hardware. System and runtime PM hooks are added in the driver along with v4l2 crop and selection API support. Minimum resolution supported is 64x64 and Maximum resolution supported is 8192x8192. All v4l2-compliance tests are passing [2] : v4l2-compliance -s -f -a -d /dev/video0 -e /dev/video1 Total for e5010 device /dev/video0: 79, Succeeded: 79, Failed: 0, Warnings: 0 NOTE: video1 here is VIVID test pattern generator Also tests [3] were run manually to verify below driver features: - Runtime Power Management - Multi-instance JPEG Encoding - DMABUF import, export support - NV12, NV21, NV16, NV61 video format support - Compression quality S_CTRL - Cropping support using S_SELECTION Existing V4L2 M2M based JPEG drivers namely s5p-jpeg, imx-jpeg and rcar_jpu were referred while making this. TODO: Add MMU and memory tiling support [1]: AM62A TRM (Section 7.6 is for JPEG Encoder) : Link: https://www.ti.com/lit/pdf/spruj16 [2]: v4l2-compliance test : Link: https://gist.github.com/devarsht/1f039c631ca953a57f405cfce1b69e49 [3]: E5010 JPEG Encoder Manual tests : Performance: Link: https://gist.github.com/devarsht/c40672944fd71c9a53ab55adbfd9e28b Functionality: Link: https://gist.github.com/devarsht/8e88fcaabff016bb2bac83d89c9d23ce Compression Quality: Link: https://gist.github.com/devarsht/cbcc7cd97e8c48ba1486caa2b7884655 Multi Instance: Link: https://gist.github.com/devarsht/22c2fca08cd3441fb40f2c7a4cebc95a Crop support: Link: https://gist.github.com/devarsht/de6f5142f678bb1a5338abfd9f814abd Runtime PM: Link: https://gist.github.com/devarsht/70cd95d4440ddc678489d93885ddd4dd Co-developed-by: David Huang Signed-off-by: David Huang Signed-off-by: Devarsh Thakkar Reviewed-by: Benjamin Gaignard Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- MAINTAINERS | 2 + drivers/media/platform/Kconfig | 1 + drivers/media/platform/Makefile | 1 + drivers/media/platform/imagination/Kconfig | 12 + drivers/media/platform/imagination/Makefile | 3 + .../media/platform/imagination/e5010-core-regs.h | 585 +++++++ .../media/platform/imagination/e5010-jpeg-enc-hw.c | 267 +++ .../media/platform/imagination/e5010-jpeg-enc-hw.h | 42 + .../media/platform/imagination/e5010-jpeg-enc.c | 1741 ++++++++++++++++++++ .../media/platform/imagination/e5010-jpeg-enc.h | 168 ++ .../media/platform/imagination/e5010-mmu-regs.h | 311 ++++ 11 files changed, 3133 insertions(+) create mode 100644 drivers/media/platform/imagination/Kconfig create mode 100644 drivers/media/platform/imagination/Makefile create mode 100644 drivers/media/platform/imagination/e5010-core-regs.h create mode 100644 drivers/media/platform/imagination/e5010-jpeg-enc-hw.c create mode 100644 drivers/media/platform/imagination/e5010-jpeg-enc-hw.h create mode 100644 drivers/media/platform/imagination/e5010-jpeg-enc.c create mode 100644 drivers/media/platform/imagination/e5010-jpeg-enc.h create mode 100644 drivers/media/platform/imagination/e5010-mmu-regs.h diff --git a/MAINTAINERS b/MAINTAINERS index 28021c506cce..be05b756c66b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10767,8 +10767,10 @@ F: drivers/auxdisplay/img-ascii-lcd.c IMGTEC JPEG ENCODER DRIVER M: Devarsh Thakkar +L: linux-media@vger.kernel.org S: Supported F: Documentation/devicetree/bindings/media/img,e5010-jpeg-enc.yaml +F: drivers/media/platform/imagination/e5010* IMGTEC IR DECODER DRIVER S: Orphan diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 2d79bfc68c15..6feeebab4b06 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -70,6 +70,7 @@ source "drivers/media/platform/atmel/Kconfig" source "drivers/media/platform/broadcom/Kconfig" source "drivers/media/platform/cadence/Kconfig" source "drivers/media/platform/chips-media/Kconfig" +source "drivers/media/platform/imagination/Kconfig" source "drivers/media/platform/intel/Kconfig" source "drivers/media/platform/marvell/Kconfig" source "drivers/media/platform/mediatek/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index da17301f7439..11a2f2d0c1a1 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -13,6 +13,7 @@ obj-y += atmel/ obj-y += broadcom/ obj-y += cadence/ obj-y += chips-media/ +obj-y += imagination/ obj-y += intel/ obj-y += marvell/ obj-y += mediatek/ diff --git a/drivers/media/platform/imagination/Kconfig b/drivers/media/platform/imagination/Kconfig new file mode 100644 index 000000000000..d8d79266ad5d --- /dev/null +++ b/drivers/media/platform/imagination/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +config VIDEO_E5010_JPEG_ENC + tristate "Imagination E5010 JPEG Encoder Driver" + depends on VIDEO_DEV + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + select V4L2_MEM2MEM_DEV + help + This is a video4linux2 M2M driver for Imagination E5010 JPEG encoder, + which supports JPEG and MJPEG baseline encoding of YUV422 and YUV420 + semiplanar video formats, with resolution ranging from 64x64 to 8K x 8K + pixels. The module will be named as e5010_jpeg_enc. diff --git a/drivers/media/platform/imagination/Makefile b/drivers/media/platform/imagination/Makefile new file mode 100644 index 000000000000..d45b85b88575 --- /dev/null +++ b/drivers/media/platform/imagination/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +e5010_jpeg_enc-objs := e5010-jpeg-enc-hw.o e5010-jpeg-enc.o +obj-$(CONFIG_VIDEO_E5010_JPEG_ENC) += e5010_jpeg_enc.o diff --git a/drivers/media/platform/imagination/e5010-core-regs.h b/drivers/media/platform/imagination/e5010-core-regs.h new file mode 100644 index 000000000000..aaec498fe83f --- /dev/null +++ b/drivers/media/platform/imagination/e5010-core-regs.h @@ -0,0 +1,585 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Imagination E5010 JPEG Encoder driver. + * + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: David Huang + * Author: Devarsh Thakkar + */ + +#ifndef _E5010_CORE_REGS_H +#define _E5010_CORE_REGS_H + +#define JASPER_CORE_ID_OFFSET (0x0000) +#define JASPER_CORE_ID_CR_GROUP_ID_MASK (0xFF000000) +#define JASPER_CORE_ID_CR_GROUP_ID_SHIFT (24) +#define JASPER_CORE_ID_CR_CORE_ID_MASK (0x00FF0000) +#define JASPER_CORE_ID_CR_CORE_ID_SHIFT (16) +#define JASPER_CORE_ID_CR_UNIQUE_NUM_MASK (0x0000FFF8) +#define JASPER_CORE_ID_CR_UNIQUE_NUM_SHIFT (3) +#define JASPER_CORE_ID_CR_PELS_PER_CYCLE_MASK (0x00000007) +#define JASPER_CORE_ID_CR_PELS_PER_CYCLE_SHIFT (0) + +#define JASPER_CORE_REV_OFFSET (0x0004) +#define JASPER_CORE_REV_CR_JASPER_DESIGNER_MASK (0xFF000000) +#define JASPER_CORE_REV_CR_JASPER_DESIGNER_SHIFT (24) +#define JASPER_CORE_REV_CR_JASPER_MAJOR_REV_MASK (0x00FF0000) +#define JASPER_CORE_REV_CR_JASPER_MAJOR_REV_SHIFT (16) +#define JASPER_CORE_REV_CR_JASPER_MINOR_REV_MASK (0x0000FF00) +#define JASPER_CORE_REV_CR_JASPER_MINOR_REV_SHIFT (8) +#define JASPER_CORE_REV_CR_JASPER_MAINT_REV_MASK (0x000000FF) +#define JASPER_CORE_REV_CR_JASPER_MAINT_REV_SHIFT (0) + +#define JASPER_INTERRUPT_MASK_OFFSET (0x0008) +#define JASPER_INTERRUPT_MASK_CR_OUTPUT_ADDRESS_ERROR_ENABLE_MASK (0x00000002) +#define JASPER_INTERRUPT_MASK_CR_OUTPUT_ADDRESS_ERROR_ENABLE_SHIFT (1) +#define JASPER_INTERRUPT_MASK_CR_PICTURE_DONE_ENABLE_MASK (0x00000001) +#define JASPER_INTERRUPT_MASK_CR_PICTURE_DONE_ENABLE_SHIFT (0) + +#define JASPER_INTERRUPT_STATUS_OFFSET (0x000C) +#define JASPER_INTERRUPT_STATUS_CR_OUTPUT_ADDRESS_ERROR_IRQ_MASK (0x00000002) +#define JASPER_INTERRUPT_STATUS_CR_OUTPUT_ADDRESS_ERROR_IRQ_SHIFT (1) +#define JASPER_INTERRUPT_STATUS_CR_PICTURE_DONE_IRQ_MASK (0x00000001) +#define JASPER_INTERRUPT_STATUS_CR_PICTURE_DONE_IRQ_SHIFT (0) + +#define JASPER_INTERRUPT_CLEAR_OFFSET (0x0010) +#define JASPER_INTERRUPT_CLEAR_CR_OUTPUT_ERROR_CLEAR_MASK (0x00000002) +#define JASPER_INTERRUPT_CLEAR_CR_OUTPUT_ERROR_CLEAR_SHIFT (1) +#define JASPER_INTERRUPT_CLEAR_CR_PICTURE_DONE_CLEAR_MASK (0x00000001) +#define JASPER_INTERRUPT_CLEAR_CR_PICTURE_DONE_CLEAR_SHIFT (0) + +#define JASPER_CLK_CONTROL_OFFSET (0x0014) +#define JASPER_CLK_CONTROL_CR_JASPER_AUTO_CLKG_ENABLE_MASK (0x00000002) +#define JASPER_CLK_CONTROL_CR_JASPER_AUTO_CLKG_ENABLE_SHIFT (1) +#define JASPER_CLK_CONTROL_CR_JASPER_MAN_CLKG_ENABLE_MASK (0x00000001) +#define JASPER_CLK_CONTROL_CR_JASPER_MAN_CLKG_ENABLE_SHIFT (0) + +#define JASPER_CLK_STATUS_OFFSET (0x0018) +#define JASPER_CLK_STATUS_CR_JASPER_CLKG_STATUS_MASK (0x00000001) +#define JASPER_CLK_STATUS_CR_JASPER_CLKG_STATUS_SHIFT (0) + +#define JASPER_RESET_OFFSET (0x001C) +#define JASPER_RESET_CR_SYS_RESET_MASK (0x00000002) +#define JASPER_RESET_CR_SYS_RESET_SHIFT (1) +#define JASPER_RESET_CR_CORE_RESET_MASK (0x00000001) +#define JASPER_RESET_CR_CORE_RESET_SHIFT (0) + +#define JASPER_CORE_CTRL_OFFSET (0x0020) +#define JASPER_CORE_CTRL_CR_JASPER_ENCODE_START_MASK (0x00000001) +#define JASPER_CORE_CTRL_CR_JASPER_ENCODE_START_SHIFT (0) + +#define JASPER_STATUS_OFFSET (0x0024) +#define JASPER_STATUS_CR_FLUSH_MODE_MASK (0x00000002) +#define JASPER_STATUS_CR_FLUSH_MODE_SHIFT (1) +#define JASPER_STATUS_CR_JASPER_BUSY_MASK (0x00000001) +#define JASPER_STATUS_CR_JASPER_BUSY_SHIFT (0) + +#define JASPER_CRC_CLEAR_OFFSET (0x0028) +#define JASPER_CRC_CLEAR_CR_FRONT_END_CRC_CLEAR_MASK (0x00000001) +#define JASPER_CRC_CLEAR_CR_FRONT_END_CRC_CLEAR_SHIFT (0) +#define JASPER_CRC_CLEAR_CR_DCT_CRC_CLEAR_MASK (0x00000002) +#define JASPER_CRC_CLEAR_CR_DCT_CRC_CLEAR_SHIFT (1) +#define JASPER_CRC_CLEAR_CR_ZZ_CRC_CLEAR_MASK (0x00000004) +#define JASPER_CRC_CLEAR_CR_ZZ_CRC_CLEAR_SHIFT (2) +#define JASPER_CRC_CLEAR_CR_QUANT_CRC_CLEAR_MASK (0x00000008) +#define JASPER_CRC_CLEAR_CR_QUANT_CRC_CLEAR_SHIFT (3) +#define JASPER_CRC_CLEAR_CR_ENTROPY_ENCODER_CRC_CLEAR_MASK (0x00000010) +#define JASPER_CRC_CLEAR_CR_ENTROPY_ENCODER_CRC_CLEAR_SHIFT (4) +#define JASPER_CRC_CLEAR_CR_PACKING_BUFFER_CRC_CLEAR_MASK (0x00000020) +#define JASPER_CRC_CLEAR_CR_PACKING_BUFFER_CRC_CLEAR_SHIFT (5) + +#define JASPER_INPUT_CTRL0_OFFSET (0x002C) +#define JASPER_INPUT_CTRL0_CR_INPUT_CHROMA_ORDER_MASK (0x01000000) +#define JASPER_INPUT_CTRL0_CR_INPUT_CHROMA_ORDER_SHIFT (24) +#define JASPER_INPUT_CTRL0_CR_INPUT_SUBSAMPLING_MASK (0x00030000) +#define JASPER_INPUT_CTRL0_CR_INPUT_SUBSAMPLING_SHIFT (16) +#define JASPER_INPUT_CTRL0_CR_INPUT_SOURCE_MASK (0x00000004) +#define JASPER_INPUT_CTRL0_CR_INPUT_SOURCE_SHIFT (2) + +#define JASPER_INPUT_CTRL1_OFFSET (0x0030) +#define JASPER_INPUT_CTRL1_CR_INPUT_LUMA_STRIDE_MASK (0x1FC00000) +#define JASPER_INPUT_CTRL1_CR_INPUT_LUMA_STRIDE_SHIFT (22) +#define JASPER_INPUT_CTRL1_CR_INPUT_CHROMA_STRIDE_MASK (0x00001FC0) +#define JASPER_INPUT_CTRL1_CR_INPUT_CHROMA_STRIDE_SHIFT (6) + +#define JASPER_MMU_CTRL_OFFSET (0x0034) +#define JASPER_MMU_CTRL_CR_JASPER_TILING_SCHEME_MASK (0x00000002) +#define JASPER_MMU_CTRL_CR_JASPER_TILING_SCHEME_SHIFT (1) +#define JASPER_MMU_CTRL_CR_JASPER_TILING_ENABLE_MASK (0x00000001) +#define JASPER_MMU_CTRL_CR_JASPER_TILING_ENABLE_SHIFT (0) + +#define JASPER_IMAGE_SIZE_OFFSET (0x0038) +#define JASPER_IMAGE_SIZE_CR_IMAGE_VERTICAL_SIZE_MASK (0x1FFF0000) +#define JASPER_IMAGE_SIZE_CR_IMAGE_VERTICAL_SIZE_SHIFT (16) +#define JASPER_IMAGE_SIZE_CR_IMAGE_HORIZONTAL_SIZE_MASK (0x00001FFF) +#define JASPER_IMAGE_SIZE_CR_IMAGE_HORIZONTAL_SIZE_SHIFT (0) + +#define INPUT_LUMA_BASE_OFFSET (0x003C) +#define INPUT_LUMA_BASE_CR_INPUT_LUMA_BASE_MASK (0xFFFFFFC0) +#define INPUT_LUMA_BASE_CR_INPUT_LUMA_BASE_SHIFT (6) + +#define INPUT_CHROMA_BASE_OFFSET (0x0040) +#define INPUT_CHROMA_BASE_CR_INPUT_CHROMA_BASE_MASK (0xFFFFFFC0) +#define INPUT_CHROMA_BASE_CR_INPUT_CHROMA_BASE_SHIFT (6) + +#define JASPER_OUTPUT_BASE_OFFSET (0x0044) +#define JASPER_OUTPUT_BASE_CR_OUTPUT_BASE_MASK (0xFFFFFFFF) +#define JASPER_OUTPUT_BASE_CR_OUTPUT_BASE_SHIFT (0) + +#define JASPER_OUTPUT_SIZE_OFFSET (0x0048) +#define JASPER_OUTPUT_SIZE_CR_OUTPUT_SIZE_MASK (0xFFFFFFFF) +#define JASPER_OUTPUT_SIZE_CR_OUTPUT_SIZE_SHIFT (0) +#define JASPER_OUTPUT_MAX_SIZE_OFFSET (0x004C) +#define JASPER_OUTPUT_MAX_SIZE_CR_OUTPUT_MAX_SIZE_MASK (0xFFFFFFFF) +#define JASPER_OUTPUT_MAX_SIZE_CR_OUTPUT_MAX_SIZE_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE0_OFFSET (0x0050) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_03_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_03_SHIFT (24) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_02_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_02_SHIFT (16) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_01_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_01_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_00_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_00_SHIFT (0) +#define JASPER_LUMA_QUANTIZATION_TABLE1_OFFSET (0x0054) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_07_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_07_SHIFT (24) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_06_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_06_SHIFT (16) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_05_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_05_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_04_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_04_SHIFT (0) +#define JASPER_LUMA_QUANTIZATION_TABLE2_OFFSET (0x0058) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_13_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_13_SHIFT (24) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_12_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_12_SHIFT (16) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_11_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_11_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_10_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_10_SHIFT (0) +#define JASPER_LUMA_QUANTIZATION_TABLE3_OFFSET (0x005C) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_17_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_17_SHIFT (24) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_16_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_16_SHIFT (16) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_15_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_15_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_14_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_14_SHIFT (0) +#define JASPER_LUMA_QUANTIZATION_TABLE4_OFFSET (0x0060) +#define JASPER_LUMA_QUANTIZATION_TABLE4_CR_LUMA_QUANTIZATION_TABLE_21_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE4_CR_LUMA_QUANTIZATION_TABLE_21_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE4_CR_LUMA_QUANTIZATION_TABLE_20_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE4_CR_LUMA_QUANTIZATION_TABLE_20_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE5_OFFSET (0x0064) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_27_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_27_SHIFT (24) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_26_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_26_SHIFT (16) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_25_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_25_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_24_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_24_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE6_OFFSET (0x0068) + +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_33_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_33_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_32_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_32_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_31_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_31_SHIFT (8) +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_30_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_30_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE7_OFFSET (0x006C) + +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_37_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_37_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_36_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_36_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_35_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_35_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_34_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_34_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE8_OFFSET (0x0070) + +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_43_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_43_SHIFT (24) +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_42_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_42_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_41_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_41_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_40_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_40_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE9_OFFSET (0x0074) + +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_47_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_47_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_46_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_46_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_45_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_45_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_44_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_44_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE10_OFFSET (0x0078) + +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_53_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_53_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_52_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_52_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_51_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_51_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_50_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_50_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE11_OFFSET (0x007C) + +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_57_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_57_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_56_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_56_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_55_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_55_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_54_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_54_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE12_OFFSET (0x0080) + +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_63_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_63_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_62_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_62_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_61_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_61_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_60_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_60_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE13_OFFSET (0x0084) + +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_67_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_67_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_66_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_66_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_65_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_65_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_64_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_64_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE14_OFFSET (0x0088) + +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_73_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_73_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_72_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_72_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_71_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_71_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_70_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_70_SHIFT (0) + +#define JASPER_LUMA_QUANTIZATION_TABLE15_OFFSET (0x008C) + +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_77_MASK (0xFF000000) +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_77_SHIFT (24) + +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_76_MASK (0x00FF0000) +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_76_SHIFT (16) + +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_75_MASK (0x0000FF00) +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_75_SHIFT (8) + +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_74_MASK (0x000000FF) +#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_74_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE0_OFFSET (0x0090) + +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_03_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_03_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_02_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_02_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_01_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_01_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_00_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_00_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE1_OFFSET (0x0094) + +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_07_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_07_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_06_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_06_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_05_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_05_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_04_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_04_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE2_OFFSET (0x0098) + +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_13_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_13_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_12_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_12_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_11_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_11_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_10_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_10_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE3_OFFSET (0x009C) + +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_17_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_17_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_16_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_16_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_15_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_15_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_14_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_14_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE4_OFFSET (0x00A0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_23_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_23_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_22_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_22_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_21_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_21_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_20_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_20_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE5_OFFSET (0x00A4) + +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_27_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_27_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_26_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_26_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_25_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_25_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_24_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_24_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE6_OFFSET (0x00A8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_33_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_33_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_32_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_32_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_31_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_31_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_30_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_30_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE7_OFFSET (0x00AC) + +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_37_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_37_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_36_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_36_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_35_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_35_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_34_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_34_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE8_OFFSET (0x00B0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_43_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_43_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_42_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_42_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_41_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_41_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_40_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_40_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE9_OFFSET (0x00B4) + +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_47_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_47_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_46_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_46_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_45_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_45_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_44_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_44_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE10_OFFSET (0x00B8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_53_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_53_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_52_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_52_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_51_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_51_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_50_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_50_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE11_OFFSET (0x00BC) + +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_57_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_57_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_56_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_56_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_55_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_55_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_54_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_54_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE12_OFFSET (0x00C0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_63_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_63_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_62_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_62_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_61_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_61_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_60_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_60_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE13_OFFSET (0x00C4) + +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_67_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_67_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_66_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_66_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_65_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_65_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_64_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_64_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE14_OFFSET (0x00C8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_73_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_73_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_72_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_72_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_71_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_71_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_70_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_70_SHIFT (0) + +#define JASPER_CHROMA_QUANTIZATION_TABLE15_OFFSET (0x00CC) + +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_77_MASK (0xFF000000) +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_77_SHIFT (24) + +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_76_MASK (0x00FF0000) +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_76_SHIFT (16) + +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_75_MASK (0x0000FF00) +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_75_SHIFT (8) + +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_74_MASK (0x000000FF) +#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_74_SHIFT (0) + +#define JASPER_CRC_CTRL_OFFSET (0x00D0) +#define JASPER_CRC_CTRL_JASPER_CRC_ENABLE_MASK (0x00000001) +#define JASPER_CRC_CTRL_JASPER_CRC_ENABLE_SHIFT (0) + +#define JASPER_FRONT_END_CRC_OFFSET (0x00D4) +#define JASPER_FRONT_END_CRC_CR_JASPER_FRONT_END_CRC_OUT_MASK (0xFFFFFFFF) +#define JASPER_FRONT_END_CRC_CR_JASPER_FRONT_END_CRC_OUT_SHIFT (0) + +#define JASPER_DCT_CRC_OFFSET (0x00D8) +#define JASPER_DCT_CRC_CR_JASPER_DCT_CRC_OUT_MASK (0xFFFFFFFF) +#define JASPER_DCT_CRC_CR_JASPER_DCT_CRC_OUT_SHIFT (0) + +#define JASPER_ZZ_CRC_OFFSET (0x00DC) +#define JASPER_ZZ_CRC_CR_JASPER_ZZ_CRC_OUT_MASK (0xFFFFFFFF) +#define JASPER_ZZ_CRC_CR_JASPER_ZZ_CRC_OUT_SHIFT (0) + +#define JASPER_QUANT_CRC_OFFSET (0x00E0) +#define JASPER_QUANT_CRC_CR_JASPER_QUANT_CRC_OUT_MASK (0xFFFFFFFF) +#define JASPER_QUANT_CRC_CR_JASPER_QUANT_CRC_OUT_SHIFT (0) + +#define JASPER_ENTROPY_ENCODER_CRC_OFFSET (0x00E4) +#define JASPER_ENTROPY_ENCODER_CRC_CR_JASPER_ENTROPY_CRC_OUT_MASK (0xFFFFFFFF) +#define JASPER_ENTROPY_ENCODER_CRC_CR_JASPER_ENTROPY_CRC_OUT_SHIFT (0) + +#define JASPER_PACKING_BUFFER_DATA_CRC_OFFSET (0x00E8) +#define JASPER_PACKING_BUFFER_DATA_CRC_CR_JASPER_PACKING_DATA_CRC_OUT_MASK (0xFFFFFFFF) +#define JASPER_PACKING_BUFFER_DATA_CRC_CR_JASPER_PACKING_DATA_CRC_OUT_SHIFT (0) + +#define JASPER_PACKING_BUFFER_ADDR_CRC_OFFSET (0x00EC) +#define JASPER_PACKING_BUFFER_ADDR_CRC_CR_JASPER_PACKING_ADDR_OUT_CRC_MASK (0xFFFFFFFF) +#define JASPER_PACKING_BUFFER_ADDR_CRC_CR_JASPER_PACKING_ADDR_OUT_CRC_SHIFT (0) + +#define JASPER_CORE_BYTE_SIZE (0x00F0) + +#endif diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc-hw.c b/drivers/media/platform/imagination/e5010-jpeg-enc-hw.c new file mode 100644 index 000000000000..56d5941020fa --- /dev/null +++ b/drivers/media/platform/imagination/e5010-jpeg-enc-hw.c @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Imagination E5010 JPEG Encoder driver. + * + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: David Huang + * Author: Devarsh Thakkar + */ + +#include +#include +#include +#include "e5010-jpeg-enc-hw.h" + +static void write_reg_field(void __iomem *base, unsigned int offset, u32 mask, + unsigned int shift, u32 value) +{ + u32 reg; + + value <<= shift; + if (mask != 0xffffffff) { + reg = readl(base + offset); + value = (value & mask) | (reg & ~mask); + } + writel(value, (base + offset)); +} + +static int write_reg_field_not_busy(void __iomem *jasper_base, void __iomem *wr_base, + unsigned int offset, u32 mask, unsigned int shift, + u32 value) +{ + int ret; + u32 val; + + ret = readl_poll_timeout_atomic(jasper_base + JASPER_STATUS_OFFSET, val, + (val & JASPER_STATUS_CR_JASPER_BUSY_MASK) == 0, + 2000, 50000); + if (ret) + return ret; + + write_reg_field(wr_base, offset, mask, shift, value); + + return 0; +} + +void e5010_reset(struct device *dev, void __iomem *core_base, void __iomem *mmu_base) +{ + int ret = 0; + u32 val; + + write_reg_field(core_base, JASPER_RESET_OFFSET, + JASPER_RESET_CR_CORE_RESET_MASK, + JASPER_RESET_CR_CORE_RESET_SHIFT, 1); + + write_reg_field(mmu_base, MMU_MMU_CONTROL1_OFFSET, + MMU_MMU_CONTROL1_MMU_SOFT_RESET_MASK, + MMU_MMU_CONTROL1_MMU_SOFT_RESET_SHIFT, 1); + + ret = readl_poll_timeout_atomic(mmu_base + MMU_MMU_CONTROL1_OFFSET, val, + (val & MMU_MMU_CONTROL1_MMU_SOFT_RESET_MASK) == 0, + 2000, 50000); + if (ret) + dev_warn(dev, "MMU soft reset timed out, forcing system soft reset\n"); + + write_reg_field(core_base, JASPER_RESET_OFFSET, + JASPER_RESET_CR_SYS_RESET_MASK, + JASPER_RESET_CR_SYS_RESET_SHIFT, 1); +} + +void e5010_hw_bypass_mmu(void __iomem *mmu_base, u32 enable) +{ + /* Bypass MMU */ + write_reg_field(mmu_base, + MMU_MMU_ADDRESS_CONTROL_OFFSET, + MMU_MMU_ADDRESS_CONTROL_MMU_BYPASS_MASK, + MMU_MMU_ADDRESS_CONTROL_MMU_BYPASS_SHIFT, + enable); +} + +int e5010_hw_enable_output_address_error_irq(void __iomem *core_base, u32 enable) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_INTERRUPT_MASK_OFFSET, + JASPER_INTERRUPT_MASK_CR_OUTPUT_ADDRESS_ERROR_ENABLE_MASK, + JASPER_INTERRUPT_MASK_CR_OUTPUT_ADDRESS_ERROR_ENABLE_SHIFT, + enable); +} + +bool e5010_hw_pic_done_irq(void __iomem *core_base) +{ + u32 reg; + + reg = readl(core_base + JASPER_INTERRUPT_STATUS_OFFSET); + return reg & JASPER_INTERRUPT_STATUS_CR_PICTURE_DONE_IRQ_MASK; +} + +bool e5010_hw_output_address_irq(void __iomem *core_base) +{ + u32 reg; + + reg = readl(core_base + JASPER_INTERRUPT_STATUS_OFFSET); + return reg & JASPER_INTERRUPT_STATUS_CR_OUTPUT_ADDRESS_ERROR_IRQ_MASK; +} + +int e5010_hw_enable_picture_done_irq(void __iomem *core_base, u32 enable) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_INTERRUPT_MASK_OFFSET, + JASPER_INTERRUPT_MASK_CR_PICTURE_DONE_ENABLE_MASK, + JASPER_INTERRUPT_MASK_CR_PICTURE_DONE_ENABLE_SHIFT, + enable); +} + +int e5010_hw_enable_auto_clock_gating(void __iomem *core_base, u32 enable) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_CLK_CONTROL_OFFSET, + JASPER_CLK_CONTROL_CR_JASPER_AUTO_CLKG_ENABLE_MASK, + JASPER_CLK_CONTROL_CR_JASPER_AUTO_CLKG_ENABLE_SHIFT, + enable); +} + +int e5010_hw_enable_manual_clock_gating(void __iomem *core_base, u32 enable) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_CLK_CONTROL_OFFSET, + JASPER_CLK_CONTROL_CR_JASPER_MAN_CLKG_ENABLE_MASK, + JASPER_CLK_CONTROL_CR_JASPER_MAN_CLKG_ENABLE_SHIFT, 0); +} + +int e5010_hw_enable_crc_check(void __iomem *core_base, u32 enable) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_CRC_CTRL_OFFSET, + JASPER_CRC_CTRL_JASPER_CRC_ENABLE_MASK, + JASPER_CRC_CTRL_JASPER_CRC_ENABLE_SHIFT, enable); +} + +int e5010_hw_set_input_source_to_memory(void __iomem *core_base, u32 set) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_INPUT_CTRL0_OFFSET, + JASPER_INPUT_CTRL0_CR_INPUT_SOURCE_MASK, + JASPER_INPUT_CTRL0_CR_INPUT_SOURCE_SHIFT, set); +} + +int e5010_hw_set_input_luma_addr(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + INPUT_LUMA_BASE_OFFSET, + INPUT_LUMA_BASE_CR_INPUT_LUMA_BASE_MASK, 0, val); +} + +int e5010_hw_set_input_chroma_addr(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + INPUT_CHROMA_BASE_OFFSET, + INPUT_CHROMA_BASE_CR_INPUT_CHROMA_BASE_MASK, 0, val); +} + +int e5010_hw_set_output_base_addr(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_OUTPUT_BASE_OFFSET, + JASPER_OUTPUT_BASE_CR_OUTPUT_BASE_MASK, + JASPER_OUTPUT_BASE_CR_OUTPUT_BASE_SHIFT, val); +} + +int e5010_hw_set_horizontal_size(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_IMAGE_SIZE_OFFSET, + JASPER_IMAGE_SIZE_CR_IMAGE_HORIZONTAL_SIZE_MASK, + JASPER_IMAGE_SIZE_CR_IMAGE_HORIZONTAL_SIZE_SHIFT, + val); +} + +int e5010_hw_set_vertical_size(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_IMAGE_SIZE_OFFSET, + JASPER_IMAGE_SIZE_CR_IMAGE_VERTICAL_SIZE_MASK, + JASPER_IMAGE_SIZE_CR_IMAGE_VERTICAL_SIZE_SHIFT, + val); +} + +int e5010_hw_set_luma_stride(void __iomem *core_base, u32 bytesperline) +{ + u32 val = bytesperline / 64; + + return write_reg_field_not_busy(core_base, core_base, + JASPER_INPUT_CTRL1_OFFSET, + JASPER_INPUT_CTRL1_CR_INPUT_LUMA_STRIDE_MASK, + JASPER_INPUT_CTRL1_CR_INPUT_LUMA_STRIDE_SHIFT, + val); +} + +int e5010_hw_set_chroma_stride(void __iomem *core_base, u32 bytesperline) +{ + u32 val = bytesperline / 64; + + return write_reg_field_not_busy(core_base, core_base, + JASPER_INPUT_CTRL1_OFFSET, + JASPER_INPUT_CTRL1_CR_INPUT_CHROMA_STRIDE_MASK, + JASPER_INPUT_CTRL1_CR_INPUT_CHROMA_STRIDE_SHIFT, + val); +} + +int e5010_hw_set_input_subsampling(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_INPUT_CTRL0_OFFSET, + JASPER_INPUT_CTRL0_CR_INPUT_SUBSAMPLING_MASK, + JASPER_INPUT_CTRL0_CR_INPUT_SUBSAMPLING_SHIFT, + val); +} + +int e5010_hw_set_chroma_order(void __iomem *core_base, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, + JASPER_INPUT_CTRL0_OFFSET, + JASPER_INPUT_CTRL0_CR_INPUT_CHROMA_ORDER_MASK, + JASPER_INPUT_CTRL0_CR_INPUT_CHROMA_ORDER_SHIFT, + val); +} + +void e5010_hw_set_output_max_size(void __iomem *core_base, u32 val) +{ + write_reg_field(core_base, JASPER_OUTPUT_MAX_SIZE_OFFSET, + JASPER_OUTPUT_MAX_SIZE_CR_OUTPUT_MAX_SIZE_MASK, + JASPER_OUTPUT_MAX_SIZE_CR_OUTPUT_MAX_SIZE_SHIFT, + val); +} + +int e5010_hw_set_qpvalue(void __iomem *core_base, u32 offset, u32 val) +{ + return write_reg_field_not_busy(core_base, core_base, offset, 0xffffffff, 0, val); +} + +void e5010_hw_clear_output_error(void __iomem *core_base, u32 clear) +{ + /* Make sure interrupts are clear */ + write_reg_field(core_base, JASPER_INTERRUPT_CLEAR_OFFSET, + JASPER_INTERRUPT_CLEAR_CR_OUTPUT_ERROR_CLEAR_MASK, + JASPER_INTERRUPT_CLEAR_CR_OUTPUT_ERROR_CLEAR_SHIFT, clear); +} + +void e5010_hw_clear_picture_done(void __iomem *core_base, u32 clear) +{ + write_reg_field(core_base, + JASPER_INTERRUPT_CLEAR_OFFSET, + JASPER_INTERRUPT_CLEAR_CR_PICTURE_DONE_CLEAR_MASK, + JASPER_INTERRUPT_CLEAR_CR_PICTURE_DONE_CLEAR_SHIFT, clear); +} + +int e5010_hw_get_output_size(void __iomem *core_base) +{ + return readl(core_base + JASPER_OUTPUT_SIZE_OFFSET); +} + +void e5010_hw_encode_start(void __iomem *core_base, u32 start) +{ + write_reg_field(core_base, JASPER_CORE_CTRL_OFFSET, + JASPER_CORE_CTRL_CR_JASPER_ENCODE_START_MASK, + JASPER_CORE_CTRL_CR_JASPER_ENCODE_START_SHIFT, start); +} diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc-hw.h b/drivers/media/platform/imagination/e5010-jpeg-enc-hw.h new file mode 100644 index 000000000000..781d353c3226 --- /dev/null +++ b/drivers/media/platform/imagination/e5010-jpeg-enc-hw.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Imagination E5010 JPEG Encoder driver. + * + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: David Huang + * Author: Devarsh Thakkar + */ + +#ifndef _E5010_JPEG_ENC_HW_H +#define _E5010_JPEG_ENC_HW_H + +#include "e5010-core-regs.h" +#include "e5010-mmu-regs.h" + +int e5010_hw_enable_output_address_error_irq(void __iomem *core_offset, u32 enable); +int e5010_hw_enable_picture_done_irq(void __iomem *core_offset, u32 enable); +int e5010_hw_enable_auto_clock_gating(void __iomem *core_offset, u32 enable); +int e5010_hw_enable_manual_clock_gating(void __iomem *core_offset, u32 enable); +int e5010_hw_enable_crc_check(void __iomem *core_offset, u32 enable); +int e5010_hw_set_input_source_to_memory(void __iomem *core_offset, u32 set); +int e5010_hw_set_input_luma_addr(void __iomem *core_offset, u32 val); +int e5010_hw_set_input_chroma_addr(void __iomem *core_offset, u32 val); +int e5010_hw_set_output_base_addr(void __iomem *core_offset, u32 val); +int e5010_hw_get_output_size(void __iomem *core_offset); +int e5010_hw_set_horizontal_size(void __iomem *core_offset, u32 val); +int e5010_hw_set_vertical_size(void __iomem *core_offset, u32 val); +int e5010_hw_set_luma_stride(void __iomem *core_offset, u32 bytesperline); +int e5010_hw_set_chroma_stride(void __iomem *core_offset, u32 bytesperline); +int e5010_hw_set_input_subsampling(void __iomem *core_offset, u32 val); +int e5010_hw_set_chroma_order(void __iomem *core_offset, u32 val); +int e5010_hw_set_qpvalue(void __iomem *core_offset, u32 offset, u32 value); +void e5010_reset(struct device *dev, void __iomem *core_offset, void __iomem *mmu_offset); +void e5010_hw_set_output_max_size(void __iomem *core_offset, u32 val); +void e5010_hw_clear_picture_done(void __iomem *core_offset, u32 clear); +void e5010_hw_encode_start(void __iomem *core_offset, u32 start); +void e5010_hw_clear_output_error(void __iomem *core_offset, u32 clear); +void e5010_hw_bypass_mmu(void __iomem *mmu_base, u32 enable); +bool e5010_hw_pic_done_irq(void __iomem *core_base); +bool e5010_hw_output_address_irq(void __iomem *core_base); +#endif diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc.c b/drivers/media/platform/imagination/e5010-jpeg-enc.c new file mode 100644 index 000000000000..abff9e023ed9 --- /dev/null +++ b/drivers/media/platform/imagination/e5010-jpeg-enc.c @@ -0,0 +1,1741 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Imagination E5010 JPEG Encoder driver. + * + * TODO: Add MMU and memory tiling support + * + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: David Huang + * Author: Devarsh Thakkar + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "e5010-jpeg-enc.h" +#include "e5010-jpeg-enc-hw.h" + +/* Luma and chroma qp table to achieve 50% compression quality + * This is as per example in Annex K.1 of ITU-T.81 + */ +static const u8 luma_q_table[64] = { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 +}; + +static const u8 chroma_q_table[64] = { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +}; + +/* Zigzag scan pattern */ +static const u8 zigzag[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +/* + * Contains the data that needs to be sent in the marker segment of an interchange format JPEG + * stream or an abbreviated format table specification data stream. + * Specifies the huffman table used for encoding the luminance DC coefficient differences. + * The table represents Table K.3 of ITU-T.81 + */ +static const u8 luma_dc_table[] = { + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B +}; + +/* + * Contains the data that needs to be sent in the marker segment of an interchange format JPEG + * stream or an abbreviated format table specification data stream. + * Specifies the huffman table used for encoding the luminance AC coefficients. + * The table represents Table K.5 of ITU-T.81 + */ +static const u8 luma_ac_table[] = { + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, + 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, + 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, + 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, + 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, + 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, + 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, + 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA +}; + +/* + * Contains the data that needs to be sent in the marker segment of an interchange format JPEG + * stream or an abbreviated format table specification data stream. + * Specifies the huffman table used for encoding the chrominance DC coefficient differences. + * The table represents Table K.4 of ITU-T.81 + */ +static const u8 chroma_dc_table[] = { + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B +}; + +/* + * Contains the data that needs to be sent in the marker segment of an interchange format JPEG + * stream or an abbreviated format table specification data stream. + * Specifies the huffman table used for encoding the chrominance AC coefficients. + * The table represents Table K.6 of ITU-T.81 + */ +static const u8 chroma_ac_table[] = { + 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, + 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, + 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, + 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, + 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, + 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, + 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, + 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, + 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA +}; + +#define JPEG_LUM_HT 0x00 +#define JPEG_CHR_HT 0x01 +#define JPEG_DC_HT 0x00 +#define JPEG_AC_HT 0x10 + +/* forward declarations */ +static const struct of_device_id e5010_of_match[]; + +static const struct v4l2_file_operations e5010_fops; + +static const struct v4l2_ioctl_ops e5010_ioctl_ops; + +static const struct vb2_ops e5010_video_ops; + +static const struct v4l2_m2m_ops e5010_m2m_ops; + +static struct e5010_fmt e5010_formats[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + .chroma_order = CHROMA_ORDER_CB_CR, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_NV12M, + .num_planes = 2, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + .chroma_order = CHROMA_ORDER_CB_CR, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_NV21, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + .chroma_order = CHROMA_ORDER_CR_CB, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_NV21M, + .num_planes = 2, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420, + .chroma_order = CHROMA_ORDER_CR_CB, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_NV16, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + .chroma_order = CHROMA_ORDER_CB_CR, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_NV16M, + .num_planes = 2, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + .chroma_order = CHROMA_ORDER_CB_CR, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + + }, + { + .fourcc = V4L2_PIX_FMT_NV61, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + .chroma_order = CHROMA_ORDER_CR_CB, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_NV61M, + .num_planes = 2, + .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422, + .chroma_order = CHROMA_ORDER_CR_CB, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, + { + .fourcc = V4L2_PIX_FMT_JPEG, + .num_planes = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .subsampling = 0, + .chroma_order = 0, + .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 16, + MIN_DIMENSION, MAX_DIMENSION, 8 }, + }, +}; + +static unsigned int debug; +module_param(debug, uint, 0644); +MODULE_PARM_DESC(debug, "debug level"); + +#define dprintk(dev, lvl, fmt, arg...) \ + v4l2_dbg(lvl, debug, &(dev)->v4l2_dev, "%s: " fmt, __func__, ## arg) + +static const struct v4l2_event e5010_eos_event = { + .type = V4L2_EVENT_EOS +}; + +static const char *type_name(enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + return "Output"; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + return "Capture"; + default: + return "Invalid"; + } +} + +static struct e5010_q_data *get_queue(struct e5010_context *ctx, enum v4l2_buf_type type) +{ + return (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? &ctx->out_queue : &ctx->cap_queue; +} + +static void calculate_qp_tables(struct e5010_context *ctx) +{ + long long luminosity, contrast; + int quality, i; + + quality = 50 - ctx->quality; + + luminosity = LUMINOSITY * quality / 50; + contrast = CONTRAST * quality / 50; + + if (quality > 0) { + luminosity *= INCREASE; + contrast *= INCREASE; + } + + for (i = 0; i < ARRAY_SIZE(luma_q_table); i++) { + long long delta = chroma_q_table[i] * contrast + luminosity; + int val = (int)(chroma_q_table[i] + delta); + + clamp(val, 1, 255); + ctx->chroma_qp[i] = quality == -50 ? 1 : val; + + delta = luma_q_table[i] * contrast + luminosity; + val = (int)(luma_q_table[i] + delta); + clamp(val, 1, 255); + ctx->luma_qp[i] = quality == -50 ? 1 : val; + } + + ctx->update_qp = true; +} + +static int update_qp_tables(struct e5010_context *ctx) +{ + struct e5010_dev *e5010 = ctx->e5010; + int i, ret = 0; + u32 lvalue, cvalue; + + lvalue = 0; + cvalue = 0; + + for (i = 0; i < QP_TABLE_SIZE; i++) { + lvalue |= ctx->luma_qp[i] << (8 * (i % 4)); + cvalue |= ctx->chroma_qp[i] << (8 * (i % 4)); + if (i % 4 == 3) { + ret |= e5010_hw_set_qpvalue(e5010->core_base, + JASPER_LUMA_QUANTIZATION_TABLE0_OFFSET + + QP_TABLE_FIELD_OFFSET * ((i - 3) / 4), + lvalue); + ret |= e5010_hw_set_qpvalue(e5010->core_base, + JASPER_CHROMA_QUANTIZATION_TABLE0_OFFSET + + QP_TABLE_FIELD_OFFSET * ((i - 3) / 4), + cvalue); + lvalue = 0; + cvalue = 0; + } + } + + return ret; +} + +static int e5010_set_input_subsampling(void __iomem *core_base, int subsampling) +{ + switch (subsampling) { + case V4L2_JPEG_CHROMA_SUBSAMPLING_420: + return e5010_hw_set_input_subsampling(core_base, SUBSAMPLING_420); + case V4L2_JPEG_CHROMA_SUBSAMPLING_422: + return e5010_hw_set_input_subsampling(core_base, SUBSAMPLING_422); + default: + return -EINVAL; + }; +} + +static int e5010_querycap(struct file *file, void *priv, struct v4l2_capability *cap) +{ + strscpy(cap->driver, E5010_MODULE_NAME, sizeof(cap->driver)); + strscpy(cap->card, E5010_MODULE_NAME, sizeof(cap->card)); + + return 0; +} + +static struct e5010_fmt *find_format(struct v4l2_format *f) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(e5010_formats); ++i) { + if (e5010_formats[i].fourcc == f->fmt.pix_mp.pixelformat && + e5010_formats[i].type == f->type) + return &e5010_formats[i]; + } + + return NULL; +} + +static int e5010_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f) +{ + int i, index = 0; + struct e5010_fmt *fmt = NULL; + struct e5010_context *ctx = file->private_data; + + if (!V4L2_TYPE_IS_MULTIPLANAR(f->type)) { + v4l2_err(&ctx->e5010->v4l2_dev, "ENUMFMT with Invalid type: %d\n", f->type); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(e5010_formats); ++i) { + if (e5010_formats[i].type == f->type) { + if (index == f->index) { + fmt = &e5010_formats[i]; + break; + } + index++; + } + } + + if (!fmt) + return -EINVAL; + + f->pixelformat = fmt->fourcc; + return 0; +} + +static int e5010_g_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct e5010_context *ctx = file->private_data; + struct e5010_q_data *queue; + int i; + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt; + + queue = get_queue(ctx, f->type); + + pix_mp->flags = 0; + pix_mp->field = V4L2_FIELD_NONE; + pix_mp->pixelformat = queue->fmt->fourcc; + pix_mp->width = queue->width_adjusted; + pix_mp->height = queue->height_adjusted; + pix_mp->num_planes = queue->fmt->num_planes; + + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + if (!pix_mp->colorspace) + pix_mp->colorspace = V4L2_COLORSPACE_SRGB; + + for (i = 0; i < queue->fmt->num_planes; i++) { + plane_fmt[i].sizeimage = queue->sizeimage[i]; + plane_fmt[i].bytesperline = queue->bytesperline[i]; + } + + } else { + pix_mp->colorspace = V4L2_COLORSPACE_JPEG; + plane_fmt[0].bytesperline = 0; + plane_fmt[0].sizeimage = queue->sizeimage[0]; + } + pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pix_mp->xfer_func = V4L2_XFER_FUNC_DEFAULT; + pix_mp->quantization = V4L2_QUANTIZATION_DEFAULT; + + return 0; +} + +static int e5010_jpeg_try_fmt(struct v4l2_format *f, struct e5010_context *ctx) +{ + struct e5010_fmt *fmt; + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt; + + fmt = find_format(f); + if (!fmt) { + if (V4L2_TYPE_IS_OUTPUT(f->type)) + pix_mp->pixelformat = V4L2_PIX_FMT_NV12; + else + pix_mp->pixelformat = V4L2_PIX_FMT_JPEG; + fmt = find_format(f); + if (!fmt) + return -EINVAL; + } + + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + if (!pix_mp->colorspace) + pix_mp->colorspace = V4L2_COLORSPACE_JPEG; + if (!pix_mp->ycbcr_enc) + pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + if (!pix_mp->quantization) + pix_mp->quantization = V4L2_QUANTIZATION_DEFAULT; + if (!pix_mp->xfer_func) + pix_mp->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + v4l2_apply_frmsize_constraints(&pix_mp->width, + &pix_mp->height, + &fmt->frmsize); + + v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, + pix_mp->width, pix_mp->height); + + } else { + pix_mp->colorspace = V4L2_COLORSPACE_JPEG; + pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pix_mp->quantization = V4L2_QUANTIZATION_DEFAULT; + pix_mp->xfer_func = V4L2_XFER_FUNC_DEFAULT; + v4l2_apply_frmsize_constraints(&pix_mp->width, + &pix_mp->height, + &fmt->frmsize); + plane_fmt[0].sizeimage = pix_mp->width * pix_mp->height * JPEG_MAX_BYTES_PER_PIXEL; + plane_fmt[0].sizeimage += HEADER_SIZE; + plane_fmt[0].bytesperline = 0; + pix_mp->pixelformat = fmt->fourcc; + pix_mp->num_planes = fmt->num_planes; + } + pix_mp->flags = 0; + pix_mp->field = V4L2_FIELD_NONE; + + dprintk(ctx->e5010, 2, + "ctx: 0x%p: format type %s:, wxh: %dx%d (plane0 : %d bytes, plane1 : %d bytes),fmt: %c%c%c%c\n", + ctx, type_name(f->type), pix_mp->width, pix_mp->height, + plane_fmt[0].sizeimage, plane_fmt[1].sizeimage, + (fmt->fourcc & 0xff), + (fmt->fourcc >> 8) & 0xff, + (fmt->fourcc >> 16) & 0xff, + (fmt->fourcc >> 24) & 0xff); + + return 0; +} + +static int e5010_try_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct e5010_context *ctx = file->private_data; + + return e5010_jpeg_try_fmt(f, ctx); +} + +static int e5010_s_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct e5010_context *ctx = file->private_data; + struct vb2_queue *vq; + int ret = 0, i = 0; + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt; + struct e5010_q_data *queue; + struct e5010_fmt *fmt; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + if (vb2_is_busy(vq)) { + v4l2_err(&ctx->e5010->v4l2_dev, "queue busy\n"); + return -EBUSY; + } + + ret = e5010_jpeg_try_fmt(f, ctx); + if (ret) + return ret; + + fmt = find_format(f); + queue = get_queue(ctx, f->type); + + queue->fmt = fmt; + queue->width = pix_mp->width; + queue->height = pix_mp->height; + + if (V4L2_TYPE_IS_OUTPUT(f->type)) { + for (i = 0; i < fmt->num_planes; i++) { + queue->bytesperline[i] = plane_fmt[i].bytesperline; + queue->sizeimage[i] = plane_fmt[i].sizeimage; + } + queue->crop.left = 0; + queue->crop.top = 0; + queue->crop.width = queue->width; + queue->crop.height = queue->height; + } else { + queue->sizeimage[0] = plane_fmt[0].sizeimage; + queue->sizeimage[1] = 0; + queue->bytesperline[0] = 0; + queue->bytesperline[1] = 0; + } + + return 0; +} + +static int e5010_enum_framesizes(struct file *file, void *priv, struct v4l2_frmsizeenum *fsize) +{ + struct v4l2_format f; + struct e5010_fmt *fmt; + + if (fsize->index != 0) + return -EINVAL; + + f.fmt.pix_mp.pixelformat = fsize->pixel_format; + if (f.fmt.pix_mp.pixelformat == V4L2_PIX_FMT_JPEG) + f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + else + f.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + + fmt = find_format(&f); + if (!fmt) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise = fmt->frmsize; + fsize->reserved[0] = 0; + fsize->reserved[1] = 0; + + return 0; +} + +static int e5010_g_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct e5010_context *ctx = file->private_data; + struct e5010_q_data *queue; + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + queue = get_queue(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + + switch (s->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + s->r.left = 0; + s->r.top = 0; + s->r.width = queue->width; + s->r.height = queue->height; + break; + case V4L2_SEL_TGT_CROP: + memcpy(&s->r, &queue->crop, sizeof(s->r)); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int e5010_s_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct e5010_context *ctx = file->private_data; + struct e5010_q_data *queue; + struct vb2_queue *vq; + struct v4l2_rect base_rect; + + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, s->type); + if (!vq) + return -EINVAL; + + if (vb2_is_streaming(vq)) + return -EBUSY; + + if (s->target != V4L2_SEL_TGT_CROP || + s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + queue = get_queue(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + base_rect.top = 0; + base_rect.left = 0; + base_rect.width = queue->width; + base_rect.height = queue->height; + + switch (s->flags) { + case 0: + s->r.width = round_down(s->r.width, queue->fmt->frmsize.step_width); + s->r.height = round_down(s->r.height, queue->fmt->frmsize.step_height); + s->r.left = round_down(s->r.left, queue->fmt->frmsize.step_width); + s->r.top = round_down(s->r.top, 2); + + if (s->r.left + s->r.width > queue->width) + s->r.width = round_down(s->r.width + s->r.left - queue->width, + queue->fmt->frmsize.step_width); + if (s->r.top + s->r.height > queue->height) + s->r.top = round_down(s->r.top + s->r.height - queue->height, 2); + break; + case V4L2_SEL_FLAG_GE: + s->r.width = round_up(s->r.width, queue->fmt->frmsize.step_width); + s->r.height = round_up(s->r.height, queue->fmt->frmsize.step_height); + s->r.left = round_up(s->r.left, queue->fmt->frmsize.step_width); + s->r.top = round_up(s->r.top, 2); + break; + case V4L2_SEL_FLAG_LE: + s->r.width = round_down(s->r.width, queue->fmt->frmsize.step_width); + s->r.height = round_down(s->r.height, queue->fmt->frmsize.step_height); + s->r.left = round_down(s->r.left, queue->fmt->frmsize.step_width); + s->r.top = round_down(s->r.top, 2); + break; + case V4L2_SEL_FLAG_LE | V4L2_SEL_FLAG_GE: + if (!IS_ALIGNED(s->r.width, queue->fmt->frmsize.step_width) || + !IS_ALIGNED(s->r.height, queue->fmt->frmsize.step_height) || + !IS_ALIGNED(s->r.left, queue->fmt->frmsize.step_width) || + !IS_ALIGNED(s->r.top, 2)) + return -ERANGE; + break; + default: + return -EINVAL; + } + + if (!v4l2_rect_enclosed(&s->r, &base_rect)) + return -ERANGE; + + memcpy(&queue->crop, &s->r, sizeof(s->r)); + + if (!v4l2_rect_equal(&s->r, &base_rect)) + queue->crop_set = true; + + dprintk(ctx->e5010, 2, "ctx: 0x%p: crop rectangle: w: %d, h : %d, l : %d, t : %d\n", + ctx, queue->crop.width, queue->crop.height, queue->crop.left, queue->crop.top); + + return 0; +} + +static int e5010_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 0, NULL); + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, sub); + default: + return -EINVAL; + } + + return 0; +} + +static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) +{ + struct e5010_context *ctx = priv; + struct e5010_dev *e5010 = ctx->e5010; + int ret = 0; + + /* src_vq */ + memset(src_vq, 0, sizeof(*src_vq)); + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_DMABUF; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct e5010_buffer); + src_vq->ops = &e5010_video_ops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &e5010->mutex; + src_vq->dev = e5010->v4l2_dev.dev; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + /* dst_vq */ + memset(dst_vq, 0, sizeof(*dst_vq)); + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct e5010_buffer); + dst_vq->ops = &e5010_video_ops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &e5010->mutex; + dst_vq->dev = e5010->v4l2_dev.dev; + + ret = vb2_queue_init(dst_vq); + if (ret) { + vb2_queue_release(src_vq); + return ret; + } + + return 0; +} + +static int e5010_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct e5010_context *ctx = + container_of(ctrl->handler, struct e5010_context, ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + ctx->quality = ctrl->val; + calculate_qp_tables(ctx); + dprintk(ctx->e5010, 2, "ctx: 0x%p compression quality set to : %d\n", ctx, + ctx->quality); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops e5010_ctrl_ops = { + .s_ctrl = e5010_s_ctrl, +}; + +static void e5010_encode_ctrls(struct e5010_context *ctx) +{ + v4l2_ctrl_new_std(&ctx->ctrl_handler, &e5010_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 75); +} + +static int e5010_ctrls_setup(struct e5010_context *ctx) +{ + int err; + + v4l2_ctrl_handler_init(&ctx->ctrl_handler, 1); + + e5010_encode_ctrls(ctx); + + if (ctx->ctrl_handler.error) { + err = ctx->ctrl_handler.error; + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + + return err; + } + + err = v4l2_ctrl_handler_setup(&ctx->ctrl_handler); + if (err) + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + + return err; +} + +static void e5010_jpeg_set_default_params(struct e5010_context *ctx) +{ + struct e5010_q_data *queue; + struct v4l2_format f; + struct e5010_fmt *fmt; + struct v4l2_pix_format_mplane *pix_mp = &f.fmt.pix_mp; + struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt; + int i = 0; + + f.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12; + fmt = find_format(&f); + queue = &ctx->out_queue; + queue->fmt = fmt; + queue->width = DEFAULT_WIDTH; + queue->height = DEFAULT_HEIGHT; + pix_mp->width = queue->width; + pix_mp->height = queue->height; + queue->crop.left = 0; + queue->crop.top = 0; + queue->crop.width = queue->width; + queue->crop.height = queue->height; + v4l2_apply_frmsize_constraints(&pix_mp->width, + &pix_mp->height, + &fmt->frmsize); + v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, + pix_mp->width, pix_mp->height); + for (i = 0; i < fmt->num_planes; i++) { + queue->bytesperline[i] = plane_fmt[i].bytesperline; + queue->sizeimage[i] = plane_fmt[i].sizeimage; + } + queue->width_adjusted = pix_mp->width; + queue->height_adjusted = pix_mp->height; + + f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_JPEG; + fmt = find_format(&f); + queue = &ctx->cap_queue; + queue->fmt = fmt; + queue->width = DEFAULT_WIDTH; + queue->height = DEFAULT_HEIGHT; + pix_mp->width = queue->width; + pix_mp->height = queue->height; + v4l2_apply_frmsize_constraints(&pix_mp->width, + &pix_mp->height, + &fmt->frmsize); + queue->sizeimage[0] = pix_mp->width * pix_mp->height * JPEG_MAX_BYTES_PER_PIXEL; + queue->sizeimage[0] += HEADER_SIZE; + queue->sizeimage[1] = 0; + queue->bytesperline[0] = 0; + queue->bytesperline[1] = 0; + queue->width_adjusted = pix_mp->width; + queue->height_adjusted = pix_mp->height; +} + +static int e5010_open(struct file *file) +{ + struct e5010_dev *e5010 = video_drvdata(file); + struct video_device *vdev = video_devdata(file); + struct e5010_context *ctx; + int ret = 0; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + if (mutex_lock_interruptible(&e5010->mutex)) { + ret = -ERESTARTSYS; + goto free; + } + + v4l2_fh_init(&ctx->fh, vdev); + file->private_data = ctx; + v4l2_fh_add(&ctx->fh); + + ctx->e5010 = e5010; + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(e5010->m2m_dev, ctx, queue_init); + if (IS_ERR(ctx->fh.m2m_ctx)) { + v4l2_err(&e5010->v4l2_dev, "failed to init m2m ctx\n"); + ret = PTR_ERR(ctx->fh.m2m_ctx); + goto exit; + } + + ret = e5010_ctrls_setup(ctx); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to setup e5010 jpeg controls\n"); + goto err_ctrls_setup; + } + ctx->fh.ctrl_handler = &ctx->ctrl_handler; + + e5010_jpeg_set_default_params(ctx); + + dprintk(e5010, 1, "Created instance: 0x%p, m2m_ctx: 0x%p\n", ctx, ctx->fh.m2m_ctx); + + mutex_unlock(&e5010->mutex); + return 0; + +err_ctrls_setup: + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); +exit: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + mutex_unlock(&e5010->mutex); +free: + kfree(ctx); + return ret; +} + +static int e5010_release(struct file *file) +{ + struct e5010_dev *e5010 = video_drvdata(file); + struct e5010_context *ctx = file->private_data; + + dprintk(e5010, 1, "Releasing instance: 0x%p, m2m_ctx: 0x%p\n", ctx, ctx->fh.m2m_ctx); + mutex_lock(&e5010->mutex); + v4l2_ctrl_handler_free(&ctx->ctrl_handler); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + mutex_unlock(&e5010->mutex); + + return 0; +} + +static void header_write(struct e5010_context *ctx, u8 *addr, unsigned int *offset, + unsigned int no_bytes, unsigned long bits) +{ + u8 *w_addr = addr + *offset; + int i; + + if ((*offset + no_bytes) > HEADER_SIZE) { + v4l2_warn(&ctx->e5010->v4l2_dev, "%s: %s: %d: Problem writing header. %d > HEADER_SIZE %d\n", + __FILE__, __func__, __LINE__, *offset + no_bytes, HEADER_SIZE); + return; + } + + for (i = no_bytes - 1; i >= 0; i--) + *(w_addr++) = ((u8 *)&bits)[i]; + + *offset += no_bytes; +} + +static void encode_marker_segment(struct e5010_context *ctx, void *addr, unsigned int *offset) +{ + u8 *buffer = (u8 *)addr; + int i; + + header_write(ctx, buffer, offset, 2, START_OF_IMAGE); + header_write(ctx, buffer, offset, 2, DQT_MARKER); + header_write(ctx, buffer, offset, 3, LQPQ << 4); + for (i = 0; i < PELS_IN_BLOCK; i++) + header_write(ctx, buffer, offset, 1, ctx->luma_qp[zigzag[i]]); + + header_write(ctx, buffer, offset, 2, DQT_MARKER); + header_write(ctx, buffer, offset, 3, (LQPQ << 4) | 1); + for (i = 0; i < PELS_IN_BLOCK; i++) + header_write(ctx, buffer, offset, 1, ctx->chroma_qp[zigzag[i]]); + + /* Huffman tables */ + header_write(ctx, buffer, offset, 2, DHT_MARKER); + header_write(ctx, buffer, offset, 2, LH_DC); + header_write(ctx, buffer, offset, 1, JPEG_LUM_HT | JPEG_DC_HT); + for (i = 0 ; i < ARRAY_SIZE(luma_dc_table); i++) + header_write(ctx, buffer, offset, 1, luma_dc_table[i]); + + header_write(ctx, buffer, offset, 2, DHT_MARKER); + header_write(ctx, buffer, offset, 2, LH_AC); + header_write(ctx, buffer, offset, 1, JPEG_LUM_HT | JPEG_AC_HT); + for (i = 0 ; i < ARRAY_SIZE(luma_ac_table); i++) + header_write(ctx, buffer, offset, 1, luma_ac_table[i]); + + header_write(ctx, buffer, offset, 2, DHT_MARKER); + header_write(ctx, buffer, offset, 2, LH_DC); + header_write(ctx, buffer, offset, 1, JPEG_CHR_HT | JPEG_DC_HT); + for (i = 0 ; i < ARRAY_SIZE(chroma_dc_table); i++) + header_write(ctx, buffer, offset, 1, chroma_dc_table[i]); + + header_write(ctx, buffer, offset, 2, DHT_MARKER); + header_write(ctx, buffer, offset, 2, LH_AC); + header_write(ctx, buffer, offset, 1, JPEG_CHR_HT | JPEG_AC_HT); + for (i = 0 ; i < ARRAY_SIZE(chroma_ac_table); i++) + header_write(ctx, buffer, offset, 1, chroma_ac_table[i]); +} + +static void encode_frame_header(struct e5010_context *ctx, void *addr, unsigned int *offset) +{ + u8 *buffer = (u8 *)addr; + + header_write(ctx, buffer, offset, 2, SOF_BASELINE_DCT); + header_write(ctx, buffer, offset, 2, 8 + (3 * UC_NUM_COMP)); + header_write(ctx, buffer, offset, 1, PRECISION); + header_write(ctx, buffer, offset, 2, ctx->out_queue.crop.height); + header_write(ctx, buffer, offset, 2, ctx->out_queue.crop.width); + header_write(ctx, buffer, offset, 1, UC_NUM_COMP); + + /* Luma details */ + header_write(ctx, buffer, offset, 1, 1); + if (ctx->out_queue.fmt->subsampling == V4L2_JPEG_CHROMA_SUBSAMPLING_422) + header_write(ctx, buffer, offset, 1, + HORZ_SAMPLING_FACTOR | (VERT_SAMPLING_FACTOR_422)); + else + header_write(ctx, buffer, offset, 1, + HORZ_SAMPLING_FACTOR | (VERT_SAMPLING_FACTOR_420)); + header_write(ctx, buffer, offset, 1, 0); + /* Chroma details */ + header_write(ctx, buffer, offset, 1, 2); + header_write(ctx, buffer, offset, 1, (HORZ_SAMPLING_FACTOR >> 1) | 1); + header_write(ctx, buffer, offset, 1, 1); + header_write(ctx, buffer, offset, 1, 3); + header_write(ctx, buffer, offset, 1, (HORZ_SAMPLING_FACTOR >> 1) | 1); + header_write(ctx, buffer, offset, 1, 1); +} + +static void jpg_encode_sos_header(struct e5010_context *ctx, void *addr, unsigned int *offset) +{ + u8 *buffer = (u8 *)addr; + int i; + + header_write(ctx, buffer, offset, 2, START_OF_SCAN); + header_write(ctx, buffer, offset, 2, 6 + (COMPONENTS_IN_SCAN << 1)); + header_write(ctx, buffer, offset, 1, COMPONENTS_IN_SCAN); + + for (i = 0; i < COMPONENTS_IN_SCAN; i++) { + header_write(ctx, buffer, offset, 1, i + 1); + if (i == 0) + header_write(ctx, buffer, offset, 1, 0); + else + header_write(ctx, buffer, offset, 1, 17); + } + + header_write(ctx, buffer, offset, 1, 0); + header_write(ctx, buffer, offset, 1, 63); + header_write(ctx, buffer, offset, 1, 0); +} + +static void write_header(struct e5010_context *ctx, void *addr) +{ + unsigned int offset = 0; + + encode_marker_segment(ctx, addr, &offset); + encode_frame_header(ctx, addr, &offset); + jpg_encode_sos_header(ctx, addr, &offset); +} + +static irqreturn_t e5010_irq(int irq, void *data) +{ + struct e5010_dev *e5010 = data; + struct e5010_context *ctx; + int output_size; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + bool pic_done, out_addr_err; + + spin_lock(&e5010->hw_lock); + pic_done = e5010_hw_pic_done_irq(e5010->core_base); + out_addr_err = e5010_hw_output_address_irq(e5010->core_base); + + if (!pic_done && !out_addr_err) { + spin_unlock(&e5010->hw_lock); + return IRQ_NONE; + } + + ctx = v4l2_m2m_get_curr_priv(e5010->m2m_dev); + if (WARN_ON(!ctx)) + goto job_unlock; + + dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); + src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); + if (!dst_buf || !src_buf) { + v4l2_err(&e5010->v4l2_dev, "ctx: 0x%p No source or destination buffer\n", ctx); + goto job_unlock; + } + + if (out_addr_err) { + e5010_hw_clear_output_error(e5010->core_base, 1); + v4l2_warn(&e5010->v4l2_dev, + "ctx: 0x%p Output bitstream size exceeded max size\n", ctx); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, dst_buf->planes[0].length); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); + if (v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, src_buf)) { + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + v4l2_m2m_mark_stopped(ctx->fh.m2m_ctx); + v4l2_event_queue_fh(&ctx->fh, &e5010_eos_event); + dprintk(e5010, 2, "ctx: 0x%p Sending EOS\n", ctx); + } + } + + if (pic_done) { + e5010_hw_clear_picture_done(e5010->core_base, 1); + dprintk(e5010, 3, "ctx: 0x%p Got output bitstream of size %d bytes\n", + ctx, readl(e5010->core_base + JASPER_OUTPUT_SIZE_OFFSET)); + + if (v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, src_buf)) { + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + v4l2_m2m_mark_stopped(ctx->fh.m2m_ctx); + v4l2_event_queue_fh(&ctx->fh, &e5010_eos_event); + dprintk(e5010, 2, "ctx: 0x%p Sending EOS\n", ctx); + } + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + output_size = e5010_hw_get_output_size(e5010->core_base); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, output_size + HEADER_SIZE); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + dprintk(e5010, 3, + "ctx: 0x%p frame done for dst_buf->sequence: %d src_buf->sequence: %d\n", + ctx, dst_buf->sequence, src_buf->sequence); + } + + v4l2_m2m_job_finish(e5010->m2m_dev, ctx->fh.m2m_ctx); + dprintk(e5010, 3, "ctx: 0x%p Finish job\n", ctx); + +job_unlock: + spin_unlock(&e5010->hw_lock); + return IRQ_HANDLED; +} + +static int e5010_init_device(struct e5010_dev *e5010) +{ + int ret = 0; + + /*TODO: Set MMU in bypass mode until support for the same is added in driver*/ + e5010_hw_bypass_mmu(e5010->mmu_base, 1); + + if (e5010_hw_enable_auto_clock_gating(e5010->core_base, 1)) + v4l2_warn(&e5010->v4l2_dev, "failed to enable auto clock gating\n"); + + if (e5010_hw_enable_manual_clock_gating(e5010->core_base, 0)) + v4l2_warn(&e5010->v4l2_dev, "failed to disable manual clock gating\n"); + + if (e5010_hw_enable_crc_check(e5010->core_base, 0)) + v4l2_warn(&e5010->v4l2_dev, "failed to disable CRC check\n"); + + if (e5010_hw_enable_output_address_error_irq(e5010->core_base, 1)) + v4l2_err(&e5010->v4l2_dev, "failed to enable Output Address Error interrupts\n"); + + ret = e5010_hw_set_input_source_to_memory(e5010->core_base, 1); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set input source to memory\n"); + return ret; + } + + ret = e5010_hw_enable_picture_done_irq(e5010->core_base, 1); + if (ret) + v4l2_err(&e5010->v4l2_dev, "failed to enable Picture Done interrupts\n"); + + return ret; +} + +static int e5010_probe(struct platform_device *pdev) +{ + struct e5010_dev *e5010; + int irq, ret = 0; + struct device *dev = &pdev->dev; + + ret = dma_set_mask(dev, DMA_BIT_MASK(32)); + if (ret) + return dev_err_probe(dev, ret, "32-bit consistent DMA enable failed\n"); + + e5010 = devm_kzalloc(dev, sizeof(*e5010), GFP_KERNEL); + if (!e5010) + return -ENOMEM; + + platform_set_drvdata(pdev, e5010); + + e5010->dev = dev; + + mutex_init(&e5010->mutex); + spin_lock_init(&e5010->hw_lock); + + e5010->vdev = video_device_alloc(); + if (!e5010->vdev) { + dev_err(dev, "failed to allocate video device\n"); + return -ENOMEM; + } + + snprintf(e5010->vdev->name, sizeof(e5010->vdev->name), "%s", E5010_MODULE_NAME); + e5010->vdev->fops = &e5010_fops; + e5010->vdev->ioctl_ops = &e5010_ioctl_ops; + e5010->vdev->minor = -1; + e5010->vdev->release = video_device_release; + e5010->vdev->vfl_dir = VFL_DIR_M2M; + e5010->vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; + e5010->vdev->v4l2_dev = &e5010->v4l2_dev; + e5010->vdev->lock = &e5010->mutex; + + ret = v4l2_device_register(dev, &e5010->v4l2_dev); + if (ret) + return dev_err_probe(dev, ret, "failed to register v4l2 device\n"); + + e5010->m2m_dev = v4l2_m2m_init(&e5010_m2m_ops); + if (IS_ERR(e5010->m2m_dev)) { + ret = PTR_ERR(e5010->m2m_dev); + e5010->m2m_dev = NULL; + dev_err_probe(dev, ret, "failed to init mem2mem device\n"); + goto fail_after_v4l2_register; + } + + video_set_drvdata(e5010->vdev, e5010); + + e5010->core_base = devm_platform_ioremap_resource_byname(pdev, "core"); + if (IS_ERR(e5010->core_base)) { + ret = PTR_ERR(e5010->core_base); + dev_err_probe(dev, ret, "Missing 'core' resources area\n"); + goto fail_after_v4l2_register; + } + + e5010->mmu_base = devm_platform_ioremap_resource_byname(pdev, "mmu"); + if (IS_ERR(e5010->mmu_base)) { + ret = PTR_ERR(e5010->mmu_base); + dev_err_probe(dev, ret, "Missing 'mmu' resources area\n"); + goto fail_after_v4l2_register; + } + + e5010->last_context_run = NULL; + + irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(dev, irq, e5010_irq, 0, + E5010_MODULE_NAME, e5010); + if (ret) { + dev_err_probe(dev, ret, "failed to register IRQ %d\n", irq); + goto fail_after_v4l2_register; + } + + e5010->clk = devm_clk_get(dev, NULL); + if (IS_ERR(e5010->clk)) { + ret = PTR_ERR(e5010->clk); + dev_err_probe(dev, ret, "failed to get clock\n"); + goto fail_after_v4l2_register; + } + + pm_runtime_enable(dev); + + ret = video_register_device(e5010->vdev, VFL_TYPE_VIDEO, 0); + if (ret) { + dev_err_probe(dev, ret, "failed to register video device\n"); + goto fail_after_video_register_device; + } + + v4l2_info(&e5010->v4l2_dev, "Device registered as /dev/video%d\n", + e5010->vdev->num); + + return 0; + +fail_after_video_register_device: + v4l2_m2m_release(e5010->m2m_dev); +fail_after_v4l2_register: + v4l2_device_unregister(&e5010->v4l2_dev); + return ret; +} + +static void e5010_remove(struct platform_device *pdev) +{ + struct e5010_dev *e5010 = platform_get_drvdata(pdev); + + pm_runtime_disable(e5010->dev); + video_unregister_device(e5010->vdev); + v4l2_m2m_release(e5010->m2m_dev); + v4l2_device_unregister(&e5010->v4l2_dev); +} + +static void e5010_vb2_buffers_return(struct vb2_queue *q, enum vb2_buffer_state state) +{ + struct vb2_v4l2_buffer *vbuf; + struct e5010_context *ctx = vb2_get_drv_priv(q); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + while ((vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) { + dprintk(ctx->e5010, 2, "ctx: 0x%p, buf type %s | index %d\n", + ctx, type_name(vbuf->vb2_buf.type), vbuf->vb2_buf.index); + v4l2_m2m_buf_done(vbuf, state); + } + } else { + while ((vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) { + dprintk(ctx->e5010, 2, "ctx: 0x%p, buf type %s | index %d\n", + ctx, type_name(vbuf->vb2_buf.type), vbuf->vb2_buf.index); + vb2_set_plane_payload(&vbuf->vb2_buf, 0, 0); + v4l2_m2m_buf_done(vbuf, state); + } + } +} + +static int e5010_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct e5010_context *ctx = vb2_get_drv_priv(vq); + struct e5010_q_data *queue; + int i; + + queue = get_queue(ctx, vq->type); + + if (*nplanes) { + if (*nplanes != queue->fmt->num_planes) + return -EINVAL; + for (i = 0; i < *nplanes; i++) { + if (sizes[i] < queue->sizeimage[i]) + return -EINVAL; + } + return 0; + } + + *nplanes = queue->fmt->num_planes; + for (i = 0; i < *nplanes; i++) + sizes[i] = queue->sizeimage[i]; + + dprintk(ctx->e5010, 2, + "ctx: 0x%p, type %s, buffer(s): %d, planes %d, plane1: bytes %d plane2: %d bytes\n", + ctx, type_name(vq->type), *nbuffers, *nplanes, sizes[0], sizes[1]); + + return 0; +} + +static void e5010_buf_finish(struct vb2_buffer *vb) +{ + struct e5010_context *ctx = vb2_get_drv_priv(vb->vb2_queue); + void *d_addr; + + if (vb->state != VB2_BUF_STATE_DONE || V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) + return; + + d_addr = vb2_plane_vaddr(vb, 0); + write_header(ctx, d_addr); +} + +static int e5010_buf_out_validate(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct e5010_context *ctx = vb2_get_drv_priv(vb->vb2_queue); + + if (vbuf->field != V4L2_FIELD_NONE) + dprintk(ctx->e5010, 1, "ctx: 0x%p, field isn't supported\n", ctx); + + vbuf->field = V4L2_FIELD_NONE; + + return 0; +} + +static int e5010_buf_prepare(struct vb2_buffer *vb) +{ + struct e5010_context *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct e5010_q_data *queue; + int i; + + vbuf->field = V4L2_FIELD_NONE; + + queue = get_queue(ctx, vb->vb2_queue->type); + + for (i = 0; i < queue->fmt->num_planes; i++) { + if (vb2_plane_size(vb, i) < (unsigned long)queue->sizeimage[i]) { + v4l2_err(&ctx->e5010->v4l2_dev, "plane %d too small (%lu < %lu)", i, + vb2_plane_size(vb, i), (unsigned long)queue->sizeimage[i]); + + return -EINVAL; + } + } + + if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) { + vb2_set_plane_payload(vb, 0, 0); + vb2_set_plane_payload(vb, 1, 0); + } + + return 0; +} + +static void e5010_buf_queue(struct vb2_buffer *vb) +{ + struct e5010_context *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) && + vb2_is_streaming(vb->vb2_queue) && + v4l2_m2m_dst_buf_is_last(ctx->fh.m2m_ctx)) { + struct e5010_q_data *queue = get_queue(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + + vbuf->sequence = queue->sequence++; + v4l2_m2m_last_buffer_done(ctx->fh.m2m_ctx, vbuf); + v4l2_event_queue_fh(&ctx->fh, &e5010_eos_event); + return; + } + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); +} + +static int e5010_encoder_cmd(struct file *file, void *priv, + struct v4l2_encoder_cmd *cmd) +{ + struct e5010_context *ctx = file->private_data; + int ret; + struct vb2_queue *cap_vq; + + cap_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + + ret = v4l2_m2m_ioctl_try_encoder_cmd(file, &ctx->fh, cmd); + if (ret < 0) + return ret; + + if (!vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx)) || + !vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx))) + return 0; + + ret = v4l2_m2m_ioctl_encoder_cmd(file, &ctx->fh, cmd); + if (ret < 0) + return ret; + + if (cmd->cmd == V4L2_ENC_CMD_STOP && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) + v4l2_event_queue_fh(&ctx->fh, &e5010_eos_event); + + if (cmd->cmd == V4L2_ENC_CMD_START && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) + vb2_clear_last_buffer_dequeued(cap_vq); + + return 0; +} + +static int e5010_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct e5010_context *ctx = vb2_get_drv_priv(q); + int ret; + + struct e5010_q_data *queue = get_queue(ctx, q->type); + + v4l2_m2m_update_start_streaming_state(ctx->fh.m2m_ctx, q); + queue->sequence = 0; + + ret = pm_runtime_resume_and_get(ctx->e5010->dev); + if (ret < 0) { + v4l2_err(&ctx->e5010->v4l2_dev, "failed to power up jpeg\n"); + goto fail; + } + + ret = e5010_init_device(ctx->e5010); + if (ret) { + v4l2_err(&ctx->e5010->v4l2_dev, "failed to Enable e5010 device\n"); + goto fail; + } + + return 0; + +fail: + e5010_vb2_buffers_return(q, VB2_BUF_STATE_QUEUED); + + return ret; +} + +static void e5010_stop_streaming(struct vb2_queue *q) +{ + struct e5010_context *ctx = vb2_get_drv_priv(q); + + e5010_vb2_buffers_return(q, VB2_BUF_STATE_ERROR); + + if (V4L2_TYPE_IS_OUTPUT(q->type)) + v4l2_m2m_update_stop_streaming_state(ctx->fh.m2m_ctx, q); + + if (V4L2_TYPE_IS_OUTPUT(q->type) && + v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) { + v4l2_event_queue_fh(&ctx->fh, &e5010_eos_event); + } + + pm_runtime_put_sync(ctx->e5010->dev); +} + +static void e5010_device_run(void *priv) +{ + struct e5010_context *ctx = priv; + struct e5010_dev *e5010 = ctx->e5010; + struct vb2_v4l2_buffer *s_vb, *d_vb; + u32 reg = 0; + int ret = 0, luma_crop_offset = 0, chroma_crop_offset = 0; + unsigned long flags; + int num_planes = ctx->out_queue.fmt->num_planes; + + spin_lock_irqsave(&e5010->hw_lock, flags); + s_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); + WARN_ON(!s_vb); + d_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); + WARN_ON(!d_vb); + if (!s_vb || !d_vb) + goto no_ready_buf_err; + + s_vb->sequence = ctx->out_queue.sequence++; + d_vb->sequence = ctx->cap_queue.sequence++; + + v4l2_m2m_buf_copy_metadata(s_vb, d_vb, false); + + if (ctx != e5010->last_context_run || ctx->update_qp) { + dprintk(e5010, 1, "ctx updated: 0x%p -> 0x%p, updating qp tables\n", + e5010->last_context_run, ctx); + ret = update_qp_tables(ctx); + } + + if (ret) { + ctx->update_qp = true; + v4l2_err(&e5010->v4l2_dev, "failed to update QP tables\n"); + goto device_busy_err; + } else { + e5010->last_context_run = ctx; + ctx->update_qp = false; + } + + /* Set I/O Buffer addresses */ + reg = (u32)vb2_dma_contig_plane_dma_addr(&s_vb->vb2_buf, 0); + + if (ctx->out_queue.crop_set) { + luma_crop_offset = ctx->out_queue.bytesperline[0] * ctx->out_queue.crop.top + + ctx->out_queue.crop.left; + + if (ctx->out_queue.fmt->subsampling == V4L2_JPEG_CHROMA_SUBSAMPLING_422) { + chroma_crop_offset = + ctx->out_queue.bytesperline[0] * ctx->out_queue.crop.top + + ctx->out_queue.crop.left; + } else { + chroma_crop_offset = + ctx->out_queue.bytesperline[0] * ctx->out_queue.crop.top / 2 + + ctx->out_queue.crop.left; + } + + dprintk(e5010, 1, "Luma crop offset : %x, chroma crop offset : %x\n", + luma_crop_offset, chroma_crop_offset); + } + + ret = e5010_hw_set_input_luma_addr(e5010->core_base, reg + luma_crop_offset); + if (ret || !reg) { + v4l2_err(&e5010->v4l2_dev, "failed to set input luma address\n"); + goto device_busy_err; + } + + if (num_planes == 1) + reg += (ctx->out_queue.bytesperline[0]) * (ctx->out_queue.height); + else + reg = (u32)vb2_dma_contig_plane_dma_addr(&s_vb->vb2_buf, 1); + + dprintk(e5010, 3, + "ctx: 0x%p, luma_addr: 0x%x, chroma_addr: 0x%x, out_addr: 0x%x\n", + ctx, (u32)vb2_dma_contig_plane_dma_addr(&s_vb->vb2_buf, 0) + luma_crop_offset, + reg + chroma_crop_offset, (u32)vb2_dma_contig_plane_dma_addr(&d_vb->vb2_buf, 0)); + + dprintk(e5010, 3, + "ctx: 0x%p, buf indices: src_index: %d, dst_index: %d\n", + ctx, s_vb->vb2_buf.index, d_vb->vb2_buf.index); + + ret = e5010_hw_set_input_chroma_addr(e5010->core_base, reg + chroma_crop_offset); + if (ret || !reg) { + v4l2_err(&e5010->v4l2_dev, "failed to set input chroma address\n"); + goto device_busy_err; + } + + reg = (u32)vb2_dma_contig_plane_dma_addr(&d_vb->vb2_buf, 0); + reg += HEADER_SIZE; + ret = e5010_hw_set_output_base_addr(e5010->core_base, reg); + if (ret || !reg) { + v4l2_err(&e5010->v4l2_dev, "failed to set output base address\n"); + goto device_busy_err; + } + + /* Set input settings */ + ret = e5010_hw_set_horizontal_size(e5010->core_base, ctx->out_queue.crop.width - 1); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set input width\n"); + goto device_busy_err; + } + + ret = e5010_hw_set_vertical_size(e5010->core_base, ctx->out_queue.crop.height - 1); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set input width\n"); + goto device_busy_err; + } + + ret = e5010_hw_set_luma_stride(e5010->core_base, ctx->out_queue.bytesperline[0]); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set luma stride\n"); + goto device_busy_err; + } + + ret = e5010_hw_set_chroma_stride(e5010->core_base, ctx->out_queue.bytesperline[0]); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set chroma stride\n"); + goto device_busy_err; + } + + ret = e5010_set_input_subsampling(e5010->core_base, ctx->out_queue.fmt->subsampling); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set input subsampling\n"); + goto device_busy_err; + } + + ret = e5010_hw_set_chroma_order(e5010->core_base, ctx->out_queue.fmt->chroma_order); + if (ret) { + v4l2_err(&e5010->v4l2_dev, "failed to set chroma order\n"); + goto device_busy_err; + } + + e5010_hw_set_output_max_size(e5010->core_base, d_vb->planes[0].length); + e5010_hw_encode_start(e5010->core_base, 1); + + spin_unlock_irqrestore(&e5010->hw_lock, flags); + + return; + +device_busy_err: + e5010_reset(e5010->dev, e5010->core_base, e5010->mmu_base); + +no_ready_buf_err: + if (s_vb) { + v4l2_m2m_src_buf_remove_by_buf(ctx->fh.m2m_ctx, s_vb); + v4l2_m2m_buf_done(s_vb, VB2_BUF_STATE_ERROR); + } + + if (d_vb) { + v4l2_m2m_dst_buf_remove_by_buf(ctx->fh.m2m_ctx, d_vb); + /* Payload set to 1 since 0 payload can trigger EOS */ + vb2_set_plane_payload(&d_vb->vb2_buf, 0, 1); + v4l2_m2m_buf_done(d_vb, VB2_BUF_STATE_ERROR); + } + v4l2_m2m_job_finish(e5010->m2m_dev, ctx->fh.m2m_ctx); + spin_unlock_irqrestore(&e5010->hw_lock, flags); +} + +#ifdef CONFIG_PM +static int e5010_runtime_resume(struct device *dev) +{ + struct e5010_dev *e5010 = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(e5010->clk); + if (ret < 0) { + v4l2_err(&e5010->v4l2_dev, "failed to enable clock\n"); + return ret; + } + + return 0; +} + +static int e5010_runtime_suspend(struct device *dev) +{ + struct e5010_dev *e5010 = dev_get_drvdata(dev); + + clk_disable_unprepare(e5010->clk); + + return 0; +} +#endif + +#ifdef CONFIG_PM_SLEEP +static int e5010_suspend(struct device *dev) +{ + struct e5010_dev *e5010 = dev_get_drvdata(dev); + + v4l2_m2m_suspend(e5010->m2m_dev); + + return pm_runtime_force_suspend(dev); +} + +static int e5010_resume(struct device *dev) +{ + struct e5010_dev *e5010 = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret < 0) + return ret; + + ret = e5010_init_device(e5010); + if (ret) { + dev_err(dev, "Failed to re-enable e5010 device\n"); + return ret; + } + + v4l2_m2m_resume(e5010->m2m_dev); + + return ret; +} +#endif + +static const struct dev_pm_ops e5010_pm_ops = { + SET_RUNTIME_PM_OPS(e5010_runtime_suspend, + e5010_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(e5010_suspend, e5010_resume) +}; + +static const struct v4l2_ioctl_ops e5010_ioctl_ops = { + .vidioc_querycap = e5010_querycap, + + .vidioc_enum_fmt_vid_cap = e5010_enum_fmt, + .vidioc_g_fmt_vid_cap_mplane = e5010_g_fmt, + .vidioc_try_fmt_vid_cap_mplane = e5010_try_fmt, + .vidioc_s_fmt_vid_cap_mplane = e5010_s_fmt, + + .vidioc_enum_fmt_vid_out = e5010_enum_fmt, + .vidioc_g_fmt_vid_out_mplane = e5010_g_fmt, + .vidioc_try_fmt_vid_out_mplane = e5010_try_fmt, + .vidioc_s_fmt_vid_out_mplane = e5010_s_fmt, + + .vidioc_g_selection = e5010_g_selection, + .vidioc_s_selection = e5010_s_selection, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + + .vidioc_subscribe_event = e5010_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, + .vidioc_encoder_cmd = e5010_encoder_cmd, + + .vidioc_enum_framesizes = e5010_enum_framesizes, +}; + +static const struct vb2_ops e5010_video_ops = { + .queue_setup = e5010_queue_setup, + .buf_queue = e5010_buf_queue, + .buf_finish = e5010_buf_finish, + .buf_prepare = e5010_buf_prepare, + .buf_out_validate = e5010_buf_out_validate, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = e5010_start_streaming, + .stop_streaming = e5010_stop_streaming, +}; + +static const struct v4l2_file_operations e5010_fops = { + .owner = THIS_MODULE, + .open = e5010_open, + .release = e5010_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static const struct v4l2_m2m_ops e5010_m2m_ops = { + .device_run = e5010_device_run, +}; + +static const struct of_device_id e5010_of_match[] = { + {.compatible = "img,e5010-jpeg-enc"}, { /* end */}, +}; +MODULE_DEVICE_TABLE(of, e5010_of_match); + +static struct platform_driver e5010_driver = { + .probe = e5010_probe, + .remove_new = e5010_remove, + .driver = { + .name = E5010_MODULE_NAME, + .of_match_table = e5010_of_match, + .pm = &e5010_pm_ops, + }, +}; +module_platform_driver(e5010_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Imagination E5010 JPEG encoder driver"); diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc.h b/drivers/media/platform/imagination/e5010-jpeg-enc.h new file mode 100644 index 000000000000..71f49ead6898 --- /dev/null +++ b/drivers/media/platform/imagination/e5010-jpeg-enc.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Imagination E5010 JPEG Encoder driver. + * + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: David Huang + * Author: Devarsh Thakkar + */ + +#include +#include +#include + +#ifndef _E5010_JPEG_ENC_H +#define _E5010_JPEG_ENC_H + +#define MAX_PLANES 2 +#define HEADER_SIZE 0x025D +#define MIN_DIMENSION 64 +#define MAX_DIMENSION 8192 +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 +#define E5010_MODULE_NAME "e5010" +#define JPEG_MAX_BYTES_PER_PIXEL 2 + +/* JPEG marker definitions */ +#define START_OF_IMAGE 0xFFD8 +#define SOF_BASELINE_DCT 0xFFC0 +#define END_OF_IMAGE 0xFFD9 +#define START_OF_SCAN 0xFFDA + +/* Definitions for the huffman table specification in the Marker segment */ +#define DHT_MARKER 0xFFC4 +#define LH_DC 0x001F +#define LH_AC 0x00B5 + +/* Definitions for the quantization table specification in the Marker segment */ +#define DQT_MARKER 0xFFDB +#define ACMAX 0x03FF +#define DCMAX 0x07FF + +/* Length and precision of the quantization table parameters */ +#define LQPQ 0x00430 +#define QMAX 255 + +/* Misc JPEG header definitions */ +#define UC_NUM_COMP 3 +#define PRECISION 8 +#define HORZ_SAMPLING_FACTOR (2 << 4) +#define VERT_SAMPLING_FACTOR_422 1 +#define VERT_SAMPLING_FACTOR_420 2 +#define COMPONENTS_IN_SCAN 3 +#define PELS_IN_BLOCK 64 + +/* Used for Qp table generation */ +#define LUMINOSITY 10 +#define CONTRAST 1 +#define INCREASE 2 +#define QP_TABLE_SIZE (8 * 8) +#define QP_TABLE_FIELD_OFFSET 0x04 + +/* + * vb2 queue structure + * contains queue data information + * + * @fmt: format info + * @width: frame width + * @height: frame height + * @bytesperline: bytes per line in memory + * @size_image: image size in memory + */ +struct e5010_q_data { + struct e5010_fmt *fmt; + u32 width; + u32 height; + u32 width_adjusted; + u32 height_adjusted; + u32 sizeimage[MAX_PLANES]; + u32 bytesperline[MAX_PLANES]; + u32 sequence; + struct v4l2_rect crop; + bool crop_set; +}; + +/* + * Driver device structure + * Holds all memory handles and global parameters + * Shared by all instances + */ +struct e5010_dev { + struct device *dev; + struct v4l2_device v4l2_dev; + struct v4l2_m2m_dev *m2m_dev; + struct video_device *vdev; + void __iomem *core_base; + void __iomem *mmu_base; + struct clk *clk; + struct e5010_context *last_context_run; + /* Protect access to device data */ + struct mutex mutex; + /* Protect access to hardware*/ + spinlock_t hw_lock; +}; + +/* + * Driver context structure + * One of these exists for every m2m context + * Holds context specific data + */ +struct e5010_context { + struct v4l2_fh fh; + struct e5010_dev *e5010; + struct e5010_q_data out_queue; + struct e5010_q_data cap_queue; + int quality; + bool update_qp; + struct v4l2_ctrl_handler ctrl_handler; + u8 luma_qp[QP_TABLE_SIZE]; + u8 chroma_qp[QP_TABLE_SIZE]; +}; + +/* + * Buffer structure + * Contains info for all buffers + */ +struct e5010_buffer { + struct v4l2_m2m_buffer buffer; +}; + +enum { + CHROMA_ORDER_CB_CR = 0, //UV ordering + CHROMA_ORDER_CR_CB = 1, //VU ordering +}; + +enum { + SUBSAMPLING_420 = 1, + SUBSAMPLING_422 = 2, +}; + +/* + * e5010 format structure + * contains format information + */ +struct e5010_fmt { + u32 fourcc; + unsigned int num_planes; + unsigned int type; + u32 subsampling; + u32 chroma_order; + const struct v4l2_frmsize_stepwise frmsize; +}; + +/* + * struct e5010_ctrl - contains info for each supported v4l2 control + */ +struct e5010_ctrl { + unsigned int cid; + enum v4l2_ctrl_type type; + unsigned char name[32]; + int minimum; + int maximum; + int step; + int default_value; + unsigned char compound; +}; + +#endif diff --git a/drivers/media/platform/imagination/e5010-mmu-regs.h b/drivers/media/platform/imagination/e5010-mmu-regs.h new file mode 100644 index 000000000000..bfba06956cf2 --- /dev/null +++ b/drivers/media/platform/imagination/e5010-mmu-regs.h @@ -0,0 +1,311 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Imagination E5010 JPEG Encoder driver. + * + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/ + * + * Author: David Huang + * Author: Devarsh Thakkar + */ + +#ifndef _E5010_MMU_REGS_H +#define _E5010_MMU_REGS_H + +#define MMU_MMU_DIR_BASE_ADDR_OFFSET (0x0020) +#define MMU_MMU_DIR_BASE_ADDR_STRIDE (4) +#define MMU_MMU_DIR_BASE_ADDR_NO_ENTRIES (4) + +#define MMU_MMU_DIR_BASE_ADDR_MMU_DIR_BASE_ADDR_MASK (0xFFFFFFFF) +#define MMU_MMU_DIR_BASE_ADDR_MMU_DIR_BASE_ADDR_SHIFT (0) + +#define MMU_MMU_TILE_CFG_OFFSET (0x0040) +#define MMU_MMU_TILE_CFG_STRIDE (4) +#define MMU_MMU_TILE_CFG_NO_ENTRIES (4) + +#define MMU_MMU_TILE_CFG_TILE_128INTERLEAVE_MASK (0x00000010) +#define MMU_MMU_TILE_CFG_TILE_128INTERLEAVE_SHIFT (4) + +#define MMU_MMU_TILE_CFG_TILE_ENABLE_MASK (0x00000008) +#define MMU_MMU_TILE_CFG_TILE_ENABLE_SHIFT (3) + +#define MMU_MMU_TILE_CFG_TILE_STRIDE_MASK (0x00000007) +#define MMU_MMU_TILE_CFG_TILE_STRIDE_SHIFT (0) + +#define MMU_MMU_TILE_MIN_ADDR_OFFSET (0x0050) +#define MMU_MMU_TILE_MIN_ADDR_STRIDE (4) +#define MMU_MMU_TILE_MIN_ADDR_NO_ENTRIES (4) + +#define MMU_MMU_TILE_MIN_ADDR_TILE_MIN_ADDR_MASK (0xFFFFFFFF) +#define MMU_MMU_TILE_MIN_ADDR_TILE_MIN_ADDR_SHIFT (0) + +#define MMU_MMU_TILE_MAX_ADDR_OFFSET (0x0060) +#define MMU_MMU_TILE_MAX_ADDR_STRIDE (4) +#define MMU_MMU_TILE_MAX_ADDR_NO_ENTRIES (4) + +#define MMU_MMU_TILE_MAX_ADDR_TILE_MAX_ADDR_MASK (0xFFFFFFFF) +#define MMU_MMU_TILE_MAX_ADDR_TILE_MAX_ADDR_SHIFT (0) + +#define MMU_MMU_CONTROL0_OFFSET (0x0000) + +#define MMU_MMU_CONTROL0_MMU_TILING_SCHEME_MASK (0x00000001) +#define MMU_MMU_CONTROL0_MMU_TILING_SCHEME_SHIFT (0) + +#define MMU_MMU_CONTROL0_MMU_CACHE_POLICY_MASK (0x00000100) +#define MMU_MMU_CONTROL0_MMU_CACHE_POLICY_SHIFT (8) + +#define MMU_MMU_CONTROL0_FORCE_CACHE_POLICY_BYPASS_MASK (0x00000200) +#define MMU_MMU_CONTROL0_FORCE_CACHE_POLICY_BYPASS_SHIFT (9) + +#define MMU_MMU_CONTROL0_STALL_ON_PROTOCOL_FAULT_MASK (0x00001000) +#define MMU_MMU_CONTROL0_STALL_ON_PROTOCOL_FAULT_SHIFT (12) + +#define MMU_MMU_CONTROL1_OFFSET (0x0008) + +#define MMU_MMU_CONTROL1_MMU_FLUSH_MASK (0x00000008) +#define MMU_MMU_CONTROL1_MMU_FLUSH_SHIFT (3) +#define MMU_MMU_CONTROL1_MMU_FLUSH_NO_REPS (4) +#define MMU_MMU_CONTROL1_MMU_FLUSH_SIZE (1) + +#define MMU_MMU_CONTROL1_MMU_INVALDC_MASK (0x00000800) +#define MMU_MMU_CONTROL1_MMU_INVALDC_SHIFT (11) +#define MMU_MMU_CONTROL1_MMU_INVALDC_NO_REPS (4) +#define MMU_MMU_CONTROL1_MMU_INVALDC_SIZE (1) + +#define MMU_MMU_CONTROL1_MMU_FAULT_CLEAR_MASK (0x00010000) +#define MMU_MMU_CONTROL1_MMU_FAULT_CLEAR_SHIFT (16) + +#define MMU_MMU_CONTROL1_PROTOCOL_FAULT_CLEAR_MASK (0x00100000) +#define MMU_MMU_CONTROL1_PROTOCOL_FAULT_CLEAR_SHIFT (20) + +#define MMU_MMU_CONTROL1_MMU_PAUSE_SET_MASK (0x01000000) +#define MMU_MMU_CONTROL1_MMU_PAUSE_SET_SHIFT (24) + +#define MMU_MMU_CONTROL1_MMU_PAUSE_CLEAR_MASK (0x02000000) +#define MMU_MMU_CONTROL1_MMU_PAUSE_CLEAR_SHIFT (25) + +#define MMU_MMU_CONTROL1_MMU_SOFT_RESET_MASK (0x10000000) +#define MMU_MMU_CONTROL1_MMU_SOFT_RESET_SHIFT (28) + +#define MMU_MMU_BANK_INDEX_OFFSET (0x0010) + +#define MMU_MMU_BANK_INDEX_MMU_BANK_INDEX_MASK (0xC0000000) +#define MMU_MMU_BANK_INDEX_MMU_BANK_INDEX_SHIFT (30) +#define MMU_MMU_BANK_INDEX_MMU_BANK_INDEX_NO_REPS (16) +#define MMU_MMU_BANK_INDEX_MMU_BANK_INDEX_SIZE (2) + +#define MMU_REQUEST_PRIORITY_ENABLE_OFFSET (0x0018) + +#define MMU_REQUEST_PRIORITY_ENABLE_CMD_PRIORITY_ENABLE_MASK (0x00008000) +#define MMU_REQUEST_PRIORITY_ENABLE_CMD_PRIORITY_ENABLE_SHIFT (15) +#define MMU_REQUEST_PRIORITY_ENABLE_CMD_PRIORITY_ENABLE_NO_REPS (16) +#define MMU_REQUEST_PRIORITY_ENABLE_CMD_PRIORITY_ENABLE_SIZE (1) + +#define MMU_REQUEST_PRIORITY_ENABLE_CMD_MMU_PRIORITY_ENABLE_MASK (0x00010000) +#define MMU_REQUEST_PRIORITY_ENABLE_CMD_MMU_PRIORITY_ENABLE_SHIFT (16) + +#define MMU_REQUEST_LIMITED_THROUGHPUT_OFFSET (0x001C) + +#define MMU_REQUEST_LIMITED_THROUGHPUT_LIMITED_WORDS_MASK (0x000003FF) +#define MMU_REQUEST_LIMITED_THROUGHPUT_LIMITED_WORDS_SHIFT (0) + +#define MMU_REQUEST_LIMITED_THROUGHPUT_REQUEST_GAP_MASK (0x0FFF0000) +#define MMU_REQUEST_LIMITED_THROUGHPUT_REQUEST_GAP_SHIFT (16) + +#define MMU_MMU_ADDRESS_CONTROL_OFFSET (0x0070) +#define MMU_MMU_ADDRESS_CONTROL_TRUSTED (IMG_TRUE) + +#define MMU_MMU_ADDRESS_CONTROL_MMU_BYPASS_MASK (0x00000001) +#define MMU_MMU_ADDRESS_CONTROL_MMU_BYPASS_SHIFT (0) + +#define MMU_MMU_ADDRESS_CONTROL_MMU_ENABLE_EXT_ADDRESSING_MASK (0x00000010) +#define MMU_MMU_ADDRESS_CONTROL_MMU_ENABLE_EXT_ADDRESSING_SHIFT (4) + +#define MMU_MMU_ADDRESS_CONTROL_UPPER_ADDRESS_FIXED_MASK (0x00FF0000) +#define MMU_MMU_ADDRESS_CONTROL_UPPER_ADDRESS_FIXED_SHIFT (16) + +#define MMU_MMU_CONFIG0_OFFSET (0x0080) + +#define MMU_MMU_CONFIG0_NUM_REQUESTORS_MASK (0x0000000F) +#define MMU_MMU_CONFIG0_NUM_REQUESTORS_SHIFT (0) + +#define MMU_MMU_CONFIG0_EXTENDED_ADDR_RANGE_MASK (0x000000F0) +#define MMU_MMU_CONFIG0_EXTENDED_ADDR_RANGE_SHIFT (4) + +#define MMU_MMU_CONFIG0_GROUP_OVERRIDE_SIZE_MASK (0x00000700) +#define MMU_MMU_CONFIG0_GROUP_OVERRIDE_SIZE_SHIFT (8) + +#define MMU_MMU_CONFIG0_ADDR_COHERENCY_SUPPORTED_MASK (0x00001000) +#define MMU_MMU_CONFIG0_ADDR_COHERENCY_SUPPORTED_SHIFT (12) + +#define MMU_MMU_CONFIG0_MMU_SUPPORTED_MASK (0x00002000) +#define MMU_MMU_CONFIG0_MMU_SUPPORTED_SHIFT (13) + +#define MMU_MMU_CONFIG0_TILE_ADDR_GRANULARITY_MASK (0x001F0000) +#define MMU_MMU_CONFIG0_TILE_ADDR_GRANULARITY_SHIFT (16) + +#define MMU_MMU_CONFIG0_NO_READ_REORDER_MASK (0x00200000) +#define MMU_MMU_CONFIG0_NO_READ_REORDER_SHIFT (21) + +#define MMU_MMU_CONFIG0_TAGS_SUPPORTED_MASK (0xFFC00000) +#define MMU_MMU_CONFIG0_TAGS_SUPPORTED_SHIFT (22) + +#define MMU_MMU_CONFIG1_OFFSET (0x0084) + +#define MMU_MMU_CONFIG1_PAGE_SIZE_MASK (0x0000000F) +#define MMU_MMU_CONFIG1_PAGE_SIZE_SHIFT (0) + +#define MMU_MMU_CONFIG1_PAGE_CACHE_ENTRIES_MASK (0x0000FF00) +#define MMU_MMU_CONFIG1_PAGE_CACHE_ENTRIES_SHIFT (8) + +#define MMU_MMU_CONFIG1_DIR_CACHE_ENTRIES_MASK (0x001F0000) +#define MMU_MMU_CONFIG1_DIR_CACHE_ENTRIES_SHIFT (16) + +#define MMU_MMU_CONFIG1_BANDWIDTH_COUNT_SUPPORTED_MASK (0x01000000) +#define MMU_MMU_CONFIG1_BANDWIDTH_COUNT_SUPPORTED_SHIFT (24) + +#define MMU_MMU_CONFIG1_STALL_COUNT_SUPPORTED_MASK (0x02000000) +#define MMU_MMU_CONFIG1_STALL_COUNT_SUPPORTED_SHIFT (25) + +#define MMU_MMU_CONFIG1_LATENCY_COUNT_SUPPORTED_MASK (0x04000000) +#define MMU_MMU_CONFIG1_LATENCY_COUNT_SUPPORTED_SHIFT (26) + +#define MMU_MMU_STATUS0_OFFSET (0x0088) + +#define MMU_MMU_STATUS0_MMU_PF_N_RW_MASK (0x00000001) +#define MMU_MMU_STATUS0_MMU_PF_N_RW_SHIFT (0) + +#define MMU_MMU_STATUS0_MMU_FAULT_ADDR_MASK (0xFFFFF000) +#define MMU_MMU_STATUS0_MMU_FAULT_ADDR_SHIFT (12) + +#define MMU_MMU_STATUS1_OFFSET (0x008C) + +#define MMU_MMU_STATUS1_MMU_FAULT_REQ_STAT_MASK (0x0000FFFF) +#define MMU_MMU_STATUS1_MMU_FAULT_REQ_STAT_SHIFT (0) + +#define MMU_MMU_STATUS1_MMU_FAULT_REQ_ID_MASK (0x000F0000) +#define MMU_MMU_STATUS1_MMU_FAULT_REQ_ID_SHIFT (16) + +#define MMU_MMU_STATUS1_MMU_FAULT_INDEX_MASK (0x03000000) +#define MMU_MMU_STATUS1_MMU_FAULT_INDEX_SHIFT (24) + +#define MMU_MMU_STATUS1_MMU_FAULT_RNW_MASK (0x10000000) +#define MMU_MMU_STATUS1_MMU_FAULT_RNW_SHIFT (28) + +#define MMU_MMU_MEM_REQ_OFFSET (0x0090) + +#define MMU_MMU_MEM_REQ_TAG_OUTSTANDING_MASK (0x000003FF) +#define MMU_MMU_MEM_REQ_TAG_OUTSTANDING_SHIFT (0) + +#define MMU_MMU_MEM_REQ_EXT_WRRESP_FAULT_MASK (0x00001000) +#define MMU_MMU_MEM_REQ_EXT_WRRESP_FAULT_SHIFT (12) + +#define MMU_MMU_MEM_REQ_EXT_RDRESP_FAULT_MASK (0x00002000) +#define MMU_MMU_MEM_REQ_EXT_RDRESP_FAULT_SHIFT (13) + +#define MMU_MMU_MEM_REQ_EXT_READ_BURST_FAULT_MASK (0x00004000) +#define MMU_MMU_MEM_REQ_EXT_READ_BURST_FAULT_SHIFT (14) + +#define MMU_MMU_MEM_REQ_INT_PROTOCOL_FAULT_MASK (0x80000000) +#define MMU_MMU_MEM_REQ_INT_PROTOCOL_FAULT_SHIFT (31) +#define MMU_MMU_MEM_REQ_INT_PROTOCOL_FAULT_NO_REPS (16) +#define MMU_MMU_MEM_REQ_INT_PROTOCOL_FAULT_SIZE (1) + +#define MMU_MMU_FAULT_SELECT_OFFSET (0x00A0) + +#define MMU_MMU_FAULT_SELECT_MMU_FAULT_SELECT_MASK (0x0000000F) +#define MMU_MMU_FAULT_SELECT_MMU_FAULT_SELECT_SHIFT (0) + +#define MMU_PROTOCOL_FAULT_OFFSET (0x00A8) + +#define MMU_PROTOCOL_FAULT_FAULT_PAGE_BREAK_MASK (0x00000001) +#define MMU_PROTOCOL_FAULT_FAULT_PAGE_BREAK_SHIFT (0) + +#define MMU_PROTOCOL_FAULT_FAULT_WRITE_MASK (0x00000010) +#define MMU_PROTOCOL_FAULT_FAULT_WRITE_SHIFT (4) + +#define MMU_PROTOCOL_FAULT_FAULT_READ_MASK (0x00000020) +#define MMU_PROTOCOL_FAULT_FAULT_READ_SHIFT (5) + +#define MMU_TOTAL_READ_REQ_OFFSET (0x0100) + +#define MMU_TOTAL_READ_REQ_TOTAL_READ_REQ_MASK (0xFFFFFFFF) +#define MMU_TOTAL_READ_REQ_TOTAL_READ_REQ_SHIFT (0) + +#define MMU_TOTAL_WRITE_REQ_OFFSET (0x0104) + +#define MMU_TOTAL_WRITE_REQ_TOTAL_WRITE_REQ_MASK (0xFFFFFFFF) +#define MMU_TOTAL_WRITE_REQ_TOTAL_WRITE_REQ_SHIFT (0) + +#define MMU_READS_LESS_64_REQ_OFFSET (0x0108) + +#define MMU_READS_LESS_64_REQ_READS_LESS_64_REQ_MASK (0xFFFFFFFF) +#define MMU_READS_LESS_64_REQ_READS_LESS_64_REQ_SHIFT (0) + +#define MMU_WRITES_LESS_64_REQ_OFFSET (0x010C) + +#define MMU_WRITES_LESS_64_REQ_WRITES_LESS_64_REQ_MASK (0xFFFFFFFF) +#define MMU_WRITES_LESS_64_REQ_WRITES_LESS_64_REQ_SHIFT (0) + +#define MMU_EXT_CMD_STALL_OFFSET (0x0120) + +#define MMU_EXT_CMD_STALL_EXT_CMD_STALL_MASK (0xFFFFFFFF) +#define MMU_EXT_CMD_STALL_EXT_CMD_STALL_SHIFT (0) + +#define MMU_WRITE_REQ_STALL_OFFSET (0x0124) + +#define MMU_WRITE_REQ_STALL_WRITE_REQ_STALL_MASK (0xFFFFFFFF) +#define MMU_WRITE_REQ_STALL_WRITE_REQ_STALL_SHIFT (0) + +#define MMU_MMU_MISS_STALL_OFFSET (0x0128) + +#define MMU_MMU_MISS_STALL_MMU_MISS_STALL_MASK (0xFFFFFFFF) +#define MMU_MMU_MISS_STALL_MMU_MISS_STALL_SHIFT (0) + +#define MMU_ADDRESS_STALL_OFFSET (0x012C) + +#define MMU_ADDRESS_STALL_ADDRESS_STALL_MASK (0xFFFFFFFF) +#define MMU_ADDRESS_STALL_ADDRESS_STALL_SHIFT (0) + +#define MMU_TAG_STALL_OFFSET (0x0130) + +#define MMU_TAG_STALL_TAG_STALL_MASK (0xFFFFFFFF) +#define MMU_TAG_STALL_TAG_STALL_SHIFT (0) + +#define MMU_PEAK_READ_OUTSTANDING_OFFSET (0x0140) + +#define MMU_PEAK_READ_OUTSTANDING_PEAK_TAG_OUTSTANDING_MASK (0x000003FF) +#define MMU_PEAK_READ_OUTSTANDING_PEAK_TAG_OUTSTANDING_SHIFT (0) + +#define MMU_PEAK_READ_OUTSTANDING_PEAK_READ_LATENCY_MASK (0xFFFF0000) +#define MMU_PEAK_READ_OUTSTANDING_PEAK_READ_LATENCY_SHIFT (16) + +#define MMU_AVERAGE_READ_LATENCY_OFFSET (0x0144) + +#define MMU_AVERAGE_READ_LATENCY_AVERAGE_READ_LATENCY_MASK (0xFFFFFFFF) +#define MMU_AVERAGE_READ_LATENCY_AVERAGE_READ_LATENCY_SHIFT (0) + +#define MMU_STATISTICS_CONTROL_OFFSET (0x0160) + +#define MMU_STATISTICS_CONTROL_BANDWIDTH_STATS_INIT_MASK (0x00000001) +#define MMU_STATISTICS_CONTROL_BANDWIDTH_STATS_INIT_SHIFT (0) + +#define MMU_STATISTICS_CONTROL_STALL_STATS_INIT_MASK (0x00000002) +#define MMU_STATISTICS_CONTROL_STALL_STATS_INIT_SHIFT (1) + +#define MMU_STATISTICS_CONTROL_LATENCY_STATS_INIT_MASK (0x00000004) +#define MMU_STATISTICS_CONTROL_LATENCY_STATS_INIT_SHIFT (2) + +#define MMU_MMU_VERSION_OFFSET (0x01D0) + +#define MMU_MMU_VERSION_MMU_MAJOR_REV_MASK (0x00FF0000) +#define MMU_MMU_VERSION_MMU_MAJOR_REV_SHIFT (16) + +#define MMU_MMU_VERSION_MMU_MINOR_REV_MASK (0x0000FF00) +#define MMU_MMU_VERSION_MMU_MINOR_REV_SHIFT (8) + +#define MMU_MMU_VERSION_MMU_MAINT_REV_MASK (0x000000FF) +#define MMU_MMU_VERSION_MMU_MAINT_REV_SHIFT (0) + +#define MMU_BYTE_SIZE (0x01D4) + +#endif -- cgit v1.2.3 From b5bad839c01e3a2a41056ac0bc07e7b1ea0ba412 Mon Sep 17 00:00:00 2001 From: Devarsh Thakkar Date: Wed, 19 Jun 2024 01:06:48 +0530 Subject: media: v4l2-jpeg: Export reference quantization and huffman tables Export reference quantization and huffman tables as provided in ITU-T.81 so that they can be re-used by other JPEG drivers. These are example tables provided in ITU-T.81 as reference tables and the JPEG encoders are free to use either these or their own proprietary tables. Also add necessary prefixes to be used for huffman tables in global header file. Signed-off-by: Devarsh Thakkar Reviewed-by: Hans Verkuil Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-jpeg.c | 116 ++++++++++++++++++++++++++++++++++++ include/media/v4l2-jpeg.h | 32 ++++++++++ 2 files changed, 148 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-jpeg.c b/drivers/media/v4l2-core/v4l2-jpeg.c index 94435a7b6816..b8bece739d07 100644 --- a/drivers/media/v4l2-core/v4l2-jpeg.c +++ b/drivers/media/v4l2-core/v4l2-jpeg.c @@ -52,6 +52,122 @@ MODULE_LICENSE("GPL"); #define COM 0xfffe /* comment */ #define TEM 0xff01 /* temporary */ +/* Luma and chroma qp tables to achieve 50% compression quality + * This is as per example in Annex K.1 of ITU-T.81 + */ +const u8 v4l2_jpeg_ref_table_luma_qt[V4L2_JPEG_PIXELS_IN_BLOCK] = { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_luma_qt); + +const u8 v4l2_jpeg_ref_table_chroma_qt[V4L2_JPEG_PIXELS_IN_BLOCK] = { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_chroma_qt); + +/* Zigzag scan pattern indexes */ +const u8 v4l2_jpeg_zigzag_scan_index[V4L2_JPEG_PIXELS_IN_BLOCK] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_zigzag_scan_index); + +/* + * Contains the data that needs to be sent in the marker segment of an + * interchange format JPEG stream or an abbreviated format table specification + * data stream. Specifies the huffman table used for encoding the luminance DC + * coefficient differences. The table represents Table K.3 of ITU-T.81 + */ +const u8 v4l2_jpeg_ref_table_luma_dc_ht[V4L2_JPEG_REF_HT_DC_LEN] = { + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_luma_dc_ht); + +/* + * Contains the data that needs to be sent in the marker segment of an + * interchange format JPEG stream or an abbreviated format table specification + * data stream. Specifies the huffman table used for encoding the luminance AC + * coefficients. The table represents Table K.5 of ITU-T.81 + */ +const u8 v4l2_jpeg_ref_table_luma_ac_ht[V4L2_JPEG_REF_HT_AC_LEN] = { + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, + 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, + 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, + 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, + 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, + 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, + 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, + 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, + 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, + 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_luma_ac_ht); + +/* + * Contains the data that needs to be sent in the marker segment of an interchange format JPEG + * stream or an abbreviated format table specification data stream. + * Specifies the huffman table used for encoding the chrominance DC coefficient differences. + * The table represents Table K.4 of ITU-T.81 + */ +const u8 v4l2_jpeg_ref_table_chroma_dc_ht[V4L2_JPEG_REF_HT_DC_LEN] = { + 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_chroma_dc_ht); + +/* + * Contains the data that needs to be sent in the marker segment of an + * interchange format JPEG stream or an abbreviated format table specification + * data stream. Specifies the huffman table used for encoding the chrominance + * AC coefficients. The table represents Table K.6 of ITU-T.81 + */ +const u8 v4l2_jpeg_ref_table_chroma_ac_ht[V4L2_JPEG_REF_HT_AC_LEN] = { + 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, + 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, + 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, + 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, + 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, + 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, + 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, + 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA +}; +EXPORT_SYMBOL_GPL(v4l2_jpeg_ref_table_chroma_ac_ht); + /** * struct jpeg_stream - JPEG byte stream * @curr: current position in stream diff --git a/include/media/v4l2-jpeg.h b/include/media/v4l2-jpeg.h index 2dba843ce3bd..b65658a02e3c 100644 --- a/include/media/v4l2-jpeg.h +++ b/include/media/v4l2-jpeg.h @@ -14,6 +14,30 @@ #define V4L2_JPEG_MAX_COMPONENTS 4 #define V4L2_JPEG_MAX_TABLES 4 +/* + * Prefixes used to generate huffman table class and destination identifiers as + * described below: + * + * V4L2_JPEG_LUM_HT | V4L2_JPEG_DC_HT : Prefix for Luma DC coefficients + * huffman table + * V4L2_JPEG_LUM_HT | V4L2_JPEG_AC_HT : Prefix for Luma AC coefficients + * huffman table + * V4L2_JPEG_CHR_HT | V4L2_JPEG_DC_HT : Prefix for Chroma DC coefficients + * huffman table + * V4L2_JPEG_CHR_HT | V4L2_JPEG_AC_HT : Prefix for Chroma AC coefficients + * huffman table + */ +#define V4L2_JPEG_LUM_HT 0x00 +#define V4L2_JPEG_CHR_HT 0x01 +#define V4L2_JPEG_DC_HT 0x00 +#define V4L2_JPEG_AC_HT 0x10 + +/* Length of reference huffman tables as provided in Table K.3 of ITU-T.81 */ +#define V4L2_JPEG_REF_HT_AC_LEN 178 +#define V4L2_JPEG_REF_HT_DC_LEN 28 + +/* Array size for 8x8 block of samples or DCT coefficient */ +#define V4L2_JPEG_PIXELS_IN_BLOCK 64 /** * struct v4l2_jpeg_reference - reference into the JPEG buffer @@ -154,4 +178,12 @@ int v4l2_jpeg_parse_quantization_tables(void *buf, size_t len, u8 precision, int v4l2_jpeg_parse_huffman_tables(void *buf, size_t len, struct v4l2_jpeg_reference *huffman_tables); +extern const u8 v4l2_jpeg_zigzag_scan_index[V4L2_JPEG_PIXELS_IN_BLOCK]; +extern const u8 v4l2_jpeg_ref_table_luma_qt[V4L2_JPEG_PIXELS_IN_BLOCK]; +extern const u8 v4l2_jpeg_ref_table_chroma_qt[V4L2_JPEG_PIXELS_IN_BLOCK]; +extern const u8 v4l2_jpeg_ref_table_luma_dc_ht[V4L2_JPEG_REF_HT_DC_LEN]; +extern const u8 v4l2_jpeg_ref_table_luma_ac_ht[V4L2_JPEG_REF_HT_AC_LEN]; +extern const u8 v4l2_jpeg_ref_table_chroma_dc_ht[V4L2_JPEG_REF_HT_DC_LEN]; +extern const u8 v4l2_jpeg_ref_table_chroma_ac_ht[V4L2_JPEG_REF_HT_AC_LEN]; + #endif -- cgit v1.2.3 From 7dfa3259d200c674b288d88e26ccc6327ca77525 Mon Sep 17 00:00:00 2001 From: Devarsh Thakkar Date: Wed, 19 Jun 2024 01:06:49 +0530 Subject: media: Documentation: Document v4l2-jpeg helper functions Enable documentation for v4l2-jpeg header related helper functions which are useful for parsing jpeg files while decoding or creating jpeg headers while encoding. Signed-off-by: Devarsh Thakkar Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- Documentation/driver-api/media/v4l2-core.rst | 1 + Documentation/driver-api/media/v4l2-jpeg.rst | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 Documentation/driver-api/media/v4l2-jpeg.rst diff --git a/Documentation/driver-api/media/v4l2-core.rst b/Documentation/driver-api/media/v4l2-core.rst index 58cba831ade5..ad987c34ad2a 100644 --- a/Documentation/driver-api/media/v4l2-core.rst +++ b/Documentation/driver-api/media/v4l2-core.rst @@ -26,3 +26,4 @@ Video4Linux devices v4l2-tuner v4l2-common v4l2-tveeprom + v4l2-jpeg diff --git a/Documentation/driver-api/media/v4l2-jpeg.rst b/Documentation/driver-api/media/v4l2-jpeg.rst new file mode 100644 index 000000000000..af3bc52f865b --- /dev/null +++ b/Documentation/driver-api/media/v4l2-jpeg.rst @@ -0,0 +1,10 @@ +.. SPDX-License-Identifier: GPL-2.0 + +V4L2 JPEG header related functions and data structures +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. kernel-doc:: include/media/v4l2-jpeg.h + :internal: + +.. kernel-doc:: drivers/media/v4l2-core/v4l2-jpeg.c + :export: -- cgit v1.2.3 From ceb9a33bbd94b70cb4f480b03e6b1cdc6ac97bbb Mon Sep 17 00:00:00 2001 From: Devarsh Thakkar Date: Wed, 19 Jun 2024 01:06:50 +0530 Subject: media: imagination: Use exported tables from v4l2-jpeg core Use exported huffman and quantization tables from v4l2-jpeg core library. Signed-off-by: Devarsh Thakkar Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/imagination/Kconfig | 1 + .../media/platform/imagination/e5010-jpeg-enc.c | 153 +++------------------ 2 files changed, 23 insertions(+), 131 deletions(-) diff --git a/drivers/media/platform/imagination/Kconfig b/drivers/media/platform/imagination/Kconfig index d8d79266ad5d..7139ae22219b 100644 --- a/drivers/media/platform/imagination/Kconfig +++ b/drivers/media/platform/imagination/Kconfig @@ -5,6 +5,7 @@ config VIDEO_E5010_JPEG_ENC select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV + select V4L2_JPEG_HELPER help This is a video4linux2 M2M driver for Imagination E5010 JPEG encoder, which supports JPEG and MJPEG baseline encoding of YUV422 and YUV420 diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc.c b/drivers/media/platform/imagination/e5010-jpeg-enc.c index abff9e023ed9..187f2d8abfbb 100644 --- a/drivers/media/platform/imagination/e5010-jpeg-enc.c +++ b/drivers/media/platform/imagination/e5010-jpeg-enc.c @@ -32,116 +32,6 @@ #include "e5010-jpeg-enc.h" #include "e5010-jpeg-enc-hw.h" -/* Luma and chroma qp table to achieve 50% compression quality - * This is as per example in Annex K.1 of ITU-T.81 - */ -static const u8 luma_q_table[64] = { - 16, 11, 10, 16, 24, 40, 51, 61, - 12, 12, 14, 19, 26, 58, 60, 55, - 14, 13, 16, 24, 40, 57, 69, 56, - 14, 17, 22, 29, 51, 87, 80, 62, - 18, 22, 37, 56, 68, 109, 103, 77, - 24, 35, 55, 64, 81, 104, 113, 92, - 49, 64, 78, 87, 103, 121, 120, 101, - 72, 92, 95, 98, 112, 100, 103, 99 -}; - -static const u8 chroma_q_table[64] = { - 17, 18, 24, 47, 99, 99, 99, 99, - 18, 21, 26, 66, 99, 99, 99, 99, - 24, 26, 56, 99, 99, 99, 99, 99, - 47, 66, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99 -}; - -/* Zigzag scan pattern */ -static const u8 zigzag[64] = { - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63 -}; - -/* - * Contains the data that needs to be sent in the marker segment of an interchange format JPEG - * stream or an abbreviated format table specification data stream. - * Specifies the huffman table used for encoding the luminance DC coefficient differences. - * The table represents Table K.3 of ITU-T.81 - */ -static const u8 luma_dc_table[] = { - 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B -}; - -/* - * Contains the data that needs to be sent in the marker segment of an interchange format JPEG - * stream or an abbreviated format table specification data stream. - * Specifies the huffman table used for encoding the luminance AC coefficients. - * The table represents Table K.5 of ITU-T.81 - */ -static const u8 luma_ac_table[] = { - 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, - 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, - 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, - 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, - 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, - 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, - 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, - 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, - 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, - 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, - 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, - 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA -}; - -/* - * Contains the data that needs to be sent in the marker segment of an interchange format JPEG - * stream or an abbreviated format table specification data stream. - * Specifies the huffman table used for encoding the chrominance DC coefficient differences. - * The table represents Table K.4 of ITU-T.81 - */ -static const u8 chroma_dc_table[] = { - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B -}; - -/* - * Contains the data that needs to be sent in the marker segment of an interchange format JPEG - * stream or an abbreviated format table specification data stream. - * Specifies the huffman table used for encoding the chrominance AC coefficients. - * The table represents Table K.6 of ITU-T.81 - */ -static const u8 chroma_ac_table[] = { - 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, - 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, - 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, - 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, - 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, - 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, - 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, - 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, - 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, - 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, - 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA -}; - -#define JPEG_LUM_HT 0x00 -#define JPEG_CHR_HT 0x01 -#define JPEG_DC_HT 0x00 -#define JPEG_AC_HT 0x10 - /* forward declarations */ static const struct of_device_id e5010_of_match[]; @@ -281,15 +171,15 @@ static void calculate_qp_tables(struct e5010_context *ctx) contrast *= INCREASE; } - for (i = 0; i < ARRAY_SIZE(luma_q_table); i++) { - long long delta = chroma_q_table[i] * contrast + luminosity; - int val = (int)(chroma_q_table[i] + delta); + for (i = 0; i < V4L2_JPEG_PIXELS_IN_BLOCK; i++) { + long long delta = v4l2_jpeg_ref_table_chroma_qt[i] * contrast + luminosity; + int val = (int)(v4l2_jpeg_ref_table_chroma_qt[i] + delta); clamp(val, 1, 255); ctx->chroma_qp[i] = quality == -50 ? 1 : val; - delta = luma_q_table[i] * contrast + luminosity; - val = (int)(luma_q_table[i] + delta); + delta = v4l2_jpeg_ref_table_luma_qt[i] * contrast + luminosity; + val = (int)(v4l2_jpeg_ref_table_luma_qt[i] + delta); clamp(val, 1, 255); ctx->luma_qp[i] = quality == -50 ? 1 : val; } @@ -931,38 +821,39 @@ static void encode_marker_segment(struct e5010_context *ctx, void *addr, unsigne header_write(ctx, buffer, offset, 2, START_OF_IMAGE); header_write(ctx, buffer, offset, 2, DQT_MARKER); header_write(ctx, buffer, offset, 3, LQPQ << 4); - for (i = 0; i < PELS_IN_BLOCK; i++) - header_write(ctx, buffer, offset, 1, ctx->luma_qp[zigzag[i]]); + for (i = 0; i < V4L2_JPEG_PIXELS_IN_BLOCK; i++) + header_write(ctx, buffer, offset, 1, ctx->luma_qp[v4l2_jpeg_zigzag_scan_index[i]]); header_write(ctx, buffer, offset, 2, DQT_MARKER); header_write(ctx, buffer, offset, 3, (LQPQ << 4) | 1); - for (i = 0; i < PELS_IN_BLOCK; i++) - header_write(ctx, buffer, offset, 1, ctx->chroma_qp[zigzag[i]]); + for (i = 0; i < V4L2_JPEG_PIXELS_IN_BLOCK; i++) + header_write(ctx, buffer, offset, 1, + ctx->chroma_qp[v4l2_jpeg_zigzag_scan_index[i]]); /* Huffman tables */ header_write(ctx, buffer, offset, 2, DHT_MARKER); header_write(ctx, buffer, offset, 2, LH_DC); - header_write(ctx, buffer, offset, 1, JPEG_LUM_HT | JPEG_DC_HT); - for (i = 0 ; i < ARRAY_SIZE(luma_dc_table); i++) - header_write(ctx, buffer, offset, 1, luma_dc_table[i]); + header_write(ctx, buffer, offset, 1, V4L2_JPEG_LUM_HT | V4L2_JPEG_DC_HT); + for (i = 0 ; i < V4L2_JPEG_REF_HT_DC_LEN; i++) + header_write(ctx, buffer, offset, 1, v4l2_jpeg_ref_table_luma_dc_ht[i]); header_write(ctx, buffer, offset, 2, DHT_MARKER); header_write(ctx, buffer, offset, 2, LH_AC); - header_write(ctx, buffer, offset, 1, JPEG_LUM_HT | JPEG_AC_HT); - for (i = 0 ; i < ARRAY_SIZE(luma_ac_table); i++) - header_write(ctx, buffer, offset, 1, luma_ac_table[i]); + header_write(ctx, buffer, offset, 1, V4L2_JPEG_LUM_HT | V4L2_JPEG_AC_HT); + for (i = 0 ; i < V4L2_JPEG_REF_HT_AC_LEN; i++) + header_write(ctx, buffer, offset, 1, v4l2_jpeg_ref_table_luma_ac_ht[i]); header_write(ctx, buffer, offset, 2, DHT_MARKER); header_write(ctx, buffer, offset, 2, LH_DC); - header_write(ctx, buffer, offset, 1, JPEG_CHR_HT | JPEG_DC_HT); - for (i = 0 ; i < ARRAY_SIZE(chroma_dc_table); i++) - header_write(ctx, buffer, offset, 1, chroma_dc_table[i]); + header_write(ctx, buffer, offset, 1, V4L2_JPEG_CHR_HT | V4L2_JPEG_DC_HT); + for (i = 0 ; i < V4L2_JPEG_REF_HT_DC_LEN; i++) + header_write(ctx, buffer, offset, 1, v4l2_jpeg_ref_table_chroma_dc_ht[i]); header_write(ctx, buffer, offset, 2, DHT_MARKER); header_write(ctx, buffer, offset, 2, LH_AC); - header_write(ctx, buffer, offset, 1, JPEG_CHR_HT | JPEG_AC_HT); - for (i = 0 ; i < ARRAY_SIZE(chroma_ac_table); i++) - header_write(ctx, buffer, offset, 1, chroma_ac_table[i]); + header_write(ctx, buffer, offset, 1, V4L2_JPEG_CHR_HT | V4L2_JPEG_AC_HT); + for (i = 0 ; i < V4L2_JPEG_REF_HT_AC_LEN; i++) + header_write(ctx, buffer, offset, 1, v4l2_jpeg_ref_table_chroma_ac_ht[i]); } static void encode_frame_header(struct e5010_context *ctx, void *addr, unsigned int *offset) -- cgit v1.2.3 From e1bda64a5865113727869fe0303c8d07d004869f Mon Sep 17 00:00:00 2001 From: Devarsh Thakkar Date: Wed, 19 Jun 2024 01:06:51 +0530 Subject: media: verisilicon : Use exported tables from v4l2-jpeg for hantro codec Use v4l2-jpeg core API to import reference quantization and huffman tables used for JPEG Encoding. Signed-off-by: Devarsh Thakkar Reviewed-by: Chen-Yu Tsai Acked-by: Nicolas Dufresne Signed-off-by: Sebastian Fricke Signed-off-by: Hans Verkuil --- drivers/media/platform/verisilicon/Kconfig | 1 + drivers/media/platform/verisilicon/hantro_jpeg.c | 129 +++-------------------- 2 files changed, 14 insertions(+), 116 deletions(-) diff --git a/drivers/media/platform/verisilicon/Kconfig b/drivers/media/platform/verisilicon/Kconfig index 9a34d14c6e40..149d0b32c324 100644 --- a/drivers/media/platform/verisilicon/Kconfig +++ b/drivers/media/platform/verisilicon/Kconfig @@ -12,6 +12,7 @@ config VIDEO_HANTRO select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV select V4L2_H264 + select V4L2_JPEG_HELPER select V4L2_VP9 help Support for the Hantro IP based Video Processing Units present on diff --git a/drivers/media/platform/verisilicon/hantro_jpeg.c b/drivers/media/platform/verisilicon/hantro_jpeg.c index d07b1b449b61..13a60638e43f 100644 --- a/drivers/media/platform/verisilicon/hantro_jpeg.c +++ b/drivers/media/platform/verisilicon/hantro_jpeg.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "hantro_jpeg.h" #include "hantro.h" @@ -24,42 +25,6 @@ #define HUFF_CHROMA_DC_OFF 394 #define HUFF_CHROMA_AC_OFF 427 -/* Default tables from JPEG ITU-T.81 - * (ISO/IEC 10918-1) Annex K, tables K.1 and K.2 - */ -static const unsigned char luma_q_table[] = { - 0x10, 0x0b, 0x0a, 0x10, 0x18, 0x28, 0x33, 0x3d, - 0x0c, 0x0c, 0x0e, 0x13, 0x1a, 0x3a, 0x3c, 0x37, - 0x0e, 0x0d, 0x10, 0x18, 0x28, 0x39, 0x45, 0x38, - 0x0e, 0x11, 0x16, 0x1d, 0x33, 0x57, 0x50, 0x3e, - 0x12, 0x16, 0x25, 0x38, 0x44, 0x6d, 0x67, 0x4d, - 0x18, 0x23, 0x37, 0x40, 0x51, 0x68, 0x71, 0x5c, - 0x31, 0x40, 0x4e, 0x57, 0x67, 0x79, 0x78, 0x65, - 0x48, 0x5c, 0x5f, 0x62, 0x70, 0x64, 0x67, 0x63 -}; - -static const unsigned char chroma_q_table[] = { - 0x11, 0x12, 0x18, 0x2f, 0x63, 0x63, 0x63, 0x63, - 0x12, 0x15, 0x1a, 0x42, 0x63, 0x63, 0x63, 0x63, - 0x18, 0x1a, 0x38, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x2f, 0x42, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 -}; - -static const unsigned char zigzag[] = { - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63 -}; - static const u32 hw_reorder[] = { 0, 8, 16, 24, 1, 9, 17, 25, 32, 40, 48, 56, 33, 41, 49, 57, @@ -71,73 +36,6 @@ static const u32 hw_reorder[] = { 38, 46, 54, 62, 39, 47, 55, 63 }; -/* Huffman tables are shared with CODA */ -static const unsigned char luma_dc_table[] = { - 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, -}; - -static const unsigned char chroma_dc_table[] = { - 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, -}; - -static const unsigned char luma_ac_table[] = { - 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, - 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, - 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, - 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, - 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, - 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, - 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, - 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, - 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, - 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, - 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, - 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, - 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, - 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, - 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, - 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, - 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, - 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, - 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, -}; - -static const unsigned char chroma_ac_table[] = { - 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, - 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, - 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, - 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, - 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, - 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, - 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, - 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, - 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, - 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, - 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, - 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, - 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, - 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, - 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, - 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, - 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, - 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, - 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, -}; - /* For simplicity, we keep a pre-formatted JPEG header, * and we'll use fixed offsets to change the width, height * quantization tables, etc. @@ -292,11 +190,11 @@ jpeg_scale_quant_table(unsigned char *file_q_tab, { int i; - BUILD_BUG_ON(ARRAY_SIZE(zigzag) != JPEG_QUANT_SIZE); + BUILD_BUG_ON(ARRAY_SIZE(v4l2_jpeg_zigzag_scan_index) != JPEG_QUANT_SIZE); BUILD_BUG_ON(ARRAY_SIZE(hw_reorder) != JPEG_QUANT_SIZE); for (i = 0; i < JPEG_QUANT_SIZE; i++) { - file_q_tab[i] = jpeg_scale_qp(tab[zigzag[i]], scale); + file_q_tab[i] = jpeg_scale_qp(tab[v4l2_jpeg_zigzag_scan_index[i]], scale); reordered_q_tab[i] = jpeg_scale_qp(tab[hw_reorder[i]], scale); } } @@ -304,7 +202,6 @@ jpeg_scale_quant_table(unsigned char *file_q_tab, static void jpeg_set_quality(struct hantro_jpeg_ctx *ctx) { int scale; - /* * Non-linear scaling factor: * [5,50] -> [1000..100], [51,100] -> [98..0] @@ -314,15 +211,17 @@ static void jpeg_set_quality(struct hantro_jpeg_ctx *ctx) else scale = 200 - 2 * ctx->quality; - BUILD_BUG_ON(ARRAY_SIZE(luma_q_table) != JPEG_QUANT_SIZE); - BUILD_BUG_ON(ARRAY_SIZE(chroma_q_table) != JPEG_QUANT_SIZE); + BUILD_BUG_ON(ARRAY_SIZE(v4l2_jpeg_ref_table_luma_qt) != JPEG_QUANT_SIZE); + BUILD_BUG_ON(ARRAY_SIZE(v4l2_jpeg_ref_table_chroma_qt) != JPEG_QUANT_SIZE); BUILD_BUG_ON(ARRAY_SIZE(ctx->hw_luma_qtable) != JPEG_QUANT_SIZE); BUILD_BUG_ON(ARRAY_SIZE(ctx->hw_chroma_qtable) != JPEG_QUANT_SIZE); jpeg_scale_quant_table(ctx->buffer + LUMA_QUANT_OFF, - ctx->hw_luma_qtable, luma_q_table, scale); + ctx->hw_luma_qtable, + (const unsigned char *)v4l2_jpeg_ref_table_luma_qt, scale); jpeg_scale_quant_table(ctx->buffer + CHROMA_QUANT_OFF, - ctx->hw_chroma_qtable, chroma_q_table, scale); + ctx->hw_chroma_qtable, + (const unsigned char *)v4l2_jpeg_ref_table_chroma_qt, scale); } void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx) @@ -337,12 +236,10 @@ void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx) buf[WIDTH_OFF + 0] = ctx->width >> 8; buf[WIDTH_OFF + 1] = ctx->width; - memcpy(buf + HUFF_LUMA_DC_OFF, luma_dc_table, sizeof(luma_dc_table)); - memcpy(buf + HUFF_LUMA_AC_OFF, luma_ac_table, sizeof(luma_ac_table)); - memcpy(buf + HUFF_CHROMA_DC_OFF, chroma_dc_table, - sizeof(chroma_dc_table)); - memcpy(buf + HUFF_CHROMA_AC_OFF, chroma_ac_table, - sizeof(chroma_ac_table)); + memcpy(buf + HUFF_LUMA_DC_OFF, v4l2_jpeg_ref_table_luma_dc_ht, V4L2_JPEG_REF_HT_DC_LEN); + memcpy(buf + HUFF_LUMA_AC_OFF, v4l2_jpeg_ref_table_luma_ac_ht, V4L2_JPEG_REF_HT_AC_LEN); + memcpy(buf + HUFF_CHROMA_DC_OFF, v4l2_jpeg_ref_table_chroma_dc_ht, V4L2_JPEG_REF_HT_DC_LEN); + memcpy(buf + HUFF_CHROMA_AC_OFF, v4l2_jpeg_ref_table_chroma_ac_ht, V4L2_JPEG_REF_HT_AC_LEN); jpeg_set_quality(ctx); } -- cgit v1.2.3 From b178aa6f333b07bda0548d7e45085660a112414d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 20 Jun 2024 09:52:26 +0200 Subject: media: b2c2: flexcop-usb: fix flexcop_usb_memory_req smatch generated this warning: drivers/media/usb/b2c2/flexcop-usb.c:199 flexcop_usb_memory_req() warn: iterator 'i' not incremented and indeed the function is not using i or updating buf. The reason this always worked is that this function is called to write just 6 bytes (a MAC address) to the USB device, and so in practice there is only a single chunk written. If we ever would need to write more than one chunk, this function would fail since each chunk would read from or write to the same buf address. Rewrite the function to properly handle this. Signed-off-by: Hans Verkuil --- drivers/media/usb/b2c2/flexcop-usb.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/b2c2/flexcop-usb.c b/drivers/media/usb/b2c2/flexcop-usb.c index 90f1aea99dac..8033622543f2 100644 --- a/drivers/media/usb/b2c2/flexcop-usb.c +++ b/drivers/media/usb/b2c2/flexcop-usb.c @@ -179,7 +179,7 @@ static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb, flexcop_usb_request_t req, flexcop_usb_mem_page_t page_start, u32 addr, int extended, u8 *buf, u32 len) { - int i, ret = 0; + int ret = 0; u16 wMax; u32 pagechunk = 0; @@ -196,7 +196,7 @@ static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb, default: return -EINVAL; } - for (i = 0; i < len;) { + while (len) { pagechunk = min(wMax, bytes_left_to_read_on_page(addr, len)); deb_info("%x\n", (addr & V8_MEMORY_PAGE_MASK) | @@ -206,11 +206,12 @@ static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb, page_start + (addr / V8_MEMORY_PAGE_SIZE), (addr & V8_MEMORY_PAGE_MASK) | (V8_MEMORY_EXTENDED*extended), - &buf[i], pagechunk); + buf, pagechunk); if (ret < 0) return ret; addr += pagechunk; + buf += pagechunk; len -= pagechunk; } return 0; -- cgit v1.2.3 From 4ea1a3bfb0ee2a2a07cbd2f6aa76be8a2661e724 Mon Sep 17 00:00:00 2001 From: Zhi Mao Date: Wed, 12 Jun 2024 10:17:58 +0800 Subject: media: dt-bindings: i2c: add GalaxyCore GC08A3 image sensor Add YAML device tree binding for GC08A3 CMOS image sensor, and the relevant MAINTAINERS entries. Reviewed-by: Krzysztof Kozlowski Reviewed-by: Laurent Pinchart Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Zhi Mao Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .../bindings/media/i2c/galaxycore,gc08a3.yaml | 112 +++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml new file mode 100644 index 000000000000..51b8ece09c72 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml @@ -0,0 +1,112 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright (c) 2023 MediaTek Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/galaxycore,gc08a3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: GalaxyCore gc08a3 1/4" 8M Pixel MIPI CSI-2 sensor + +maintainers: + - Zhi Mao + +description: + The gc08a3 is a raw image sensor with an MIPI CSI-2 image data + interface and CCI (I2C compatible) control bus. The output format + is raw Bayer. + +properties: + compatible: + const: galaxycore,gc08a3 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + dovdd-supply: true + + avdd-supply: true + + dvdd-supply: true + + reset-gpios: + description: Reference to the GPIO connected to the RESETB pin. + maxItems: 1 + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + description: + Output port node, single endpoint describing the CSI-2 transmitter. + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + oneOf: + - items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + - items: + - const: 1 + - const: 2 + + link-frequencies: true + + required: + - data-lanes + - link-frequencies + + required: + - endpoint + +required: + - compatible + - reg + - clocks + - dovdd-supply + - avdd-supply + - dvdd-supply + - reset-gpios + - port + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + sensor@31 { + compatible = "galaxycore,gc08a3"; + reg = <0x31>; + + clocks = <&gc08a3_clk>; + + reset-gpios = <&pio 19 GPIO_ACTIVE_LOW>; + + avdd-supply = <&gc08a3_avdd>; + dovdd-supply = <&gc08a3_dovdd>; + dvdd-supply = <&gc08a3_dvdd>; + + port { + sensor_out: endpoint { + data-lanes = <1 2 3 4>; + link-frequencies = /bits/ 64 <336000000 207000000>; + remote-endpoint = <&seninf_csi_port_0_in>; + }; + }; + }; + }; + +... -- cgit v1.2.3 From 1ad0cd5ed61b24929fcdeb9a38c15a3f2c18c1fe Mon Sep 17 00:00:00 2001 From: Zhi Mao Date: Wed, 12 Jun 2024 10:17:59 +0800 Subject: media: i2c: Add GC08A3 image sensor driver Add a V4L2 sub-device driver for Galaxycore GC08A3 image sensor. Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Zhi Mao [Sakari Ailus: Fold in MAINTAINERS change.] Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- MAINTAINERS | 7 + drivers/media/i2c/Kconfig | 10 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/gc08a3.c | 1339 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1357 insertions(+) create mode 100644 drivers/media/i2c/gc08a3.c diff --git a/MAINTAINERS b/MAINTAINERS index be05b756c66b..e5dd5525fd4b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9083,6 +9083,13 @@ S: Maintained F: Documentation/devicetree/bindings/media/i2c/galaxycore,gc0308.yaml F: drivers/media/i2c/gc0308.c +GALAXYCORE GC08A3 CAMERA SENSOR DRIVER +M: Zhi Mao +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/galaxycore,gc08a3.yaml +F: drivers/media/i2c/gc08a3.c + GALAXYCORE GC2145 SENSOR DRIVER M: Alain Volmat L: linux-media@vger.kernel.org diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index f336b622e5b8..faa74d4cd776 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -70,6 +70,16 @@ config VIDEO_GC0308 To compile this driver as a module, choose M here: the module will be called gc0308. +config VIDEO_GC08A3 + tristate "GalaxyCore gc08a3 sensor support" + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the GalaxyCore gc08a3 + camera. + + To compile this driver as a module, choose M here: the + module will be called gc08a3. + config VIDEO_GC2145 select V4L2_CCI_I2C tristate "GalaxyCore GC2145 sensor support" diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index c36f5d1d7f18..8817b8e86989 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_VIDEO_DW9768) += dw9768.o obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/ obj-$(CONFIG_VIDEO_GC0308) += gc0308.o +obj-$(CONFIG_VIDEO_GC08A3) += gc08a3.o obj-$(CONFIG_VIDEO_GC2145) += gc2145.o obj-$(CONFIG_VIDEO_HI556) += hi556.o obj-$(CONFIG_VIDEO_HI846) += hi846.o diff --git a/drivers/media/i2c/gc08a3.c b/drivers/media/i2c/gc08a3.c new file mode 100644 index 000000000000..7680d807e7a5 --- /dev/null +++ b/drivers/media/i2c/gc08a3.c @@ -0,0 +1,1339 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for GalaxyCore gc08a3 image sensor + * + * Copyright 2024 MediaTek + * + * Zhi Mao + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define GC08A3_REG_TEST_PATTERN_EN CCI_REG8(0x008c) +#define GC08A3_REG_TEST_PATTERN_IDX CCI_REG8(0x008d) +#define GC08A3_TEST_PATTERN_EN 0x01 + +#define GC08A3_STREAMING_REG CCI_REG8(0x0100) + +#define GC08A3_FLIP_REG CCI_REG8(0x0101) +#define GC08A3_FLIP_H_MASK BIT(0) +#define GC08A3_FLIP_V_MASK BIT(1) + +#define GC08A3_EXP_REG CCI_REG16(0x0202) +#define GC08A3_EXP_MARGIN 16 +#define GC08A3_EXP_MIN 4 +#define GC08A3_EXP_STEP 1 + +#define GC08A3_AGAIN_REG CCI_REG16(0x0204) +#define GC08A3_AGAIN_MIN 1024 +#define GC08A3_AGAIN_MAX (1024 * 16) +#define GC08A3_AGAIN_STEP 1 + +#define GC08A3_FRAME_LENGTH_REG CCI_REG16(0x0340) +#define GC08A3_VTS_MAX 0xfff0 + +#define GC08A3_REG_CHIP_ID CCI_REG16(0x03f0) +#define GC08A3_CHIP_ID 0x08a3 + +#define GC08A3_NATIVE_WIDTH 3264 +#define GC08A3_NATIVE_HEIGHT 2448 + +#define GC08A3_DEFAULT_CLK_FREQ (24 * HZ_PER_MHZ) +#define GC08A3_MBUS_CODE MEDIA_BUS_FMT_SRGGB10_1X10 +#define GC08A3_DATA_LANES 4 + +#define GC08A3_RGB_DEPTH 10 + +#define GC08A3_SLEEP_US (2 * USEC_PER_MSEC) + +static const char *const gc08a3_test_pattern_menu[] = { + "No Pattern", "Solid Black", "Colour Bar", "Solid White", + "Solid Red", "Solid Green", "Solid Blue", "Solid Yellow", +}; + +static const s64 gc08a3_link_freq_menu_items[] = { + (336 * HZ_PER_MHZ), + (207 * HZ_PER_MHZ), +}; + +static const char *const gc08a3_supply_name[] = { + "avdd", + "dvdd", + "dovdd", +}; + +struct gc08a3 { + struct device *dev; + struct v4l2_subdev sd; + struct media_pad pad; + + struct clk *xclk; + struct regulator_bulk_data supplies[ARRAY_SIZE(gc08a3_supply_name)]; + struct gpio_desc *reset_gpio; + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + + struct regmap *regmap; + unsigned long link_freq_bitmap; + const struct gc08a3_mode *cur_mode; +}; + +struct gc08a3_reg_list { + u32 num_of_regs; + const struct cci_reg_sequence *regs; +}; + +static const struct cci_reg_sequence mode_3264x2448[] = { + /* system */ + { CCI_REG8(0x0336), 0x70 }, + { CCI_REG8(0x0383), 0xbb }, + { CCI_REG8(0x0344), 0x00 }, + { CCI_REG8(0x0345), 0x06 }, + { CCI_REG8(0x0346), 0x00 }, + { CCI_REG8(0x0347), 0x04 }, + { CCI_REG8(0x0348), 0x0c }, + { CCI_REG8(0x0349), 0xd0 }, + { CCI_REG8(0x034a), 0x09 }, + { CCI_REG8(0x034b), 0x9c }, + { CCI_REG8(0x0202), 0x09 }, + { CCI_REG8(0x0203), 0x04 }, + { CCI_REG8(0x0340), 0x09 }, + { CCI_REG8(0x0341), 0xf4 }, + { CCI_REG8(0x0342), 0x07 }, + { CCI_REG8(0x0343), 0x1c }, + + { CCI_REG8(0x0226), 0x00 }, + { CCI_REG8(0x0227), 0x28 }, + { CCI_REG8(0x0e38), 0x49 }, + { CCI_REG8(0x0210), 0x13 }, + { CCI_REG8(0x0218), 0x00 }, + { CCI_REG8(0x0241), 0x88 }, + { CCI_REG8(0x0392), 0x60 }, + + /* ISP */ + { CCI_REG8(0x00a2), 0x00 }, + { CCI_REG8(0x00a3), 0x00 }, + { CCI_REG8(0x00ab), 0x00 }, + { CCI_REG8(0x00ac), 0x00 }, + + /* GAIN */ + { CCI_REG8(0x0204), 0x04 }, + { CCI_REG8(0x0205), 0x00 }, + { CCI_REG8(0x0050), 0x5c }, + { CCI_REG8(0x0051), 0x44 }, + + /* out window */ + { CCI_REG8(0x009a), 0x66 }, + { CCI_REG8(0x0351), 0x00 }, + { CCI_REG8(0x0352), 0x06 }, + { CCI_REG8(0x0353), 0x00 }, + { CCI_REG8(0x0354), 0x08 }, + { CCI_REG8(0x034c), 0x0c }, + { CCI_REG8(0x034d), 0xc0 }, + { CCI_REG8(0x034e), 0x09 }, + { CCI_REG8(0x034f), 0x90 }, + + /* MIPI */ + { CCI_REG8(0x0114), 0x03 }, + { CCI_REG8(0x0180), 0x65 }, + { CCI_REG8(0x0181), 0xf0 }, + { CCI_REG8(0x0185), 0x01 }, + { CCI_REG8(0x0115), 0x30 }, + { CCI_REG8(0x011b), 0x12 }, + { CCI_REG8(0x011c), 0x12 }, + { CCI_REG8(0x0121), 0x06 }, + { CCI_REG8(0x0122), 0x06 }, + { CCI_REG8(0x0123), 0x15 }, + { CCI_REG8(0x0124), 0x01 }, + { CCI_REG8(0x0125), 0x0b }, + { CCI_REG8(0x0126), 0x08 }, + { CCI_REG8(0x0129), 0x06 }, + { CCI_REG8(0x012a), 0x08 }, + { CCI_REG8(0x012b), 0x08 }, + + { CCI_REG8(0x0a73), 0x60 }, + { CCI_REG8(0x0a70), 0x11 }, + { CCI_REG8(0x0313), 0x80 }, + { CCI_REG8(0x0aff), 0x00 }, + { CCI_REG8(0x0a70), 0x00 }, + { CCI_REG8(0x00a4), 0x80 }, + { CCI_REG8(0x0316), 0x01 }, + { CCI_REG8(0x0a67), 0x00 }, + { CCI_REG8(0x0084), 0x10 }, + { CCI_REG8(0x0102), 0x09 }, +}; + +static const struct cci_reg_sequence mode_1920x1080[] = { + /* system */ + { CCI_REG8(0x0336), 0x45 }, + { CCI_REG8(0x0383), 0x8b }, + { CCI_REG8(0x0344), 0x02 }, + { CCI_REG8(0x0345), 0xa6 }, + { CCI_REG8(0x0346), 0x02 }, + { CCI_REG8(0x0347), 0xb0 }, + { CCI_REG8(0x0348), 0x07 }, + { CCI_REG8(0x0349), 0x90 }, + { CCI_REG8(0x034a), 0x04 }, + { CCI_REG8(0x034b), 0x44 }, + { CCI_REG8(0x0202), 0x03 }, + { CCI_REG8(0x0203), 0x00 }, + { CCI_REG8(0x0340), 0x04 }, + { CCI_REG8(0x0341), 0xfc }, + { CCI_REG8(0x0342), 0x07 }, + { CCI_REG8(0x0343), 0x1c }, + { CCI_REG8(0x0226), 0x00 }, + { CCI_REG8(0x0227), 0x88 }, + { CCI_REG8(0x0e38), 0x49 }, + { CCI_REG8(0x0210), 0x13 }, + { CCI_REG8(0x0218), 0x00 }, + { CCI_REG8(0x0241), 0x88 }, + { CCI_REG8(0x0392), 0x60 }, + + /* ISP */ + { CCI_REG8(0x00a2), 0xac }, + { CCI_REG8(0x00a3), 0x02 }, + { CCI_REG8(0x00ab), 0xa0 }, + { CCI_REG8(0x00ac), 0x02 }, + + /* GAIN */ + { CCI_REG8(0x0204), 0x04 }, + { CCI_REG8(0x0205), 0x00 }, + { CCI_REG8(0x0050), 0x38 }, + { CCI_REG8(0x0051), 0x20 }, + + /* out window */ + { CCI_REG8(0x009a), 0x66 }, + { CCI_REG8(0x0351), 0x00 }, + { CCI_REG8(0x0352), 0x06 }, + { CCI_REG8(0x0353), 0x00 }, + { CCI_REG8(0x0354), 0x08 }, + { CCI_REG8(0x034c), 0x07 }, + { CCI_REG8(0x034d), 0x80 }, + { CCI_REG8(0x034e), 0x04 }, + { CCI_REG8(0x034f), 0x38 }, + + /* MIPI */ + { CCI_REG8(0x0114), 0x03 }, + { CCI_REG8(0x0180), 0x65 }, + { CCI_REG8(0x0181), 0xf0 }, + { CCI_REG8(0x0185), 0x01 }, + { CCI_REG8(0x0115), 0x30 }, + { CCI_REG8(0x011b), 0x12 }, + { CCI_REG8(0x011c), 0x12 }, + { CCI_REG8(0x0121), 0x02 }, + { CCI_REG8(0x0122), 0x03 }, + { CCI_REG8(0x0123), 0x0c }, + { CCI_REG8(0x0124), 0x00 }, + { CCI_REG8(0x0125), 0x09 }, + { CCI_REG8(0x0126), 0x06 }, + { CCI_REG8(0x0129), 0x04 }, + { CCI_REG8(0x012a), 0x03 }, + { CCI_REG8(0x012b), 0x06 }, + + { CCI_REG8(0x0a73), 0x60 }, + { CCI_REG8(0x0a70), 0x11 }, + { CCI_REG8(0x0313), 0x80 }, + { CCI_REG8(0x0aff), 0x00 }, + { CCI_REG8(0x0a70), 0x00 }, + { CCI_REG8(0x00a4), 0x80 }, + { CCI_REG8(0x0316), 0x01 }, + { CCI_REG8(0x0a67), 0x00 }, + { CCI_REG8(0x0084), 0x10 }, + { CCI_REG8(0x0102), 0x09 }, +}; + +static const struct cci_reg_sequence mode_table_common[] = { + { GC08A3_STREAMING_REG, 0x00 }, + /* system */ + { CCI_REG8(0x031c), 0x60 }, + { CCI_REG8(0x0337), 0x04 }, + { CCI_REG8(0x0335), 0x51 }, + { CCI_REG8(0x0336), 0x70 }, + { CCI_REG8(0x0383), 0xbb }, + { CCI_REG8(0x031a), 0x00 }, + { CCI_REG8(0x0321), 0x10 }, + { CCI_REG8(0x0327), 0x03 }, + { CCI_REG8(0x0325), 0x40 }, + { CCI_REG8(0x0326), 0x23 }, + { CCI_REG8(0x0314), 0x11 }, + { CCI_REG8(0x0315), 0xd6 }, + { CCI_REG8(0x0316), 0x01 }, + { CCI_REG8(0x0334), 0x40 }, + { CCI_REG8(0x0324), 0x42 }, + { CCI_REG8(0x031c), 0x00 }, + { CCI_REG8(0x031c), 0x9f }, + { CCI_REG8(0x039a), 0x13 }, + { CCI_REG8(0x0084), 0x30 }, + { CCI_REG8(0x02b3), 0x08 }, + { CCI_REG8(0x0057), 0x0c }, + { CCI_REG8(0x05c3), 0x50 }, + { CCI_REG8(0x0311), 0x90 }, + { CCI_REG8(0x05a0), 0x02 }, + { CCI_REG8(0x0074), 0x0a }, + { CCI_REG8(0x0059), 0x11 }, + { CCI_REG8(0x0070), 0x05 }, + { CCI_REG8(0x0101), 0x00 }, + + /* analog */ + { CCI_REG8(0x0344), 0x00 }, + { CCI_REG8(0x0345), 0x06 }, + { CCI_REG8(0x0346), 0x00 }, + { CCI_REG8(0x0347), 0x04 }, + { CCI_REG8(0x0348), 0x0c }, + { CCI_REG8(0x0349), 0xd0 }, + { CCI_REG8(0x034a), 0x09 }, + { CCI_REG8(0x034b), 0x9c }, + { CCI_REG8(0x0202), 0x09 }, + { CCI_REG8(0x0203), 0x04 }, + + { CCI_REG8(0x0219), 0x05 }, + { CCI_REG8(0x0226), 0x00 }, + { CCI_REG8(0x0227), 0x28 }, + { CCI_REG8(0x0e0a), 0x00 }, + { CCI_REG8(0x0e0b), 0x00 }, + { CCI_REG8(0x0e24), 0x04 }, + { CCI_REG8(0x0e25), 0x04 }, + { CCI_REG8(0x0e26), 0x00 }, + { CCI_REG8(0x0e27), 0x10 }, + { CCI_REG8(0x0e01), 0x74 }, + { CCI_REG8(0x0e03), 0x47 }, + { CCI_REG8(0x0e04), 0x33 }, + { CCI_REG8(0x0e05), 0x44 }, + { CCI_REG8(0x0e06), 0x44 }, + { CCI_REG8(0x0e0c), 0x1e }, + { CCI_REG8(0x0e17), 0x3a }, + { CCI_REG8(0x0e18), 0x3c }, + { CCI_REG8(0x0e19), 0x40 }, + { CCI_REG8(0x0e1a), 0x42 }, + { CCI_REG8(0x0e28), 0x21 }, + { CCI_REG8(0x0e2b), 0x68 }, + { CCI_REG8(0x0e2c), 0x0d }, + { CCI_REG8(0x0e2d), 0x08 }, + { CCI_REG8(0x0e34), 0xf4 }, + { CCI_REG8(0x0e35), 0x44 }, + { CCI_REG8(0x0e36), 0x07 }, + { CCI_REG8(0x0e38), 0x49 }, + { CCI_REG8(0x0210), 0x13 }, + { CCI_REG8(0x0218), 0x00 }, + { CCI_REG8(0x0241), 0x88 }, + { CCI_REG8(0x0e32), 0x00 }, + { CCI_REG8(0x0e33), 0x18 }, + { CCI_REG8(0x0e42), 0x03 }, + { CCI_REG8(0x0e43), 0x80 }, + { CCI_REG8(0x0e44), 0x04 }, + { CCI_REG8(0x0e45), 0x00 }, + { CCI_REG8(0x0e4f), 0x04 }, + { CCI_REG8(0x057a), 0x20 }, + { CCI_REG8(0x0381), 0x7c }, + { CCI_REG8(0x0382), 0x9b }, + { CCI_REG8(0x0384), 0xfb }, + { CCI_REG8(0x0389), 0x38 }, + { CCI_REG8(0x038a), 0x03 }, + { CCI_REG8(0x0390), 0x6a }, + { CCI_REG8(0x0391), 0x0b }, + { CCI_REG8(0x0392), 0x60 }, + { CCI_REG8(0x0393), 0xc1 }, + { CCI_REG8(0x0396), 0xff }, + { CCI_REG8(0x0398), 0x62 }, + + /* cisctl reset */ + { CCI_REG8(0x031c), 0x80 }, + { CCI_REG8(0x03fe), 0x10 }, + { CCI_REG8(0x03fe), 0x00 }, + { CCI_REG8(0x031c), 0x9f }, + { CCI_REG8(0x03fe), 0x00 }, + { CCI_REG8(0x03fe), 0x00 }, + { CCI_REG8(0x03fe), 0x00 }, + { CCI_REG8(0x03fe), 0x00 }, + { CCI_REG8(0x031c), 0x80 }, + { CCI_REG8(0x03fe), 0x10 }, + { CCI_REG8(0x03fe), 0x00 }, + { CCI_REG8(0x031c), 0x9f }, + { CCI_REG8(0x0360), 0x01 }, + { CCI_REG8(0x0360), 0x00 }, + { CCI_REG8(0x0316), 0x09 }, + { CCI_REG8(0x0a67), 0x80 }, + { CCI_REG8(0x0313), 0x00 }, + { CCI_REG8(0x0a53), 0x0e }, + { CCI_REG8(0x0a65), 0x17 }, + { CCI_REG8(0x0a68), 0xa1 }, + { CCI_REG8(0x0a58), 0x00 }, + { CCI_REG8(0x0ace), 0x0c }, + { CCI_REG8(0x00a4), 0x00 }, + { CCI_REG8(0x00a5), 0x01 }, + { CCI_REG8(0x00a7), 0x09 }, + { CCI_REG8(0x00a8), 0x9c }, + { CCI_REG8(0x00a9), 0x0c }, + { CCI_REG8(0x00aa), 0xd0 }, + { CCI_REG8(0x0a8a), 0x00 }, + { CCI_REG8(0x0a8b), 0xe0 }, + { CCI_REG8(0x0a8c), 0x13 }, + { CCI_REG8(0x0a8d), 0xe8 }, + { CCI_REG8(0x0a90), 0x0a }, + { CCI_REG8(0x0a91), 0x10 }, + { CCI_REG8(0x0a92), 0xf8 }, + { CCI_REG8(0x0a71), 0xf2 }, + { CCI_REG8(0x0a72), 0x12 }, + { CCI_REG8(0x0a73), 0x64 }, + { CCI_REG8(0x0a75), 0x41 }, + { CCI_REG8(0x0a70), 0x07 }, + { CCI_REG8(0x0313), 0x80 }, + + /* ISP */ + { CCI_REG8(0x00a0), 0x01 }, + { CCI_REG8(0x0080), 0xd2 }, + { CCI_REG8(0x0081), 0x3f }, + { CCI_REG8(0x0087), 0x51 }, + { CCI_REG8(0x0089), 0x03 }, + { CCI_REG8(0x009b), 0x40 }, + { CCI_REG8(0x05a0), 0x82 }, + { CCI_REG8(0x05ac), 0x00 }, + { CCI_REG8(0x05ad), 0x01 }, + { CCI_REG8(0x05ae), 0x00 }, + { CCI_REG8(0x0800), 0x0a }, + { CCI_REG8(0x0801), 0x14 }, + { CCI_REG8(0x0802), 0x28 }, + { CCI_REG8(0x0803), 0x34 }, + { CCI_REG8(0x0804), 0x0e }, + { CCI_REG8(0x0805), 0x33 }, + { CCI_REG8(0x0806), 0x03 }, + { CCI_REG8(0x0807), 0x8a }, + { CCI_REG8(0x0808), 0x50 }, + { CCI_REG8(0x0809), 0x00 }, + { CCI_REG8(0x080a), 0x34 }, + { CCI_REG8(0x080b), 0x03 }, + { CCI_REG8(0x080c), 0x26 }, + { CCI_REG8(0x080d), 0x03 }, + { CCI_REG8(0x080e), 0x18 }, + { CCI_REG8(0x080f), 0x03 }, + { CCI_REG8(0x0810), 0x10 }, + { CCI_REG8(0x0811), 0x03 }, + { CCI_REG8(0x0812), 0x00 }, + { CCI_REG8(0x0813), 0x00 }, + { CCI_REG8(0x0814), 0x01 }, + { CCI_REG8(0x0815), 0x00 }, + { CCI_REG8(0x0816), 0x01 }, + { CCI_REG8(0x0817), 0x00 }, + { CCI_REG8(0x0818), 0x00 }, + { CCI_REG8(0x0819), 0x0a }, + { CCI_REG8(0x081a), 0x01 }, + { CCI_REG8(0x081b), 0x6c }, + { CCI_REG8(0x081c), 0x00 }, + { CCI_REG8(0x081d), 0x0b }, + { CCI_REG8(0x081e), 0x02 }, + { CCI_REG8(0x081f), 0x00 }, + { CCI_REG8(0x0820), 0x00 }, + { CCI_REG8(0x0821), 0x0c }, + { CCI_REG8(0x0822), 0x02 }, + { CCI_REG8(0x0823), 0xd9 }, + { CCI_REG8(0x0824), 0x00 }, + { CCI_REG8(0x0825), 0x0d }, + { CCI_REG8(0x0826), 0x03 }, + { CCI_REG8(0x0827), 0xf0 }, + { CCI_REG8(0x0828), 0x00 }, + { CCI_REG8(0x0829), 0x0e }, + { CCI_REG8(0x082a), 0x05 }, + { CCI_REG8(0x082b), 0x94 }, + { CCI_REG8(0x082c), 0x09 }, + { CCI_REG8(0x082d), 0x6e }, + { CCI_REG8(0x082e), 0x07 }, + { CCI_REG8(0x082f), 0xe6 }, + { CCI_REG8(0x0830), 0x10 }, + { CCI_REG8(0x0831), 0x0e }, + { CCI_REG8(0x0832), 0x0b }, + { CCI_REG8(0x0833), 0x2c }, + { CCI_REG8(0x0834), 0x14 }, + { CCI_REG8(0x0835), 0xae }, + { CCI_REG8(0x0836), 0x0f }, + { CCI_REG8(0x0837), 0xc4 }, + { CCI_REG8(0x0838), 0x18 }, + { CCI_REG8(0x0839), 0x0e }, + { CCI_REG8(0x05ac), 0x01 }, + { CCI_REG8(0x059a), 0x00 }, + { CCI_REG8(0x059b), 0x00 }, + { CCI_REG8(0x059c), 0x01 }, + { CCI_REG8(0x0598), 0x00 }, + { CCI_REG8(0x0597), 0x14 }, + { CCI_REG8(0x05ab), 0x09 }, + { CCI_REG8(0x05a4), 0x02 }, + { CCI_REG8(0x05a3), 0x05 }, + { CCI_REG8(0x05a0), 0xc2 }, + { CCI_REG8(0x0207), 0xc4 }, + + /* GAIN */ + { CCI_REG8(0x0208), 0x01 }, + { CCI_REG8(0x0209), 0x72 }, + { CCI_REG8(0x0204), 0x04 }, + { CCI_REG8(0x0205), 0x00 }, + + { CCI_REG8(0x0040), 0x22 }, + { CCI_REG8(0x0041), 0x20 }, + { CCI_REG8(0x0043), 0x10 }, + { CCI_REG8(0x0044), 0x00 }, + { CCI_REG8(0x0046), 0x08 }, + { CCI_REG8(0x0047), 0xf0 }, + { CCI_REG8(0x0048), 0x0f }, + { CCI_REG8(0x004b), 0x0f }, + { CCI_REG8(0x004c), 0x00 }, + { CCI_REG8(0x0050), 0x5c }, + { CCI_REG8(0x0051), 0x44 }, + { CCI_REG8(0x005b), 0x03 }, + { CCI_REG8(0x00c0), 0x00 }, + { CCI_REG8(0x00c1), 0x80 }, + { CCI_REG8(0x00c2), 0x31 }, + { CCI_REG8(0x00c3), 0x00 }, + { CCI_REG8(0x0460), 0x04 }, + { CCI_REG8(0x0462), 0x08 }, + { CCI_REG8(0x0464), 0x0e }, + { CCI_REG8(0x0466), 0x0a }, + { CCI_REG8(0x0468), 0x12 }, + { CCI_REG8(0x046a), 0x12 }, + { CCI_REG8(0x046c), 0x10 }, + { CCI_REG8(0x046e), 0x0c }, + { CCI_REG8(0x0461), 0x03 }, + { CCI_REG8(0x0463), 0x03 }, + { CCI_REG8(0x0465), 0x03 }, + { CCI_REG8(0x0467), 0x03 }, + { CCI_REG8(0x0469), 0x04 }, + { CCI_REG8(0x046b), 0x04 }, + { CCI_REG8(0x046d), 0x04 }, + { CCI_REG8(0x046f), 0x04 }, + { CCI_REG8(0x0470), 0x04 }, + { CCI_REG8(0x0472), 0x10 }, + { CCI_REG8(0x0474), 0x26 }, + { CCI_REG8(0x0476), 0x38 }, + { CCI_REG8(0x0478), 0x20 }, + { CCI_REG8(0x047a), 0x30 }, + { CCI_REG8(0x047c), 0x38 }, + { CCI_REG8(0x047e), 0x60 }, + { CCI_REG8(0x0471), 0x05 }, + { CCI_REG8(0x0473), 0x05 }, + { CCI_REG8(0x0475), 0x05 }, + { CCI_REG8(0x0477), 0x05 }, + { CCI_REG8(0x0479), 0x04 }, + { CCI_REG8(0x047b), 0x04 }, + { CCI_REG8(0x047d), 0x04 }, + { CCI_REG8(0x047f), 0x04 }, +}; + +struct gc08a3_mode { + u32 width; + u32 height; + const struct gc08a3_reg_list reg_list; + + u32 hts; /* Horizontal timining size */ + u32 vts_def; /* Default vertical timining size */ + u32 vts_min; /* Min vertical timining size */ +}; + +/* Declare modes in order, from biggest to smallest height. */ +static const struct gc08a3_mode gc08a3_modes[] = { + { + /* 3264*2448@30fps */ + .width = GC08A3_NATIVE_WIDTH, + .height = GC08A3_NATIVE_HEIGHT, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3264x2448), + .regs = mode_3264x2448, + }, + .hts = 3640, + .vts_def = 2548, + .vts_min = 2548, + }, + { + /* 1920*1080@60fps */ + .width = 1920, + .height = 1080, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1920x1080), + .regs = mode_1920x1080, + }, + .hts = 3640, + .vts_def = 1276, + .vts_min = 1276, + }, +}; + +static inline struct gc08a3 *to_gc08a3(struct v4l2_subdev *sd) +{ + return container_of(sd, struct gc08a3, sd); +} + +static int gc08a3_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gc08a3 *gc08a3 = to_gc08a3(sd); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(gc08a3_supply_name), + gc08a3->supplies); + if (ret < 0) { + dev_err(gc08a3->dev, "failed to enable regulators: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(gc08a3->xclk); + if (ret < 0) { + regulator_bulk_disable(ARRAY_SIZE(gc08a3_supply_name), + gc08a3->supplies); + dev_err(gc08a3->dev, "clk prepare enable failed\n"); + return ret; + } + + fsleep(GC08A3_SLEEP_US); + + gpiod_set_value_cansleep(gc08a3->reset_gpio, 0); + fsleep(GC08A3_SLEEP_US); + + return 0; +} + +static int gc08a3_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gc08a3 *gc08a3 = to_gc08a3(sd); + + clk_disable_unprepare(gc08a3->xclk); + gpiod_set_value_cansleep(gc08a3->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(gc08a3_supply_name), + gc08a3->supplies); + + return 0; +} + +static int gc08a3_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = GC08A3_MBUS_CODE; + + return 0; +} + +static int gc08a3_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->code != GC08A3_MBUS_CODE) + return -EINVAL; + + if (fse->index >= ARRAY_SIZE(gc08a3_modes)) + return -EINVAL; + + fse->min_width = gc08a3_modes[fse->index].width; + fse->max_width = gc08a3_modes[fse->index].width; + fse->min_height = gc08a3_modes[fse->index].height; + fse->max_height = gc08a3_modes[fse->index].height; + + return 0; +} + +static int gc08a3_update_cur_mode_controls(struct gc08a3 *gc08a3, + const struct gc08a3_mode *mode) +{ + s64 exposure_max, h_blank; + int ret; + + ret = __v4l2_ctrl_modify_range(gc08a3->vblank, + mode->vts_min - mode->height, + GC08A3_VTS_MAX - mode->height, 1, + mode->vts_def - mode->height); + if (ret) { + dev_err(gc08a3->dev, "VB ctrl range update failed\n"); + return ret; + } + + h_blank = mode->hts - mode->width; + ret = __v4l2_ctrl_modify_range(gc08a3->hblank, h_blank, h_blank, 1, + h_blank); + if (ret) { + dev_err(gc08a3->dev, "HB ctrl range update failed\n"); + return ret; + } + + exposure_max = mode->vts_def - GC08A3_EXP_MARGIN; + ret = __v4l2_ctrl_modify_range(gc08a3->exposure, GC08A3_EXP_MIN, + exposure_max, GC08A3_EXP_STEP, + exposure_max); + if (ret) { + dev_err(gc08a3->dev, "exposure ctrl range update failed\n"); + return ret; + } + + return 0; +} + +static void gc08a3_update_pad_format(struct gc08a3 *gc08a3, + const struct gc08a3_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = GC08A3_MBUS_CODE; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_RAW; + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_XFER_FUNC_NONE; +} + +static int gc08a3_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct gc08a3 *gc08a3 = to_gc08a3(sd); + struct v4l2_mbus_framefmt *mbus_fmt; + struct v4l2_rect *crop; + const struct gc08a3_mode *mode; + + mode = v4l2_find_nearest_size(gc08a3_modes, ARRAY_SIZE(gc08a3_modes), + width, height, fmt->format.width, + fmt->format.height); + + /* update crop info to subdev state */ + crop = v4l2_subdev_state_get_crop(state, 0); + crop->width = mode->width; + crop->height = mode->height; + + /* update fmt info to subdev state */ + gc08a3_update_pad_format(gc08a3, mode, &fmt->format); + mbus_fmt = v4l2_subdev_state_get_format(state, 0); + *mbus_fmt = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + gc08a3->cur_mode = mode; + gc08a3_update_cur_mode_controls(gc08a3, mode); + + return 0; +} + +static int gc08a3_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(state, 0); + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = GC08A3_NATIVE_WIDTH; + sel->r.height = GC08A3_NATIVE_HEIGHT; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int gc08a3_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = 0, + .format = { + .code = GC08A3_MBUS_CODE, + .width = gc08a3_modes[0].width, + .height = gc08a3_modes[0].height, + }, + }; + + gc08a3_set_format(sd, state, &fmt); + + return 0; +} + +static int gc08a3_set_ctrl_hflip(struct gc08a3 *gc08a3, u32 ctrl_val) +{ + int ret; + u64 val; + + ret = cci_read(gc08a3->regmap, GC08A3_FLIP_REG, &val, NULL); + if (ret) { + dev_err(gc08a3->dev, "read hflip register failed: %d\n", ret); + return ret; + } + + return cci_update_bits(gc08a3->regmap, GC08A3_FLIP_REG, + GC08A3_FLIP_H_MASK, + ctrl_val ? GC08A3_FLIP_H_MASK : 0, NULL); +} + +static int gc08a3_set_ctrl_vflip(struct gc08a3 *gc08a3, u32 ctrl_val) +{ + int ret; + u64 val; + + ret = cci_read(gc08a3->regmap, GC08A3_FLIP_REG, &val, NULL); + if (ret) { + dev_err(gc08a3->dev, "read vflip register failed: %d\n", ret); + return ret; + } + + return cci_update_bits(gc08a3->regmap, GC08A3_FLIP_REG, + GC08A3_FLIP_V_MASK, + ctrl_val ? GC08A3_FLIP_V_MASK : 0, NULL); +} + +static int gc08a3_test_pattern(struct gc08a3 *gc08a3, u32 pattern_menu) +{ + u32 pattern; + int ret; + + if (pattern_menu) { + switch (pattern_menu) { + case 1: + pattern = 0x00; + break; + case 2: + pattern = 0x10; + break; + case 3: + case 4: + case 5: + case 6: + case 7: + pattern = pattern_menu + 1; + break; + default: + pattern = 0x00; + break; + } + + ret = cci_write(gc08a3->regmap, GC08A3_REG_TEST_PATTERN_IDX, + pattern, NULL); + if (ret) + return ret; + + return cci_write(gc08a3->regmap, GC08A3_REG_TEST_PATTERN_EN, + GC08A3_TEST_PATTERN_EN, NULL); + } else { + return cci_write(gc08a3->regmap, GC08A3_REG_TEST_PATTERN_EN, + 0x00, NULL); + } +} + +static int gc08a3_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc08a3 *gc08a3 = + container_of(ctrl->handler, struct gc08a3, ctrls); + int ret = 0; + s64 exposure_max; + struct v4l2_subdev_state *state; + const struct v4l2_mbus_framefmt *format; + + state = v4l2_subdev_get_locked_active_state(&gc08a3->sd); + format = v4l2_subdev_state_get_format(state, 0); + + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = format->height + ctrl->val - GC08A3_EXP_MARGIN; + __v4l2_ctrl_modify_range(gc08a3->exposure, + gc08a3->exposure->minimum, + exposure_max, gc08a3->exposure->step, + exposure_max); + } + + /* + * Applying V4L2 control value only happens + * when power is on for streaming. + */ + if (!pm_runtime_get_if_active(gc08a3->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = cci_write(gc08a3->regmap, GC08A3_EXP_REG, + ctrl->val, NULL); + break; + + case V4L2_CID_ANALOGUE_GAIN: + ret = cci_write(gc08a3->regmap, GC08A3_AGAIN_REG, + ctrl->val, NULL); + break; + + case V4L2_CID_VBLANK: + ret = cci_write(gc08a3->regmap, GC08A3_FRAME_LENGTH_REG, + gc08a3->cur_mode->height + ctrl->val, NULL); + break; + + case V4L2_CID_HFLIP: + ret = gc08a3_set_ctrl_hflip(gc08a3, ctrl->val); + break; + + case V4L2_CID_VFLIP: + ret = gc08a3_set_ctrl_vflip(gc08a3, ctrl->val); + break; + + case V4L2_CID_TEST_PATTERN: + ret = gc08a3_test_pattern(gc08a3, ctrl->val); + break; + + default: + break; + } + + pm_runtime_put(gc08a3->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops gc08a3_ctrl_ops = { + .s_ctrl = gc08a3_set_ctrl, +}; + +static int gc08a3_start_streaming(struct gc08a3 *gc08a3) +{ + const struct gc08a3_mode *mode; + const struct gc08a3_reg_list *reg_list; + int ret; + + ret = pm_runtime_resume_and_get(gc08a3->dev); + if (ret < 0) + return ret; + + ret = cci_multi_reg_write(gc08a3->regmap, + mode_table_common, + ARRAY_SIZE(mode_table_common), NULL); + if (ret) + goto err_rpm_put; + + mode = gc08a3->cur_mode; + reg_list = &mode->reg_list; + ret = cci_multi_reg_write(gc08a3->regmap, + reg_list->regs, reg_list->num_of_regs, NULL); + if (ret < 0) + goto err_rpm_put; + + ret = __v4l2_ctrl_handler_setup(&gc08a3->ctrls); + if (ret < 0) { + dev_err(gc08a3->dev, "could not sync v4l2 controls\n"); + goto err_rpm_put; + } + + ret = cci_write(gc08a3->regmap, GC08A3_STREAMING_REG, 1, NULL); + if (ret < 0) { + dev_err(gc08a3->dev, "write STRAEMING_REG failed: %d\n", ret); + goto err_rpm_put; + } + + return 0; + +err_rpm_put: + pm_runtime_put(gc08a3->dev); + return ret; +} + +static int gc08a3_stop_streaming(struct gc08a3 *gc08a3) +{ + int ret; + + ret = cci_write(gc08a3->regmap, GC08A3_STREAMING_REG, 0, NULL); + if (ret < 0) + dev_err(gc08a3->dev, "could not sent stop streaming %d\n", ret); + + pm_runtime_put(gc08a3->dev); + return ret; +} + +static int gc08a3_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct gc08a3 *gc08a3 = to_gc08a3(subdev); + struct v4l2_subdev_state *state; + int ret; + + state = v4l2_subdev_lock_and_get_active_state(subdev); + + if (enable) + ret = gc08a3_start_streaming(gc08a3); + else + ret = gc08a3_stop_streaming(gc08a3); + + v4l2_subdev_unlock_state(state); + + return ret; +} + +static const struct v4l2_subdev_video_ops gc08a3_video_ops = { + .s_stream = gc08a3_s_stream, +}; + +static const struct v4l2_subdev_pad_ops gc08a3_subdev_pad_ops = { + .enum_mbus_code = gc08a3_enum_mbus_code, + .enum_frame_size = gc08a3_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = gc08a3_set_format, + .get_selection = gc08a3_get_selection, +}; + +static const struct v4l2_subdev_core_ops gc08a3_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_ops gc08a3_subdev_ops = { + .core = &gc08a3_core_ops, + .video = &gc08a3_video_ops, + .pad = &gc08a3_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops gc08a3_internal_ops = { + .init_state = gc08a3_init_state, +}; + +static int gc08a3_get_regulators(struct device *dev, struct gc08a3 *gc08a3) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(gc08a3_supply_name); i++) + gc08a3->supplies[i].supply = gc08a3_supply_name[i]; + + return devm_regulator_bulk_get(dev, ARRAY_SIZE(gc08a3_supply_name), + gc08a3->supplies); +} + +static int gc08a3_parse_fwnode(struct gc08a3 *gc08a3) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + int ret; + struct device *dev = gc08a3->dev; + + endpoint = + fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!endpoint) { + dev_err(dev, "endpoint node not found\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg); + if (ret) { + dev_err(dev, "parsing endpoint node failed\n"); + goto done; + } + + ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + gc08a3_link_freq_menu_items, + ARRAY_SIZE(gc08a3_link_freq_menu_items), + &gc08a3->link_freq_bitmap); + if (ret) + goto done; + +done: + v4l2_fwnode_endpoint_free(&bus_cfg); + fwnode_handle_put(endpoint); + return ret; +} + +static u64 gc08a3_to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = + gc08a3_link_freq_menu_items[f_index] * 2 * GC08A3_DATA_LANES; + + return div_u64(pixel_rate, GC08A3_RGB_DEPTH); +} + +static int gc08a3_init_controls(struct gc08a3 *gc08a3) +{ + struct i2c_client *client = v4l2_get_subdevdata(&gc08a3->sd); + const struct gc08a3_mode *mode = &gc08a3_modes[0]; + const struct v4l2_ctrl_ops *ops = &gc08a3_ctrl_ops; + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl_handler *ctrl_hdlr; + s64 exposure_max, h_blank; + int ret; + + ctrl_hdlr = &gc08a3->ctrls; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9); + if (ret) + return ret; + + gc08a3->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + gc08a3->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &gc08a3->hflip); + + gc08a3->link_freq = + v4l2_ctrl_new_int_menu(ctrl_hdlr, + &gc08a3_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(gc08a3_link_freq_menu_items) - 1, + 0, + gc08a3_link_freq_menu_items); + if (gc08a3->link_freq) + gc08a3->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + gc08a3->pixel_rate = + v4l2_ctrl_new_std(ctrl_hdlr, + &gc08a3_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + gc08a3_to_pixel_rate(0), + 1, + gc08a3_to_pixel_rate(0)); + + gc08a3->vblank = + v4l2_ctrl_new_std(ctrl_hdlr, + &gc08a3_ctrl_ops, V4L2_CID_VBLANK, + mode->vts_min - mode->height, + GC08A3_VTS_MAX - mode->height, 1, + mode->vts_def - mode->height); + + h_blank = mode->hts - mode->width; + gc08a3->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, 1, + h_blank); + if (gc08a3->hblank) + gc08a3->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, GC08A3_AGAIN_MIN, + GC08A3_AGAIN_MAX, GC08A3_AGAIN_STEP, + GC08A3_AGAIN_MIN); + + exposure_max = mode->vts_def - GC08A3_EXP_MARGIN; + gc08a3->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &gc08a3_ctrl_ops, + V4L2_CID_EXPOSURE, GC08A3_EXP_MIN, + exposure_max, GC08A3_EXP_STEP, + exposure_max); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &gc08a3_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(gc08a3_test_pattern_menu) - 1, + 0, 0, gc08a3_test_pattern_menu); + + /* register properties to fwnode (e.g. rotation, orientation) */ + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto error_ctrls; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, ops, &props); + if (ret) + goto error_ctrls; + + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + goto error_ctrls; + } + + gc08a3->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error_ctrls: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ret; +} + +static int gc08a3_identify_module(struct gc08a3 *gc08a3) +{ + u64 val; + int ret; + + ret = cci_read(gc08a3->regmap, GC08A3_REG_CHIP_ID, &val, NULL); + if (ret) { + dev_err(gc08a3->dev, "failed to read chip id"); + return ret; + } + + if (val != GC08A3_CHIP_ID) { + dev_err(gc08a3->dev, "chip id mismatch: 0x%x!=0x%llx", + GC08A3_CHIP_ID, val); + return -ENXIO; + } + + return 0; +} + +static int gc08a3_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct gc08a3 *gc08a3; + int ret; + + gc08a3 = devm_kzalloc(dev, sizeof(*gc08a3), GFP_KERNEL); + if (!gc08a3) + return -ENOMEM; + + gc08a3->dev = dev; + + ret = gc08a3_parse_fwnode(gc08a3); + if (ret) + return ret; + + gc08a3->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(gc08a3->regmap)) + return dev_err_probe(dev, PTR_ERR(gc08a3->regmap), + "failed to init CCI\n"); + + gc08a3->xclk = devm_clk_get(dev, NULL); + if (IS_ERR(gc08a3->xclk)) + return dev_err_probe(dev, PTR_ERR(gc08a3->xclk), + "failed to get xclk\n"); + + ret = clk_set_rate(gc08a3->xclk, GC08A3_DEFAULT_CLK_FREQ); + if (ret) + return dev_err_probe(dev, ret, + "failed to set xclk frequency\n"); + + ret = gc08a3_get_regulators(dev, gc08a3); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to get regulators\n"); + + gc08a3->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gc08a3->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(gc08a3->reset_gpio), + "failed to get gpio\n"); + + v4l2_i2c_subdev_init(&gc08a3->sd, client, &gc08a3_subdev_ops); + gc08a3->sd.internal_ops = &gc08a3_internal_ops; + gc08a3->cur_mode = &gc08a3_modes[0]; + + ret = gc08a3_power_on(gc08a3->dev); + if (ret) + return dev_err_probe(dev, ret, + "failed to sensor power on\n"); + + ret = gc08a3_identify_module(gc08a3); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d\n", ret); + goto err_power_off; + } + + ret = gc08a3_init_controls(gc08a3); + if (ret) { + dev_err(&client->dev, "failed to init controls: %d", ret); + goto err_power_off; + } + + gc08a3->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + gc08a3->pad.flags = MEDIA_PAD_FL_SOURCE; + gc08a3->sd.dev = &client->dev; + gc08a3->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + ret = media_entity_pads_init(&gc08a3->sd.entity, 1, &gc08a3->pad); + if (ret < 0) { + dev_err(dev, "could not register media entity\n"); + goto err_v4l2_ctrl_handler_free; + } + + gc08a3->sd.state_lock = gc08a3->ctrls.lock; + ret = v4l2_subdev_init_finalize(&gc08a3->sd); + if (ret < 0) { + dev_err(dev, "v4l2 subdev init error: %d\n", ret); + goto err_media_entity_cleanup; + } + + pm_runtime_set_active(gc08a3->dev); + pm_runtime_enable(gc08a3->dev); + pm_runtime_set_autosuspend_delay(gc08a3->dev, 1000); + pm_runtime_use_autosuspend(gc08a3->dev); + pm_runtime_idle(gc08a3->dev); + + ret = v4l2_async_register_subdev_sensor(&gc08a3->sd); + if (ret < 0) { + dev_err(dev, "could not register v4l2 device\n"); + goto err_rpm; + } + + return 0; + +err_rpm: + pm_runtime_disable(gc08a3->dev); + v4l2_subdev_cleanup(&gc08a3->sd); + +err_media_entity_cleanup: + media_entity_cleanup(&gc08a3->sd.entity); + +err_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(gc08a3->sd.ctrl_handler); + +err_power_off: + gc08a3_power_off(gc08a3->dev); + + return ret; +} + +static void gc08a3_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc08a3 *gc08a3 = to_gc08a3(sd); + + v4l2_async_unregister_subdev(&gc08a3->sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&gc08a3->sd.entity); + v4l2_ctrl_handler_free(&gc08a3->ctrls); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + gc08a3_power_off(gc08a3->dev); + pm_runtime_set_suspended(&client->dev); +} + +static const struct of_device_id gc08a3_of_match[] = { + { .compatible = "galaxycore,gc08a3" }, + {} +}; +MODULE_DEVICE_TABLE(of, gc08a3_of_match); + +static DEFINE_RUNTIME_DEV_PM_OPS(gc08a3_pm_ops, + gc08a3_power_off, + gc08a3_power_on, + NULL); + +static struct i2c_driver gc08a3_i2c_driver = { + .driver = { + .of_match_table = gc08a3_of_match, + .pm = pm_ptr(&gc08a3_pm_ops), + .name = "gc08a3", + }, + .probe = gc08a3_probe, + .remove = gc08a3_remove, +}; +module_i2c_driver(gc08a3_i2c_driver); + +MODULE_DESCRIPTION("GalaxyCore gc08a3 Camera driver"); +MODULE_AUTHOR("Zhi Mao "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 783fb3f5751120108f0fb246ffc4a344d2e5acab Mon Sep 17 00:00:00 2001 From: Zhi Mao Date: Tue, 11 Jun 2024 19:32:34 +0800 Subject: media: dt-bindings: i2c: add GalaxyCore GC05A2 image sensor Add YAML device tree binding for GC05A2 CMOS image sensor, and the relevant MAINTAINERS entries. Reviewed-by: Krzysztof Kozlowski Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Zhi Mao Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .../bindings/media/i2c/galaxycore,gc05a2.yaml | 112 +++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/galaxycore,gc05a2.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/galaxycore,gc05a2.yaml b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc05a2.yaml new file mode 100644 index 000000000000..0e7a7b5ac89f --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc05a2.yaml @@ -0,0 +1,112 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright (c) 2023 MediaTek Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/galaxycore,gc05a2.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: GalaxyCore gc05a2 1/5" 5M Pixel MIPI CSI-2 sensor + +maintainers: + - Zhi Mao + +description: + The gc05a2 is a raw image sensor with an MIPI CSI-2 image data + interface and CCI (I2C compatible) control bus. The output format + is raw Bayer. + +properties: + compatible: + const: galaxycore,gc05a2 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + dovdd-supply: true + + avdd-supply: true + + dvdd-supply: true + + reset-gpios: + description: Reference to the GPIO connected to the RESETB pin. + maxItems: 1 + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + description: + Output port node, single endpoint describing the CSI-2 transmitter. + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + oneOf: + - items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + - items: + - const: 1 + - const: 2 + + link-frequencies: true + + required: + - data-lanes + - link-frequencies + + required: + - endpoint + +required: + - compatible + - reg + - clocks + - dovdd-supply + - avdd-supply + - dvdd-supply + - reset-gpios + - port + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + sensor@37 { + compatible = "galaxycore,gc05a2"; + reg = <0x37>; + + clocks = <&gc05a2_clk>; + + reset-gpios = <&pio 21 GPIO_ACTIVE_LOW>; + + avdd-supply = <&gc05a2_avdd>; + dovdd-supply = <&gc05a2_dovdd>; + dvdd-supply = <&gc05a2_dvdd>; + + port { + sensor_out: endpoint { + data-lanes = <1 2>; + link-frequencies = /bits/ 64 <448000000 224000000>; + remote-endpoint = <&seninf_csi_port_1_in>; + }; + }; + }; + }; + +... -- cgit v1.2.3 From 355f5097262b17d930e880b35f88986d3f8c1c85 Mon Sep 17 00:00:00 2001 From: Zhi Mao Date: Tue, 11 Jun 2024 19:32:35 +0800 Subject: media: i2c: Add GC05A2 image sensor driver Add a V4L2 sub-device driver for Galaxycore GC05A2 image sensor. Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Zhi Mao [Sakari Ailus: Fold in MAINTAINERS change.] Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- MAINTAINERS | 7 + drivers/media/i2c/Kconfig | 10 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/gc05a2.c | 1359 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1377 insertions(+) create mode 100644 drivers/media/i2c/gc05a2.c diff --git a/MAINTAINERS b/MAINTAINERS index e5dd5525fd4b..94a024a15af2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9083,6 +9083,13 @@ S: Maintained F: Documentation/devicetree/bindings/media/i2c/galaxycore,gc0308.yaml F: drivers/media/i2c/gc0308.c +GALAXYCORE GC05a2 CAMERA SENSOR DRIVER +M: Zhi Mao +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/galaxycore,gc05a2.yaml +F: drivers/media/i2c/gc05a2.c + GALAXYCORE GC08A3 CAMERA SENSOR DRIVER M: Zhi Mao L: linux-media@vger.kernel.org diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index faa74d4cd776..8aac41513b59 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -70,6 +70,16 @@ config VIDEO_GC0308 To compile this driver as a module, choose M here: the module will be called gc0308. +config VIDEO_GC05A2 + tristate "GalaxyCore gc05a2 sensor support" + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the GalaxyCore gc05a2 + camera. + + To compile this driver as a module, choose M here: the + module will be called gc05a2. + config VIDEO_GC08A3 tristate "GalaxyCore gc08a3 sensor support" select V4L2_CCI_I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 8817b8e86989..fbb988bd067a 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_VIDEO_DW9768) += dw9768.o obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/ obj-$(CONFIG_VIDEO_GC0308) += gc0308.o +obj-$(CONFIG_VIDEO_GC05A2) += gc05a2.o obj-$(CONFIG_VIDEO_GC08A3) += gc08a3.o obj-$(CONFIG_VIDEO_GC2145) += gc2145.o obj-$(CONFIG_VIDEO_HI556) += hi556.o diff --git a/drivers/media/i2c/gc05a2.c b/drivers/media/i2c/gc05a2.c new file mode 100644 index 000000000000..dcba29ee725c --- /dev/null +++ b/drivers/media/i2c/gc05a2.c @@ -0,0 +1,1359 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for GalaxyCore gc05a2 image sensor + * + * Copyright 2024 MediaTek + * + * Zhi Mao + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define GC05A2_REG_TEST_PATTERN_EN CCI_REG8(0x008c) +#define GC05A2_REG_TEST_PATTERN_IDX CCI_REG8(0x008d) +#define GC05A2_TEST_PATTERN_EN 0x01 + +#define GC05A2_STREAMING_REG CCI_REG8(0x0100) + +#define GC05A2_FLIP_REG CCI_REG8(0x0101) +#define GC05A2_FLIP_H_MASK BIT(0) +#define GC05A2_FLIP_V_MASK BIT(1) + +#define GC05A2_EXP_REG CCI_REG16(0x0202) +#define GC05A2_EXP_MARGIN 16 +#define GC05A2_EXP_MIN 4 +#define GC05A2_EXP_STEP 1 + +#define GC05A2_AGAIN_REG CCI_REG16(0x0204) +#define GC05A2_AGAIN_MIN 1024 +#define GC05A2_AGAIN_MAX (1024 * 16) +#define GC05A2_AGAIN_STEP 1 + +#define GC05A2_FRAME_LENGTH_REG CCI_REG16(0x0340) +#define GC05A2_VTS_MAX 0xffff + +#define GC05A2_REG_CHIP_ID CCI_REG16(0x03f0) +#define GC05A2_CHIP_ID 0x05a2 + +#define GC05A2_NATIVE_WIDTH 2592 +#define GC05A2_NATIVE_HEIGHT 1944 + +#define GC05A2_DEFAULT_CLK_FREQ (24 * HZ_PER_MHZ) +#define GC05A2_MBUS_CODE MEDIA_BUS_FMT_SGRBG10_1X10 +#define GC05A2_DATA_LANES 2 +#define GC05A2_RGB_DEPTH 10 +#define GC05A2_SLEEP_US (2 * USEC_PER_MSEC) + +static const char *const gc05a2_test_pattern_menu[] = { + "No Pattern", "Fade_to_gray_Color Bar", "Color Bar", + "PN9", "Horizental_gradient", "Checkboard Pattern", + "Slant", "Resolution", "Solid Black", + "Solid White", +}; + +static const s64 gc05a2_link_freq_menu_items[] = { + (448 * HZ_PER_MHZ), + (224 * HZ_PER_MHZ), +}; + +static const char *const gc05a2_supply_name[] = { + "avdd", + "dvdd", + "dovdd", +}; + +struct gc05a2 { + struct device *dev; + struct v4l2_subdev sd; + struct media_pad pad; + + struct clk *xclk; + struct regulator_bulk_data supplies[ARRAY_SIZE(gc05a2_supply_name)]; + struct gpio_desc *reset_gpio; + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + + struct regmap *regmap; + unsigned long link_freq_bitmap; + + /* True if the device has been identified */ + bool identified; + const struct gc05a2_mode *cur_mode; +}; + +struct gc05a2_reg_list { + u32 num_of_regs; + const struct cci_reg_sequence *regs; +}; + +static const struct cci_reg_sequence mode_2592x1944[] = { + /* system */ + { CCI_REG8(0x0135), 0x01 }, + { CCI_REG8(0x0084), 0x21 }, + { CCI_REG8(0x0d05), 0xcc }, + { CCI_REG8(0x0218), 0x00 }, + { CCI_REG8(0x005e), 0x48 }, + { CCI_REG8(0x0d06), 0x01 }, + { CCI_REG8(0x0007), 0x16 }, + { CCI_REG8(0x0101), 0x00 }, + + /* analog */ + { CCI_REG8(0x0342), 0x07 }, + { CCI_REG8(0x0343), 0x28 }, + { CCI_REG8(0x0220), 0x07 }, + { CCI_REG8(0x0221), 0xd0 }, + { CCI_REG8(0x0202), 0x07 }, + { CCI_REG8(0x0203), 0x32 }, + { CCI_REG8(0x0340), 0x07 }, + { CCI_REG8(0x0341), 0xf0 }, + { CCI_REG8(0x0219), 0x00 }, + { CCI_REG8(0x0346), 0x00 }, + { CCI_REG8(0x0347), 0x04 }, + { CCI_REG8(0x0d14), 0x00 }, + { CCI_REG8(0x0d13), 0x05 }, + { CCI_REG8(0x0d16), 0x05 }, + { CCI_REG8(0x0d15), 0x1d }, + { CCI_REG8(0x00c0), 0x0a }, + { CCI_REG8(0x00c1), 0x30 }, + { CCI_REG8(0x034a), 0x07 }, + { CCI_REG8(0x034b), 0xa8 }, + { CCI_REG8(0x0e0a), 0x00 }, + { CCI_REG8(0x0e0b), 0x00 }, + { CCI_REG8(0x0e0e), 0x03 }, + { CCI_REG8(0x0e0f), 0x00 }, + { CCI_REG8(0x0e06), 0x0a }, + { CCI_REG8(0x0e23), 0x15 }, + { CCI_REG8(0x0e24), 0x15 }, + { CCI_REG8(0x0e2a), 0x10 }, + { CCI_REG8(0x0e2b), 0x10 }, + { CCI_REG8(0x0e17), 0x49 }, + { CCI_REG8(0x0e1b), 0x1c }, + { CCI_REG8(0x0e3a), 0x36 }, + { CCI_REG8(0x0d11), 0x84 }, + { CCI_REG8(0x0e52), 0x14 }, + { CCI_REG8(0x000b), 0x10 }, + { CCI_REG8(0x0008), 0x08 }, + { CCI_REG8(0x0223), 0x17 }, + { CCI_REG8(0x0d27), 0x39 }, + { CCI_REG8(0x0d22), 0x00 }, + { CCI_REG8(0x03f6), 0x0d }, + { CCI_REG8(0x0d04), 0x07 }, + { CCI_REG8(0x03f3), 0x72 }, + { CCI_REG8(0x03f4), 0xb8 }, + { CCI_REG8(0x03f5), 0xbc }, + { CCI_REG8(0x0d02), 0x73 }, + + /* auto load start */ + { CCI_REG8(0x00cb), 0x00 }, + + /* OUT 2592*1944 */ + { CCI_REG8(0x0350), 0x01 }, + { CCI_REG8(0x0353), 0x00 }, + { CCI_REG8(0x0354), 0x08 }, + { CCI_REG16(0x034c), 2592 }, /* Width */ + { CCI_REG8(0x021f), 0x14 }, + + /* MIPI */ + { CCI_REG8(0x0107), 0x05 }, + { CCI_REG8(0x0117), 0x01 }, + { CCI_REG8(0x0d81), 0x00 }, + { CCI_REG8(0x0d84), 0x0c }, + { CCI_REG8(0x0d85), 0xa8 }, + { CCI_REG8(0x0d86), 0x06 }, + { CCI_REG8(0x0d87), 0x55 }, + { CCI_REG8(0x0db3), 0x06 }, + { CCI_REG8(0x0db4), 0x08 }, + { CCI_REG8(0x0db5), 0x1e }, + { CCI_REG8(0x0db6), 0x02 }, + { CCI_REG8(0x0db8), 0x12 }, + { CCI_REG8(0x0db9), 0x0a }, + { CCI_REG8(0x0d93), 0x06 }, + { CCI_REG8(0x0d94), 0x09 }, + { CCI_REG8(0x0d95), 0x0d }, + { CCI_REG8(0x0d99), 0x0b }, + { CCI_REG8(0x0084), 0x01 }, + { CCI_REG8(0x0110), 0x01 }, +}; + +static const struct cci_reg_sequence mode_1280x720[] = { + /* system */ + { CCI_REG8(0x0135), 0x05 }, + { CCI_REG8(0x0084), 0x21 }, + { CCI_REG8(0x0d05), 0xcc }, + { CCI_REG8(0x0218), 0x80 }, + { CCI_REG8(0x005e), 0x49 }, + { CCI_REG8(0x0d06), 0x81 }, + { CCI_REG8(0x0007), 0x16 }, + { CCI_REG8(0x0101), 0x00 }, + + /* analog */ + { CCI_REG8(0x0342), 0x07 }, + { CCI_REG8(0x0343), 0x10 }, + { CCI_REG8(0x0220), 0x07 }, + { CCI_REG8(0x0221), 0xd0 }, + { CCI_REG8(0x0202), 0x03 }, + { CCI_REG8(0x0203), 0x32 }, + { CCI_REG8(0x0340), 0x04 }, + { CCI_REG8(0x0341), 0x08 }, + { CCI_REG8(0x0219), 0x00 }, + { CCI_REG8(0x0346), 0x01 }, + { CCI_REG8(0x0347), 0x00 }, + { CCI_REG8(0x0d14), 0x00 }, + { CCI_REG8(0x0d13), 0x05 }, + { CCI_REG8(0x0d16), 0x05 }, + { CCI_REG8(0x0d15), 0x1d }, + { CCI_REG8(0x00c0), 0x0a }, + { CCI_REG8(0x00c1), 0x30 }, + { CCI_REG8(0x034a), 0x05 }, + { CCI_REG8(0x034b), 0xb0 }, + { CCI_REG8(0x0e0a), 0x00 }, + { CCI_REG8(0x0e0b), 0x00 }, + { CCI_REG8(0x0e0e), 0x03 }, + { CCI_REG8(0x0e0f), 0x00 }, + { CCI_REG8(0x0e06), 0x0a }, + { CCI_REG8(0x0e23), 0x15 }, + { CCI_REG8(0x0e24), 0x15 }, + { CCI_REG8(0x0e2a), 0x10 }, + { CCI_REG8(0x0e2b), 0x10 }, + { CCI_REG8(0x0e17), 0x49 }, + { CCI_REG8(0x0e1b), 0x1c }, + { CCI_REG8(0x0e3a), 0x36 }, + { CCI_REG8(0x0d11), 0x84 }, + { CCI_REG8(0x0e52), 0x14 }, + { CCI_REG8(0x000b), 0x0e }, + { CCI_REG8(0x0008), 0x03 }, + { CCI_REG8(0x0223), 0x16 }, + { CCI_REG8(0x0d27), 0x39 }, + { CCI_REG8(0x0d22), 0x00 }, + { CCI_REG8(0x03f6), 0x0d }, + { CCI_REG8(0x0d04), 0x07 }, + { CCI_REG8(0x03f3), 0x72 }, + { CCI_REG8(0x03f4), 0xb8 }, + { CCI_REG8(0x03f5), 0xbc }, + { CCI_REG8(0x0d02), 0x73 }, + + /* auto load start */ + { CCI_REG8(0x00cb), 0xfc }, + + /* OUT 1280x720 */ + { CCI_REG8(0x0350), 0x01 }, + { CCI_REG8(0x0353), 0x00 }, + { CCI_REG8(0x0354), 0x0c }, + { CCI_REG16(0x034c), 1280 }, /* Width */ + { CCI_REG8(0x021f), 0x14 }, + + /* MIPI */ + { CCI_REG8(0x0107), 0x05 }, + { CCI_REG8(0x0117), 0x01 }, + { CCI_REG8(0x0d81), 0x00 }, + { CCI_REG8(0x0d84), 0x06 }, + { CCI_REG8(0x0d85), 0x40 }, + { CCI_REG8(0x0d86), 0x03 }, + { CCI_REG8(0x0d87), 0x21 }, + { CCI_REG8(0x0db3), 0x03 }, + { CCI_REG8(0x0db4), 0x04 }, + { CCI_REG8(0x0db5), 0x0d }, + { CCI_REG8(0x0db6), 0x01 }, + { CCI_REG8(0x0db8), 0x04 }, + { CCI_REG8(0x0db9), 0x06 }, + { CCI_REG8(0x0d93), 0x03 }, + { CCI_REG8(0x0d94), 0x04 }, + { CCI_REG8(0x0d95), 0x05 }, + { CCI_REG8(0x0d99), 0x06 }, + { CCI_REG8(0x0084), 0x01 }, + { CCI_REG8(0x0110), 0x01 }, +}; + +static const struct cci_reg_sequence mode_table_common[] = { + { GC05A2_STREAMING_REG, 0x00 }, + /* system */ + { CCI_REG8(0x0315), 0xd4 }, + { CCI_REG8(0x0d06), 0x01 }, + { CCI_REG8(0x0a70), 0x80 }, + { CCI_REG8(0x031a), 0x00 }, + { CCI_REG8(0x0314), 0x00 }, + { CCI_REG8(0x0130), 0x08 }, + { CCI_REG8(0x0132), 0x01 }, + { CCI_REG8(0x0136), 0x38 }, + { CCI_REG8(0x0137), 0x03 }, + { CCI_REG8(0x0134), 0x5b }, + { CCI_REG8(0x031c), 0xe0 }, + { CCI_REG8(0x0d82), 0x14 }, + { CCI_REG8(0x0dd1), 0x56 }, + { CCI_REG8(0x0af4), 0x01 }, + { CCI_REG8(0x0002), 0x10 }, + { CCI_REG8(0x00c3), 0x34 }, + { CCI_REG8(0x00c4), 0x00 }, + { CCI_REG8(0x00c5), 0x01 }, + { CCI_REG8(0x0af6), 0x00 }, + { CCI_REG8(0x0ba0), 0x17 }, + { CCI_REG8(0x0ba1), 0x00 }, + { CCI_REG8(0x0ba2), 0x00 }, + { CCI_REG8(0x0ba3), 0x00 }, + { CCI_REG8(0x0ba4), 0x03 }, + { CCI_REG8(0x0ba5), 0x00 }, + { CCI_REG8(0x0ba6), 0x00 }, + { CCI_REG8(0x0ba7), 0x00 }, + { CCI_REG8(0x0ba8), 0x40 }, + { CCI_REG8(0x0ba9), 0x00 }, + { CCI_REG8(0x0baa), 0x00 }, + { CCI_REG8(0x0bab), 0x00 }, + { CCI_REG8(0x0bac), 0x40 }, + { CCI_REG8(0x0bad), 0x00 }, + { CCI_REG8(0x0bae), 0x00 }, + { CCI_REG8(0x0baf), 0x00 }, + { CCI_REG8(0x0bb0), 0x02 }, + { CCI_REG8(0x0bb1), 0x00 }, + { CCI_REG8(0x0bb2), 0x00 }, + { CCI_REG8(0x0bb3), 0x00 }, + { CCI_REG8(0x0bb8), 0x02 }, + { CCI_REG8(0x0bb9), 0x00 }, + { CCI_REG8(0x0bba), 0x00 }, + { CCI_REG8(0x0bbb), 0x00 }, + { CCI_REG8(0x0a70), 0x80 }, + { CCI_REG8(0x0a71), 0x00 }, + { CCI_REG8(0x0a72), 0x00 }, + { CCI_REG8(0x0a66), 0x00 }, + { CCI_REG8(0x0a67), 0x80 }, + { CCI_REG8(0x0a4d), 0x4e }, + { CCI_REG8(0x0a50), 0x00 }, + { CCI_REG8(0x0a4f), 0x0c }, + { CCI_REG8(0x0a66), 0x00 }, + { CCI_REG8(0x00ca), 0x00 }, + { CCI_REG8(0x00cc), 0x00 }, + { CCI_REG8(0x00cd), 0x00 }, + { CCI_REG8(0x0aa1), 0x00 }, + { CCI_REG8(0x0aa2), 0xe0 }, + { CCI_REG8(0x0aa3), 0x00 }, + { CCI_REG8(0x0aa4), 0x40 }, + { CCI_REG8(0x0a90), 0x03 }, + { CCI_REG8(0x0a91), 0x0e }, + { CCI_REG8(0x0a94), 0x80 }, + { CCI_REG8(0x0af6), 0x20 }, + { CCI_REG8(0x0b00), 0x91 }, + { CCI_REG8(0x0b01), 0x17 }, + { CCI_REG8(0x0b02), 0x01 }, + { CCI_REG8(0x0b03), 0x00 }, + { CCI_REG8(0x0b04), 0x01 }, + { CCI_REG8(0x0b05), 0x17 }, + { CCI_REG8(0x0b06), 0x01 }, + { CCI_REG8(0x0b07), 0x00 }, + { CCI_REG8(0x0ae9), 0x01 }, + { CCI_REG8(0x0aea), 0x02 }, + { CCI_REG8(0x0ae8), 0x53 }, + { CCI_REG8(0x0ae8), 0x43 }, + { CCI_REG8(0x0af6), 0x30 }, + { CCI_REG8(0x0b00), 0x08 }, + { CCI_REG8(0x0b01), 0x0f }, + { CCI_REG8(0x0b02), 0x00 }, + { CCI_REG8(0x0b04), 0x1c }, + { CCI_REG8(0x0b05), 0x24 }, + { CCI_REG8(0x0b06), 0x00 }, + { CCI_REG8(0x0b08), 0x30 }, + { CCI_REG8(0x0b09), 0x40 }, + { CCI_REG8(0x0b0a), 0x00 }, + { CCI_REG8(0x0b0c), 0x0e }, + { CCI_REG8(0x0b0d), 0x2a }, + { CCI_REG8(0x0b0e), 0x00 }, + { CCI_REG8(0x0b10), 0x0e }, + { CCI_REG8(0x0b11), 0x2b }, + { CCI_REG8(0x0b12), 0x00 }, + { CCI_REG8(0x0b14), 0x0e }, + { CCI_REG8(0x0b15), 0x23 }, + { CCI_REG8(0x0b16), 0x00 }, + { CCI_REG8(0x0b18), 0x0e }, + { CCI_REG8(0x0b19), 0x24 }, + { CCI_REG8(0x0b1a), 0x00 }, + { CCI_REG8(0x0b1c), 0x0c }, + { CCI_REG8(0x0b1d), 0x0c }, + { CCI_REG8(0x0b1e), 0x00 }, + { CCI_REG8(0x0b20), 0x03 }, + { CCI_REG8(0x0b21), 0x03 }, + { CCI_REG8(0x0b22), 0x00 }, + { CCI_REG8(0x0b24), 0x0e }, + { CCI_REG8(0x0b25), 0x0e }, + { CCI_REG8(0x0b26), 0x00 }, + { CCI_REG8(0x0b28), 0x03 }, + { CCI_REG8(0x0b29), 0x03 }, + { CCI_REG8(0x0b2a), 0x00 }, + { CCI_REG8(0x0b2c), 0x12 }, + { CCI_REG8(0x0b2d), 0x12 }, + { CCI_REG8(0x0b2e), 0x00 }, + { CCI_REG8(0x0b30), 0x08 }, + { CCI_REG8(0x0b31), 0x08 }, + { CCI_REG8(0x0b32), 0x00 }, + { CCI_REG8(0x0b34), 0x14 }, + { CCI_REG8(0x0b35), 0x14 }, + { CCI_REG8(0x0b36), 0x00 }, + { CCI_REG8(0x0b38), 0x10 }, + { CCI_REG8(0x0b39), 0x10 }, + { CCI_REG8(0x0b3a), 0x00 }, + { CCI_REG8(0x0b3c), 0x16 }, + { CCI_REG8(0x0b3d), 0x16 }, + { CCI_REG8(0x0b3e), 0x00 }, + { CCI_REG8(0x0b40), 0x10 }, + { CCI_REG8(0x0b41), 0x10 }, + { CCI_REG8(0x0b42), 0x00 }, + { CCI_REG8(0x0b44), 0x19 }, + { CCI_REG8(0x0b45), 0x19 }, + { CCI_REG8(0x0b46), 0x00 }, + { CCI_REG8(0x0b48), 0x16 }, + { CCI_REG8(0x0b49), 0x16 }, + { CCI_REG8(0x0b4a), 0x00 }, + { CCI_REG8(0x0b4c), 0x19 }, + { CCI_REG8(0x0b4d), 0x19 }, + { CCI_REG8(0x0b4e), 0x00 }, + { CCI_REG8(0x0b50), 0x16 }, + { CCI_REG8(0x0b51), 0x16 }, + { CCI_REG8(0x0b52), 0x00 }, + { CCI_REG8(0x0b80), 0x01 }, + { CCI_REG8(0x0b81), 0x00 }, + { CCI_REG8(0x0b82), 0x00 }, + { CCI_REG8(0x0b84), 0x00 }, + { CCI_REG8(0x0b85), 0x00 }, + { CCI_REG8(0x0b86), 0x00 }, + { CCI_REG8(0x0b88), 0x01 }, + { CCI_REG8(0x0b89), 0x6a }, + { CCI_REG8(0x0b8a), 0x00 }, + { CCI_REG8(0x0b8c), 0x00 }, + { CCI_REG8(0x0b8d), 0x01 }, + { CCI_REG8(0x0b8e), 0x00 }, + { CCI_REG8(0x0b90), 0x01 }, + { CCI_REG8(0x0b91), 0xf6 }, + { CCI_REG8(0x0b92), 0x00 }, + { CCI_REG8(0x0b94), 0x00 }, + { CCI_REG8(0x0b95), 0x02 }, + { CCI_REG8(0x0b96), 0x00 }, + { CCI_REG8(0x0b98), 0x02 }, + { CCI_REG8(0x0b99), 0xc4 }, + { CCI_REG8(0x0b9a), 0x00 }, + { CCI_REG8(0x0b9c), 0x00 }, + { CCI_REG8(0x0b9d), 0x03 }, + { CCI_REG8(0x0b9e), 0x00 }, + { CCI_REG8(0x0ba0), 0x03 }, + { CCI_REG8(0x0ba1), 0xd8 }, + { CCI_REG8(0x0ba2), 0x00 }, + { CCI_REG8(0x0ba4), 0x00 }, + { CCI_REG8(0x0ba5), 0x04 }, + { CCI_REG8(0x0ba6), 0x00 }, + { CCI_REG8(0x0ba8), 0x05 }, + { CCI_REG8(0x0ba9), 0x4d }, + { CCI_REG8(0x0baa), 0x00 }, + { CCI_REG8(0x0bac), 0x00 }, + { CCI_REG8(0x0bad), 0x05 }, + { CCI_REG8(0x0bae), 0x00 }, + { CCI_REG8(0x0bb0), 0x07 }, + { CCI_REG8(0x0bb1), 0x3e }, + { CCI_REG8(0x0bb2), 0x00 }, + { CCI_REG8(0x0bb4), 0x00 }, + { CCI_REG8(0x0bb5), 0x06 }, + { CCI_REG8(0x0bb6), 0x00 }, + { CCI_REG8(0x0bb8), 0x0a }, + { CCI_REG8(0x0bb9), 0x1a }, + { CCI_REG8(0x0bba), 0x00 }, + { CCI_REG8(0x0bbc), 0x09 }, + { CCI_REG8(0x0bbd), 0x36 }, + { CCI_REG8(0x0bbe), 0x00 }, + { CCI_REG8(0x0bc0), 0x0e }, + { CCI_REG8(0x0bc1), 0x66 }, + { CCI_REG8(0x0bc2), 0x00 }, + { CCI_REG8(0x0bc4), 0x10 }, + { CCI_REG8(0x0bc5), 0x06 }, + { CCI_REG8(0x0bc6), 0x00 }, + { CCI_REG8(0x02c1), 0xe0 }, + { CCI_REG8(0x0207), 0x04 }, + { CCI_REG8(0x02c2), 0x10 }, + { CCI_REG8(0x02c3), 0x74 }, + { CCI_REG8(0x02c5), 0x09 }, + { CCI_REG8(0x02c1), 0xe0 }, + { CCI_REG8(0x0207), 0x04 }, + { CCI_REG8(0x02c2), 0x10 }, + { CCI_REG8(0x02c5), 0x09 }, + { CCI_REG8(0x02c1), 0xe0 }, + { CCI_REG8(0x0207), 0x04 }, + { CCI_REG8(0x02c2), 0x10 }, + { CCI_REG8(0x02c5), 0x09 }, + { CCI_REG8(0x0aa1), 0x15 }, + { CCI_REG8(0x0aa2), 0x50 }, + { CCI_REG8(0x0aa3), 0x00 }, + { CCI_REG8(0x0aa4), 0x09 }, + { CCI_REG8(0x0a90), 0x25 }, + { CCI_REG8(0x0a91), 0x0e }, + { CCI_REG8(0x0a94), 0x80 }, + + /* ISP */ + { CCI_REG8(0x0050), 0x00 }, + { CCI_REG8(0x0089), 0x83 }, + { CCI_REG8(0x005a), 0x40 }, + { CCI_REG8(0x00c3), 0x35 }, + { CCI_REG8(0x00c4), 0x80 }, + { CCI_REG8(0x0080), 0x10 }, + { CCI_REG8(0x0040), 0x12 }, + { CCI_REG8(0x0053), 0x0a }, + { CCI_REG8(0x0054), 0x44 }, + { CCI_REG8(0x0055), 0x32 }, + { CCI_REG8(0x0058), 0x89 }, + { CCI_REG8(0x004a), 0x03 }, + { CCI_REG8(0x0048), 0xf0 }, + { CCI_REG8(0x0049), 0x0f }, + { CCI_REG8(0x0041), 0x20 }, + { CCI_REG8(0x0043), 0x0a }, + { CCI_REG8(0x009d), 0x08 }, + { CCI_REG8(0x0236), 0x40 }, + { CCI_REG8(0x0204), 0x04 }, + { CCI_REG8(0x0205), 0x00 }, + { CCI_REG8(0x02b3), 0x00 }, + { CCI_REG8(0x02b4), 0x00 }, + { CCI_REG8(0x009e), 0x01 }, + { CCI_REG8(0x009f), 0x94 }, + + /* auto load REG */ + { CCI_REG8(0x0aa1), 0x10 }, + { CCI_REG8(0x0aa2), 0xf8 }, + { CCI_REG8(0x0aa3), 0x00 }, + { CCI_REG8(0x0aa4), 0x1f }, + { CCI_REG8(0x0a90), 0x11 }, + { CCI_REG8(0x0a91), 0x0e }, + { CCI_REG8(0x0a94), 0x80 }, + { CCI_REG8(0x03fe), 0x00 }, + { CCI_REG8(0x0a90), 0x00 }, + { CCI_REG8(0x0a70), 0x00 }, + { CCI_REG8(0x0a67), 0x00 }, + { CCI_REG8(0x0af4), 0x29 }, + + /* DPHY */ + { CCI_REG8(0x0d80), 0x07 }, + { CCI_REG8(0x0dd3), 0x18 }, + + /* CISCTL_Reset */ + { CCI_REG8(0x031c), 0x80 }, + { CCI_REG8(0x03fe), 0x30 }, + { CCI_REG8(0x0d17), 0x06 }, + { CCI_REG8(0x03fe), 0x00 }, + { CCI_REG8(0x0d17), 0x00 }, + { CCI_REG8(0x031c), 0x93 }, + { CCI_REG8(0x03fe), 0x00 }, + { CCI_REG8(0x031c), 0x80 }, + { CCI_REG8(0x03fe), 0x30 }, + { CCI_REG8(0x0d17), 0x06 }, + { CCI_REG8(0x03fe), 0x00 }, + { CCI_REG8(0x0d17), 0x00 }, + { CCI_REG8(0x031c), 0x93 }, +}; + +struct gc05a2_mode { + u32 width; + u32 height; + const struct gc05a2_reg_list reg_list; + + u32 hts; /* Horizontal timining size */ + u32 vts_def; /* Default vertical timining size */ + u32 vts_min; /* Min vertical timining size */ +}; + +/* Declare modes in order, from biggest to smallest height. */ +static const struct gc05a2_mode gc05a2_modes[] = { + { + /* 2592*1944@30fps */ + .width = GC05A2_NATIVE_WIDTH, + .height = GC05A2_NATIVE_HEIGHT, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_2592x1944), + .regs = mode_2592x1944, + }, + .hts = 3664, + .vts_def = 2032, + .vts_min = 2032, + }, + { + /* 1280*720@60fps */ + .width = 1280, + .height = 720, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1280x720), + .regs = mode_1280x720, + }, + .hts = 3616, + .vts_def = 1032, + .vts_min = 1032, + }, +}; + +static inline struct gc05a2 *to_gc05a2(struct v4l2_subdev *sd) +{ + return container_of(sd, struct gc05a2, sd); +} + +static int gc05a2_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gc05a2 *gc05a2 = to_gc05a2(sd); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(gc05a2_supply_name), + gc05a2->supplies); + if (ret < 0) { + dev_err(gc05a2->dev, "failed to enable regulators: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(gc05a2->xclk); + if (ret < 0) { + regulator_bulk_disable(ARRAY_SIZE(gc05a2_supply_name), + gc05a2->supplies); + dev_err(gc05a2->dev, "clk prepare enable failed\n"); + return ret; + } + + fsleep(GC05A2_SLEEP_US); + + gpiod_set_value_cansleep(gc05a2->reset_gpio, 0); + fsleep(GC05A2_SLEEP_US); + + return 0; +} + +static int gc05a2_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct gc05a2 *gc05a2 = to_gc05a2(sd); + + clk_disable_unprepare(gc05a2->xclk); + gpiod_set_value_cansleep(gc05a2->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(gc05a2_supply_name), + gc05a2->supplies); + + return 0; +} + +static int gc05a2_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = GC05A2_MBUS_CODE; + + return 0; +} + +static int gc05a2_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->code != GC05A2_MBUS_CODE) + return -EINVAL; + + if (fse->index >= ARRAY_SIZE(gc05a2_modes)) + return -EINVAL; + + fse->min_width = gc05a2_modes[fse->index].width; + fse->max_width = gc05a2_modes[fse->index].width; + fse->min_height = gc05a2_modes[fse->index].height; + fse->max_height = gc05a2_modes[fse->index].height; + + return 0; +} + +static int gc05a2_update_cur_mode_controls(struct gc05a2 *gc05a2, + const struct gc05a2_mode *mode) +{ + s64 exposure_max, h_blank; + int ret; + + ret = __v4l2_ctrl_modify_range(gc05a2->vblank, + mode->vts_min - mode->height, + GC05A2_VTS_MAX - mode->height, 1, + mode->vts_def - mode->height); + if (ret) { + dev_err(gc05a2->dev, "VB ctrl range update failed\n"); + return ret; + } + + h_blank = mode->hts - mode->width; + ret = __v4l2_ctrl_modify_range(gc05a2->hblank, h_blank, h_blank, 1, + h_blank); + if (ret) { + dev_err(gc05a2->dev, "HB ctrl range update failed\n"); + return ret; + } + + exposure_max = mode->vts_def - GC05A2_EXP_MARGIN; + ret = __v4l2_ctrl_modify_range(gc05a2->exposure, GC05A2_EXP_MIN, + exposure_max, GC05A2_EXP_STEP, + exposure_max); + if (ret) { + dev_err(gc05a2->dev, "exposure ctrl range update failed\n"); + return ret; + } + + return 0; +} + +static void gc05a2_update_pad_format(struct gc05a2 *gc08a3, + const struct gc05a2_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = GC05A2_MBUS_CODE; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_RAW; + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->xfer_func = V4L2_XFER_FUNC_NONE; +} + +static int gc05a2_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct gc05a2 *gc05a2 = to_gc05a2(sd); + struct v4l2_mbus_framefmt *mbus_fmt; + struct v4l2_rect *crop; + const struct gc05a2_mode *mode; + + mode = v4l2_find_nearest_size(gc05a2_modes, ARRAY_SIZE(gc05a2_modes), + width, height, fmt->format.width, + fmt->format.height); + + /* update crop info to subdev state */ + crop = v4l2_subdev_state_get_crop(state, 0); + crop->width = mode->width; + crop->height = mode->height; + + /* update fmt info to subdev state */ + gc05a2_update_pad_format(gc05a2, mode, &fmt->format); + mbus_fmt = v4l2_subdev_state_get_format(state, 0); + *mbus_fmt = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + gc05a2->cur_mode = mode; + gc05a2_update_cur_mode_controls(gc05a2, mode); + + return 0; +} + +static int gc05a2_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(state, 0); + break; + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = GC05A2_NATIVE_WIDTH; + sel->r.height = GC05A2_NATIVE_HEIGHT; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int gc05a2_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = 0, + .format = { + .code = GC05A2_MBUS_CODE, + .width = gc05a2_modes[0].width, + .height = gc05a2_modes[0].height, + }, + }; + + gc05a2_set_format(sd, state, &fmt); + + return 0; +} + +static int gc05a2_set_ctrl_hflip(struct gc05a2 *gc05a2, u32 ctrl_val) +{ + int ret; + u64 val; + + ret = cci_read(gc05a2->regmap, GC05A2_FLIP_REG, &val, NULL); + if (ret) { + dev_err(gc05a2->dev, "read hflip register failed: %d\n", ret); + return ret; + } + + return cci_update_bits(gc05a2->regmap, GC05A2_FLIP_REG, + GC05A2_FLIP_H_MASK, + ctrl_val ? GC05A2_FLIP_H_MASK : 0, NULL); +} + +static int gc05a2_set_ctrl_vflip(struct gc05a2 *gc05a2, u32 ctrl_val) +{ + int ret; + u64 val; + + ret = cci_read(gc05a2->regmap, GC05A2_FLIP_REG, &val, NULL); + if (ret) { + dev_err(gc05a2->dev, "read vflip register failed: %d\n", ret); + return ret; + } + + return cci_update_bits(gc05a2->regmap, GC05A2_FLIP_REG, + GC05A2_FLIP_V_MASK, + ctrl_val ? GC05A2_FLIP_V_MASK : 0, NULL); +} + +static int gc05a2_test_pattern(struct gc05a2 *gc05a2, u32 pattern_menu) +{ + u32 pattern; + int ret; + + if (pattern_menu) { + switch (pattern_menu) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + pattern = pattern_menu << 4; + break; + + case 8: + pattern = 0; + break; + + case 9: + pattern = 4; + break; + + default: + /* Set pattern to 0, it's a safe default. */ + pattern = 0; + break; + } + + ret = cci_write(gc05a2->regmap, GC05A2_REG_TEST_PATTERN_IDX, + pattern, NULL); + if (ret) + return ret; + + return cci_write(gc05a2->regmap, GC05A2_REG_TEST_PATTERN_EN, + GC05A2_TEST_PATTERN_EN, NULL); + } else { + return cci_write(gc05a2->regmap, GC05A2_REG_TEST_PATTERN_EN, + 0x00, NULL); + } +} + +static int gc05a2_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gc05a2 *gc05a2 = + container_of(ctrl->handler, struct gc05a2, ctrls); + int ret = 0; + s64 exposure_max; + struct v4l2_subdev_state *state; + const struct v4l2_mbus_framefmt *format; + + state = v4l2_subdev_get_locked_active_state(&gc05a2->sd); + format = v4l2_subdev_state_get_format(state, 0); + + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = format->height + ctrl->val - GC05A2_EXP_MARGIN; + __v4l2_ctrl_modify_range(gc05a2->exposure, + gc05a2->exposure->minimum, + exposure_max, gc05a2->exposure->step, + exposure_max); + } + + /* + * Applying V4L2 control value only happens + * when power is on for streaming. + */ + if (!pm_runtime_get_if_active(gc05a2->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + ret = cci_write(gc05a2->regmap, GC05A2_EXP_REG, + ctrl->val, NULL); + break; + + case V4L2_CID_ANALOGUE_GAIN: + ret = cci_write(gc05a2->regmap, GC05A2_AGAIN_REG, + ctrl->val, NULL); + break; + + case V4L2_CID_VBLANK: + ret = cci_write(gc05a2->regmap, GC05A2_FRAME_LENGTH_REG, + gc05a2->cur_mode->height + ctrl->val, NULL); + break; + + case V4L2_CID_HFLIP: + ret = gc05a2_set_ctrl_hflip(gc05a2, ctrl->val); + break; + + case V4L2_CID_VFLIP: + ret = gc05a2_set_ctrl_vflip(gc05a2, ctrl->val); + break; + + case V4L2_CID_TEST_PATTERN: + ret = gc05a2_test_pattern(gc05a2, ctrl->val); + break; + + default: + break; + } + + pm_runtime_put(gc05a2->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops gc05a2_ctrl_ops = { + .s_ctrl = gc05a2_set_ctrl, +}; + +static int gc05a2_identify_module(struct gc05a2 *gc05a2) +{ + u64 val; + int ret; + + if (gc05a2->identified) + return 0; + + ret = cci_read(gc05a2->regmap, GC05A2_REG_CHIP_ID, &val, NULL); + if (ret) + return ret; + + if (val != GC05A2_CHIP_ID) { + dev_err(gc05a2->dev, "chip id mismatch: 0x%x!=0x%llx", + GC05A2_CHIP_ID, val); + return -ENXIO; + } + + gc05a2->identified = true; + + return 0; +} + +static int gc05a2_start_streaming(struct gc05a2 *gc05a2) +{ + const struct gc05a2_mode *mode; + const struct gc05a2_reg_list *reg_list; + int ret; + + ret = pm_runtime_resume_and_get(gc05a2->dev); + if (ret < 0) + return ret; + + ret = gc05a2_identify_module(gc05a2); + if (ret) + goto err_rpm_put; + + ret = cci_multi_reg_write(gc05a2->regmap, + mode_table_common, + ARRAY_SIZE(mode_table_common), NULL); + if (ret) + goto err_rpm_put; + + mode = gc05a2->cur_mode; + reg_list = &mode->reg_list; + + ret = cci_multi_reg_write(gc05a2->regmap, + reg_list->regs, reg_list->num_of_regs, NULL); + if (ret < 0) + goto err_rpm_put; + + ret = __v4l2_ctrl_handler_setup(&gc05a2->ctrls); + if (ret < 0) { + dev_err(gc05a2->dev, "could not sync v4l2 controls\n"); + goto err_rpm_put; + } + + ret = cci_write(gc05a2->regmap, GC05A2_STREAMING_REG, 1, NULL); + if (ret < 0) { + dev_err(gc05a2->dev, "write STREAMING_REG failed: %d\n", ret); + goto err_rpm_put; + } + + return 0; + +err_rpm_put: + pm_runtime_put(gc05a2->dev); + return ret; +} + +static int gc05a2_stop_streaming(struct gc05a2 *gc05a2) +{ + int ret; + + ret = cci_write(gc05a2->regmap, GC05A2_STREAMING_REG, 0, NULL); + if (ret < 0) + dev_err(gc05a2->dev, "could not sent stop streaming %d\n", ret); + + pm_runtime_put(gc05a2->dev); + return ret; +} + +static int gc05a2_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct gc05a2 *gc05a2 = to_gc05a2(subdev); + struct v4l2_subdev_state *state; + int ret; + + state = v4l2_subdev_lock_and_get_active_state(subdev); + + if (enable) + ret = gc05a2_start_streaming(gc05a2); + else + ret = gc05a2_stop_streaming(gc05a2); + + v4l2_subdev_unlock_state(state); + + return ret; +} + +static const struct v4l2_subdev_video_ops gc05a2_video_ops = { + .s_stream = gc05a2_s_stream, +}; + +static const struct v4l2_subdev_pad_ops gc05a2_subdev_pad_ops = { + .enum_mbus_code = gc05a2_enum_mbus_code, + .enum_frame_size = gc05a2_enum_frame_size, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = gc05a2_set_format, + .get_selection = gc05a2_get_selection, +}; + +static const struct v4l2_subdev_core_ops gc05a2_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_ops gc05a2_subdev_ops = { + .core = &gc05a2_core_ops, + .video = &gc05a2_video_ops, + .pad = &gc05a2_subdev_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops gc05a2_internal_ops = { + .init_state = gc05a2_init_state, +}; + +static int gc05a2_get_regulators(struct device *dev, struct gc05a2 *gc05a2) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(gc05a2_supply_name); i++) + gc05a2->supplies[i].supply = gc05a2_supply_name[i]; + + return devm_regulator_bulk_get(dev, ARRAY_SIZE(gc05a2_supply_name), + gc05a2->supplies); +} + +static int gc05a2_parse_fwnode(struct gc05a2 *gc05a2) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + int ret; + struct device *dev = gc05a2->dev; + + endpoint = + fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!endpoint) + return dev_err_probe(dev, -EINVAL, "Missing endpoint node\n"); + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg); + if (ret) { + dev_err_probe(dev, ret, "parsing endpoint node failed\n"); + goto done; + } + + ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + gc05a2_link_freq_menu_items, + ARRAY_SIZE(gc05a2_link_freq_menu_items), + &gc05a2->link_freq_bitmap); + if (ret) + goto done; + +done: + v4l2_fwnode_endpoint_free(&bus_cfg); + fwnode_handle_put(endpoint); + return ret; +} + +static u64 gc05a2_to_pixel_rate(u32 f_index) +{ + u64 pixel_rate = + gc05a2_link_freq_menu_items[f_index] * 2 * GC05A2_DATA_LANES; + + return div_u64(pixel_rate, GC05A2_RGB_DEPTH); +} + +static int gc05a2_init_controls(struct gc05a2 *gc05a2) +{ + struct i2c_client *client = v4l2_get_subdevdata(&gc05a2->sd); + const struct gc05a2_mode *mode = &gc05a2_modes[0]; + const struct v4l2_ctrl_ops *ops = &gc05a2_ctrl_ops; + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl_handler *ctrl_hdlr; + s64 exposure_max, h_blank; + int ret; + + ctrl_hdlr = &gc05a2->ctrls; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9); + if (ret) + return ret; + + gc05a2->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &gc05a2_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + gc05a2->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &gc05a2_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &gc05a2->hflip); + + gc05a2->link_freq = + v4l2_ctrl_new_int_menu(ctrl_hdlr, + &gc05a2_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(gc05a2_link_freq_menu_items) - 1, + 0, + gc05a2_link_freq_menu_items); + if (gc05a2->link_freq) + gc05a2->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + gc05a2->pixel_rate = + v4l2_ctrl_new_std(ctrl_hdlr, + &gc05a2_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, + gc05a2_to_pixel_rate(0), + 1, + gc05a2_to_pixel_rate(0)); + + gc05a2->vblank = + v4l2_ctrl_new_std(ctrl_hdlr, + &gc05a2_ctrl_ops, V4L2_CID_VBLANK, + mode->vts_min - mode->height, + GC05A2_VTS_MAX - mode->height, 1, + mode->vts_def - mode->height); + + h_blank = mode->hts - mode->width; + gc05a2->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &gc05a2_ctrl_ops, + V4L2_CID_HBLANK, h_blank, h_blank, 1, + h_blank); + if (gc05a2->hblank) + gc05a2->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &gc05a2_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, GC05A2_AGAIN_MIN, + GC05A2_AGAIN_MAX, GC05A2_AGAIN_STEP, + GC05A2_AGAIN_MIN); + + exposure_max = mode->vts_def - GC05A2_EXP_MARGIN; + gc05a2->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &gc05a2_ctrl_ops, + V4L2_CID_EXPOSURE, GC05A2_EXP_MIN, + exposure_max, GC05A2_EXP_STEP, + exposure_max); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &gc05a2_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(gc05a2_test_pattern_menu) - 1, + 0, 0, gc05a2_test_pattern_menu); + + /* register properties to fwnode (e.g. rotation, orientation) */ + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto error_ctrls; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, ops, &props); + if (ret) + goto error_ctrls; + + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + goto error_ctrls; + } + + gc05a2->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error_ctrls: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ret; +} + +static int gc05a2_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct gc05a2 *gc05a2; + int ret; + + gc05a2 = devm_kzalloc(dev, sizeof(*gc05a2), GFP_KERNEL); + if (!gc05a2) + return -ENOMEM; + + gc05a2->dev = dev; + + ret = gc05a2_parse_fwnode(gc05a2); + if (ret) + return ret; + + gc05a2->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(gc05a2->regmap)) + return dev_err_probe(dev, PTR_ERR(gc05a2->regmap), + "failed to init CCI\n"); + + gc05a2->xclk = devm_clk_get(dev, NULL); + if (IS_ERR(gc05a2->xclk)) + return dev_err_probe(dev, PTR_ERR(gc05a2->xclk), + "failed to get xclk\n"); + + ret = clk_set_rate(gc05a2->xclk, GC05A2_DEFAULT_CLK_FREQ); + if (ret) + return dev_err_probe(dev, ret, + "failed to set xclk frequency\n"); + + ret = gc05a2_get_regulators(dev, gc05a2); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to get regulators\n"); + + gc05a2->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gc05a2->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(gc05a2->reset_gpio), + "failed to get gpio\n"); + + v4l2_i2c_subdev_init(&gc05a2->sd, client, &gc05a2_subdev_ops); + gc05a2->sd.internal_ops = &gc05a2_internal_ops; + gc05a2->cur_mode = &gc05a2_modes[0]; + + ret = gc05a2_init_controls(gc05a2); + if (ret) + return dev_err_probe(dev, ret, + "failed to init controls\n"); + + gc05a2->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + gc05a2->pad.flags = MEDIA_PAD_FL_SOURCE; + gc05a2->sd.dev = &client->dev; + gc05a2->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + ret = media_entity_pads_init(&gc05a2->sd.entity, 1, &gc05a2->pad); + if (ret < 0) { + dev_err_probe(dev, ret, "could not register media entity\n"); + goto err_v4l2_ctrl_handler_free; + } + + gc05a2->sd.state_lock = gc05a2->ctrls.lock; + ret = v4l2_subdev_init_finalize(&gc05a2->sd); + if (ret < 0) { + dev_err_probe(dev, ret, "v4l2 subdev init error\n"); + goto err_media_entity_cleanup; + } + + pm_runtime_enable(gc05a2->dev); + pm_runtime_set_autosuspend_delay(gc05a2->dev, 1000); + pm_runtime_use_autosuspend(gc05a2->dev); + pm_runtime_idle(gc05a2->dev); + + ret = v4l2_async_register_subdev_sensor(&gc05a2->sd); + if (ret < 0) { + dev_err_probe(dev, ret, "could not register v4l2 device\n"); + goto err_rpm; + } + + return 0; + +err_rpm: + pm_runtime_disable(gc05a2->dev); + v4l2_subdev_cleanup(&gc05a2->sd); + +err_media_entity_cleanup: + media_entity_cleanup(&gc05a2->sd.entity); + +err_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(&gc05a2->ctrls); + + return ret; +} + +static void gc05a2_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct gc05a2 *gc05a2 = to_gc05a2(sd); + + v4l2_async_unregister_subdev(&gc05a2->sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&gc05a2->sd.entity); + v4l2_ctrl_handler_free(&gc05a2->ctrls); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + gc05a2_power_off(gc05a2->dev); + pm_runtime_set_suspended(&client->dev); +} + +static const struct of_device_id gc05a2_of_match[] = { + { .compatible = "galaxycore,gc05a2" }, + {} +}; +MODULE_DEVICE_TABLE(of, gc05a2_of_match); + +static DEFINE_RUNTIME_DEV_PM_OPS(gc05a2_pm_ops, + gc05a2_power_off, + gc05a2_power_on, + NULL); + +static struct i2c_driver gc05a2_i2c_driver = { + .driver = { + .of_match_table = gc05a2_of_match, + .pm = pm_ptr(&gc05a2_pm_ops), + .name = "gc05a2", + }, + .probe = gc05a2_probe, + .remove = gc05a2_remove, +}; +module_i2c_driver(gc05a2_i2c_driver); + +MODULE_DESCRIPTION("GalaxyCore gc05a2 Camera driver"); +MODULE_AUTHOR("Zhi Mao "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 9123419c3b12dda7f81442299758f3bcd1aa9883 Mon Sep 17 00:00:00 2001 From: Samuel Wein Date: Mon, 17 Jun 2024 18:53:09 +0000 Subject: media: Documentation: ipu6: Fix examples in ipu6-isys admin-guide Fix flags in X1 Yoga example. MEDIA_LNK_FL_DYNAMIC (0x4 in the link flag) was removed in V4 Intel IPU6 and IPU6 input system drivers. Added -V flag to media-ctl commands for X1 Yoga, lower-case v only makes it verbose upper-case V sets the format. Signed-off-by: Samuel Wein [Sakari Ailus: Align subject line, rewrap commit message.] Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- Documentation/admin-guide/media/ipu6-isys.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Documentation/admin-guide/media/ipu6-isys.rst b/Documentation/admin-guide/media/ipu6-isys.rst index 0721e920b5e6..d05086824a74 100644 --- a/Documentation/admin-guide/media/ipu6-isys.rst +++ b/Documentation/admin-guide/media/ipu6-isys.rst @@ -135,16 +135,16 @@ sensor ov2740 on Lenovo X1 Yoga laptop. .. code-block:: none media-ctl -l "\"ov2740 14-0036\":0 -> \"Intel IPU6 CSI2 1\":0[1]" - media-ctl -l "\"Intel IPU6 CSI2 1\":1 -> \"Intel IPU6 ISYS Capture 0\":0[5]" - media-ctl -l "\"Intel IPU6 CSI2 1\":2 -> \"Intel IPU6 ISYS Capture 1\":0[5]" + media-ctl -l "\"Intel IPU6 CSI2 1\":1 -> \"Intel IPU6 ISYS Capture 0\":0[1]" + media-ctl -l "\"Intel IPU6 CSI2 1\":2 -> \"Intel IPU6 ISYS Capture 1\":0[1]" # set routing - media-ctl -v -R "\"Intel IPU6 CSI2 1\" [0/0->1/0[1],0/1->2/1[1]]" + media-ctl -R "\"Intel IPU6 CSI2 1\" [0/0->1/0[1],0/1->2/1[1]]" - media-ctl -v "\"Intel IPU6 CSI2 1\":0/0 [fmt:SGRBG10/1932x1092]" - media-ctl -v "\"Intel IPU6 CSI2 1\":0/1 [fmt:GENERIC_8/97x1]" - media-ctl -v "\"Intel IPU6 CSI2 1\":1/0 [fmt:SGRBG10/1932x1092]" - media-ctl -v "\"Intel IPU6 CSI2 1\":2/1 [fmt:GENERIC_8/97x1]" + media-ctl -V "\"Intel IPU6 CSI2 1\":0/0 [fmt:SGRBG10/1932x1092]" + media-ctl -V "\"Intel IPU6 CSI2 1\":0/1 [fmt:GENERIC_8/97x1]" + media-ctl -V "\"Intel IPU6 CSI2 1\":1/0 [fmt:SGRBG10/1932x1092]" + media-ctl -V "\"Intel IPU6 CSI2 1\":2/1 [fmt:GENERIC_8/97x1]" CAPTURE_DEV=$(media-ctl -e "Intel IPU6 ISYS Capture 0") ./yavta --data-prefix -c100 -n5 -I -s1932x1092 --file=/tmp/frame-#.bin \ -- cgit v1.2.3 From bf9817d2ed3a499d4ec1a5125fb700185b2f2499 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 3 Jun 2024 12:01:17 +0200 Subject: MAINTAINERS: delete email for Anton Sviridenko The email address bounced. I couldn't find a newer one in recent git history, so update MAINTAINERS accordingly. Signed-off-by: Wolfram Sang Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- MAINTAINERS | 2 -- 1 file changed, 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 94a024a15af2..ad1ecb2c4aed 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20741,7 +20741,6 @@ F: include/uapi/rdma/rdma_user_rxe.h SOFTLOGIC 6x10 MPEG CODEC M: Bluecherry Maintainers -M: Anton Sviridenko M: Andrey Utkin M: Ismael Luceno L: linux-media@vger.kernel.org @@ -22953,7 +22952,6 @@ F: tools/testing/selftests/turbostat/ TW5864 VIDEO4LINUX DRIVER M: Bluecherry Maintainers -M: Anton Sviridenko M: Andrey Utkin M: Andrey Utkin L: linux-media@vger.kernel.org -- cgit v1.2.3 From 57e9ce68ae98551da9c161aaab12b41fe8601856 Mon Sep 17 00:00:00 2001 From: Harshit Mogalapalli Date: Tue, 14 May 2024 02:50:38 -0700 Subject: media: imx-pxp: Fix ERR_PTR dereference in pxp_probe() devm_regmap_init_mmio() can fail, add a check and bail out in case of error. Fixes: 4e5bd3fdbeb3 ("media: imx-pxp: convert to regmap") Cc: stable@vger.kernel.org Signed-off-by: Harshit Mogalapalli Reviewed-by: Laurent Pinchart Link: https://lore.kernel.org/r/20240514095038.3464191-1-harshit.m.mogalapalli@oracle.com Signed-off-by: Laurent Pinchart --- drivers/media/platform/nxp/imx-pxp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/nxp/imx-pxp.c b/drivers/media/platform/nxp/imx-pxp.c index e62dc5c1a4ae..e4427e6487fb 100644 --- a/drivers/media/platform/nxp/imx-pxp.c +++ b/drivers/media/platform/nxp/imx-pxp.c @@ -1805,6 +1805,9 @@ static int pxp_probe(struct platform_device *pdev) return PTR_ERR(mmio); dev->regmap = devm_regmap_init_mmio(&pdev->dev, mmio, &pxp_regmap_config); + if (IS_ERR(dev->regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(dev->regmap), + "Failed to init regmap\n"); irq = platform_get_irq(pdev, 0); if (irq < 0) -- cgit v1.2.3 From fea074e97886b7ee2fe04a17bd60fd41723da1b7 Mon Sep 17 00:00:00 2001 From: Kory Maincent Date: Thu, 20 Jun 2024 12:25:43 +0200 Subject: media: i2c: Kconfig: Fix missing firmware upload config select FW_LOADER config only selects the firmware loader API, but we also need the sysfs_upload symbols for firmware_upload_unregister() and firmware_upload_register() to function properly. Fixes: 7a52ab415b43 ("media: i2c: Add driver for THine THP7312") Cc: stable@vger.kernel.org Signed-off-by: Kory Maincent Reviewed-by: Laurent Pinchart Reviewed-by: Paul Elder Link: https://lore.kernel.org/r/20240620102544.1918105-1-kory.maincent@bootlin.com Signed-off-by: Laurent Pinchart --- drivers/media/i2c/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 8aac41513b59..8ba096b8ebca 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -710,6 +710,7 @@ config VIDEO_THP7312 tristate "THine THP7312 support" depends on I2C select FW_LOADER + select FW_UPLOAD select MEDIA_CONTROLLER select V4L2_CCI_I2C select V4L2_FWNODE -- cgit v1.2.3 From a047b66c0f05c668b4a6d41a3cacfe707efc9ecb Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 20 Jun 2024 01:53:43 +0300 Subject: media: v4l: subdev: Fix typo in documentation Replace the incorrect reference to the v4l2_subdev_enable_stream() function with the correct v4l2_subdev_enable_streams() spelling. Fixes: d0749adb3070 ("media: v4l2-subdev: Add subdev .(enable|disable)_streams() operations") Reviewed-by: Tomi Valkeinen Link: https://lore.kernel.org/r/20240619225343.15873-1-laurent.pinchart@ideasonboard.com Signed-off-by: Laurent Pinchart --- include/media/v4l2-subdev.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 1b74e037e440..bd235d325ff9 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -690,7 +690,7 @@ struct v4l2_subdev_pad_config { * * @pad: pad number * @stream: stream number - * @enabled: has the stream been enabled with v4l2_subdev_enable_stream() + * @enabled: has the stream been enabled with v4l2_subdev_enable_streams() * @fmt: &struct v4l2_mbus_framefmt * @crop: &struct v4l2_rect to be used for crop * @compose: &struct v4l2_rect to be used for compose -- cgit v1.2.3 From 2ef9a1e722688ccea824e5f224b91ab4b6fb1a47 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 25 Apr 2024 02:57:33 +0300 Subject: media: vimc: Don't iterate over single pad The .init_state() operations of the debayer and sensor entities iterate over the entity's pads. In practice, the iteration covers a single pad only. Access the pad directly and remove the loops. Signed-off-by: Laurent Pinchart Reviewed-by: Sakari Ailus Signed-off-by: Shuah Khan Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vimc/vimc-debayer.c | 9 +++------ drivers/media/test-drivers/vimc/vimc-sensor.c | 10 +++------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/media/test-drivers/vimc/vimc-debayer.c b/drivers/media/test-drivers/vimc/vimc-debayer.c index d72ed086e00b..e1bf6db73050 100644 --- a/drivers/media/test-drivers/vimc/vimc-debayer.c +++ b/drivers/media/test-drivers/vimc/vimc-debayer.c @@ -155,16 +155,13 @@ static int vimc_debayer_init_state(struct v4l2_subdev *sd, { struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf; - unsigned int i; mf = v4l2_subdev_state_get_format(sd_state, 0); *mf = sink_fmt_default; - for (i = 1; i < sd->entity.num_pads; i++) { - mf = v4l2_subdev_state_get_format(sd_state, i); - *mf = sink_fmt_default; - mf->code = vdebayer->src_code; - } + mf = v4l2_subdev_state_get_format(sd_state, 1); + *mf = sink_fmt_default; + mf->code = vdebayer->src_code; return 0; } diff --git a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c index 5e34b1aed95e..b535b3ffecff 100644 --- a/drivers/media/test-drivers/vimc/vimc-sensor.c +++ b/drivers/media/test-drivers/vimc/vimc-sensor.c @@ -44,14 +44,10 @@ static const struct v4l2_mbus_framefmt fmt_default = { static int vimc_sensor_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; + struct v4l2_mbus_framefmt *mf; - mf = v4l2_subdev_state_get_format(sd_state, i); - *mf = fmt_default; - } + mf = v4l2_subdev_state_get_format(sd_state, 0); + *mf = fmt_default; return 0; } -- cgit v1.2.3 From 73a4385c6947df393ffb4bcdf84eef1294057d0c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 25 Apr 2024 02:57:34 +0300 Subject: media: vimc: Constify vimc_ent_type structures The vimc_ent_type structure contains static pointers to functions, and no other information that need to be modified after initialization. Make them const to avoid the risk of arbitrary code execution following an overflow that would overwrite the structure's contents. Signed-off-by: Laurent Pinchart Signed-off-by: Shuah Khan Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vimc/vimc-capture.c | 2 +- drivers/media/test-drivers/vimc/vimc-common.h | 12 ++++++------ drivers/media/test-drivers/vimc/vimc-debayer.c | 2 +- drivers/media/test-drivers/vimc/vimc-lens.c | 2 +- drivers/media/test-drivers/vimc/vimc-scaler.c | 2 +- drivers/media/test-drivers/vimc/vimc-sensor.c | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/media/test-drivers/vimc/vimc-capture.c b/drivers/media/test-drivers/vimc/vimc-capture.c index ba7550b8ba7e..89506ae00901 100644 --- a/drivers/media/test-drivers/vimc/vimc-capture.c +++ b/drivers/media/test-drivers/vimc/vimc-capture.c @@ -494,7 +494,7 @@ err_free_vcapture: return ERR_PTR(ret); } -struct vimc_ent_type vimc_capture_type = { +const struct vimc_ent_type vimc_capture_type = { .add = vimc_capture_add, .unregister = vimc_capture_unregister, .release = vimc_capture_release diff --git a/drivers/media/test-drivers/vimc/vimc-common.h b/drivers/media/test-drivers/vimc/vimc-common.h index 7641a101a728..6a76717e0384 100644 --- a/drivers/media/test-drivers/vimc/vimc-common.h +++ b/drivers/media/test-drivers/vimc/vimc-common.h @@ -156,7 +156,7 @@ struct vimc_ent_type { */ struct vimc_ent_config { const char *name; - struct vimc_ent_type *type; + const struct vimc_ent_type *type; }; /** @@ -167,11 +167,11 @@ struct vimc_ent_config { */ bool vimc_is_source(struct media_entity *ent); -extern struct vimc_ent_type vimc_sensor_type; -extern struct vimc_ent_type vimc_debayer_type; -extern struct vimc_ent_type vimc_scaler_type; -extern struct vimc_ent_type vimc_capture_type; -extern struct vimc_ent_type vimc_lens_type; +extern const struct vimc_ent_type vimc_sensor_type; +extern const struct vimc_ent_type vimc_debayer_type; +extern const struct vimc_ent_type vimc_scaler_type; +extern const struct vimc_ent_type vimc_capture_type; +extern const struct vimc_ent_type vimc_lens_type; /** * vimc_pix_map_by_index - get vimc_pix_map struct by its index diff --git a/drivers/media/test-drivers/vimc/vimc-debayer.c b/drivers/media/test-drivers/vimc/vimc-debayer.c index e1bf6db73050..e2f12a7fb58f 100644 --- a/drivers/media/test-drivers/vimc/vimc-debayer.c +++ b/drivers/media/test-drivers/vimc/vimc-debayer.c @@ -622,7 +622,7 @@ err_free_vdebayer: return ERR_PTR(ret); } -struct vimc_ent_type vimc_debayer_type = { +const struct vimc_ent_type vimc_debayer_type = { .add = vimc_debayer_add, .release = vimc_debayer_release }; diff --git a/drivers/media/test-drivers/vimc/vimc-lens.c b/drivers/media/test-drivers/vimc/vimc-lens.c index 3ce7f4b4d2cc..e7d78fa8ccdb 100644 --- a/drivers/media/test-drivers/vimc/vimc-lens.c +++ b/drivers/media/test-drivers/vimc/vimc-lens.c @@ -96,7 +96,7 @@ static void vimc_lens_release(struct vimc_ent_device *ved) kfree(vlens); } -struct vimc_ent_type vimc_lens_type = { +const struct vimc_ent_type vimc_lens_type = { .add = vimc_lens_add, .release = vimc_lens_release }; diff --git a/drivers/media/test-drivers/vimc/vimc-scaler.c b/drivers/media/test-drivers/vimc/vimc-scaler.c index afe13d6af321..3e32cfb79c64 100644 --- a/drivers/media/test-drivers/vimc/vimc-scaler.c +++ b/drivers/media/test-drivers/vimc/vimc-scaler.c @@ -442,7 +442,7 @@ static struct vimc_ent_device *vimc_scaler_add(struct vimc_device *vimc, return &vscaler->ved; } -struct vimc_ent_type vimc_scaler_type = { +const struct vimc_ent_type vimc_scaler_type = { .add = vimc_scaler_add, .release = vimc_scaler_release }; diff --git a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c index b535b3ffecff..11df18332865 100644 --- a/drivers/media/test-drivers/vimc/vimc-sensor.c +++ b/drivers/media/test-drivers/vimc/vimc-sensor.c @@ -448,7 +448,7 @@ err_free_vsensor: return ERR_PTR(ret); } -struct vimc_ent_type vimc_sensor_type = { +const struct vimc_ent_type vimc_sensor_type = { .add = vimc_sensor_add, .release = vimc_sensor_release }; -- cgit v1.2.3 From 27d8f61ee977e15bc1b6c36fca1abe5d08919782 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 25 Apr 2024 02:57:35 +0300 Subject: media: vimc: Constify the ent_config array The ent_config array contains data that is never modified. Make it const. Signed-off-by: Laurent Pinchart Signed-off-by: Shuah Khan Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vimc/vimc-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c index af127476e920..2083c60e34d6 100644 --- a/drivers/media/test-drivers/vimc/vimc-core.c +++ b/drivers/media/test-drivers/vimc/vimc-core.c @@ -81,7 +81,7 @@ struct vimc_pipeline_config { * Topology Configuration */ -static struct vimc_ent_config ent_config[] = { +static const struct vimc_ent_config ent_config[] = { [SENSOR_A] = { .name = "Sensor A", .type = &vimc_sensor_type -- cgit v1.2.3 From 0b3b27bb69e4b5a043830176932c9b7f8fa09cf3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 25 Apr 2024 02:57:36 +0300 Subject: media: vimc: scaler: Rename vic_sca_pad enum to vimc_scaler_pad The vic_sca_pad enum's name has been shortened to the extreme for no good reason. Rename it to vimc_scaler_pad. Signed-off-by: Laurent Pinchart Signed-off-by: Shuah Khan Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vimc/vimc-scaler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/test-drivers/vimc/vimc-scaler.c b/drivers/media/test-drivers/vimc/vimc-scaler.c index 3e32cfb79c64..013cd84f82be 100644 --- a/drivers/media/test-drivers/vimc/vimc-scaler.c +++ b/drivers/media/test-drivers/vimc/vimc-scaler.c @@ -15,7 +15,7 @@ #include "vimc-common.h" /* Pad identifier */ -enum vic_sca_pad { +enum vimc_scaler_pad { VIMC_SCALER_SINK = 0, VIMC_SCALER_SRC = 1, }; -- cgit v1.2.3 From 556d821ade98edd76c59368a3301fbb30014b851 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 25 Apr 2024 02:57:37 +0300 Subject: media: vimc: Centralize subdev internal_ops initialization Initialize the subdev internal_ops field in the vimc_ent_sd_register() function. This handles the internal ops the same way as the subdev ops, and prepares for moving to the V4L2 subdev active state API. Signed-off-by: Laurent Pinchart Signed-off-by: Shuah Khan Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vimc/vimc-common.c | 2 ++ drivers/media/test-drivers/vimc/vimc-common.h | 2 ++ drivers/media/test-drivers/vimc/vimc-debayer.c | 5 ++--- drivers/media/test-drivers/vimc/vimc-lens.c | 2 +- drivers/media/test-drivers/vimc/vimc-scaler.c | 5 ++--- drivers/media/test-drivers/vimc/vimc-sensor.c | 4 +--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/media/test-drivers/vimc/vimc-common.c b/drivers/media/test-drivers/vimc/vimc-common.c index 2e72974e35b4..3da2271215c6 100644 --- a/drivers/media/test-drivers/vimc/vimc-common.c +++ b/drivers/media/test-drivers/vimc/vimc-common.c @@ -358,6 +358,7 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, u32 function, u16 num_pads, struct media_pad *pads, + const struct v4l2_subdev_internal_ops *int_ops, const struct v4l2_subdev_ops *sd_ops) { int ret; @@ -367,6 +368,7 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, /* Initialize the subdev */ v4l2_subdev_init(sd, sd_ops); + sd->internal_ops = int_ops; sd->entity.function = function; sd->entity.ops = &vimc_ent_sd_mops; sd->owner = THIS_MODULE; diff --git a/drivers/media/test-drivers/vimc/vimc-common.h b/drivers/media/test-drivers/vimc/vimc-common.h index 6a76717e0384..7a45a2117748 100644 --- a/drivers/media/test-drivers/vimc/vimc-common.h +++ b/drivers/media/test-drivers/vimc/vimc-common.h @@ -215,6 +215,7 @@ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat); * @num_pads: number of pads to initialize * @pads: the array of pads of the entity, the caller should set the * flags of the pads + * @int_ops: pointer to &struct v4l2_subdev_internal_ops. * @sd_ops: pointer to &struct v4l2_subdev_ops. * * Helper function initialize and register the struct vimc_ent_device and struct @@ -227,6 +228,7 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, u32 function, u16 num_pads, struct media_pad *pads, + const struct v4l2_subdev_internal_ops *int_ops, const struct v4l2_subdev_ops *sd_ops); /** diff --git a/drivers/media/test-drivers/vimc/vimc-debayer.c b/drivers/media/test-drivers/vimc/vimc-debayer.c index e2f12a7fb58f..d4ca57b3672d 100644 --- a/drivers/media/test-drivers/vimc/vimc-debayer.c +++ b/drivers/media/test-drivers/vimc/vimc-debayer.c @@ -591,12 +591,11 @@ static struct vimc_ent_device *vimc_debayer_add(struct vimc_device *vimc, ret = vimc_ent_sd_register(&vdebayer->ved, &vdebayer->sd, v4l2_dev, vcfg_name, MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV, 2, - vdebayer->pads, &vimc_debayer_ops); + vdebayer->pads, &vimc_debayer_internal_ops, + &vimc_debayer_ops); if (ret) goto err_free_hdl; - vdebayer->sd.internal_ops = &vimc_debayer_internal_ops; - vdebayer->ved.process_frame = vimc_debayer_process_frame; vdebayer->ved.dev = vimc->mdev.dev; vdebayer->mean_win_size = vimc_debayer_ctrl_mean_win_size.def; diff --git a/drivers/media/test-drivers/vimc/vimc-lens.c b/drivers/media/test-drivers/vimc/vimc-lens.c index e7d78fa8ccdb..42c58a3e3f1b 100644 --- a/drivers/media/test-drivers/vimc/vimc-lens.c +++ b/drivers/media/test-drivers/vimc/vimc-lens.c @@ -72,7 +72,7 @@ static struct vimc_ent_device *vimc_lens_add(struct vimc_device *vimc, ret = vimc_ent_sd_register(&vlens->ved, &vlens->sd, v4l2_dev, vcfg_name, MEDIA_ENT_F_LENS, 0, - NULL, &vimc_lens_ops); + NULL, NULL, &vimc_lens_ops); if (ret) goto err_free_hdl; diff --git a/drivers/media/test-drivers/vimc/vimc-scaler.c b/drivers/media/test-drivers/vimc/vimc-scaler.c index 013cd84f82be..4f9c44a663e1 100644 --- a/drivers/media/test-drivers/vimc/vimc-scaler.c +++ b/drivers/media/test-drivers/vimc/vimc-scaler.c @@ -421,14 +421,13 @@ static struct vimc_ent_device *vimc_scaler_add(struct vimc_device *vimc, ret = vimc_ent_sd_register(&vscaler->ved, &vscaler->sd, v4l2_dev, vcfg_name, MEDIA_ENT_F_PROC_VIDEO_SCALER, 2, - vscaler->pads, &vimc_scaler_ops); + vscaler->pads, &vimc_scaler_internal_ops, + &vimc_scaler_ops); if (ret) { kfree(vscaler); return ERR_PTR(ret); } - vscaler->sd.internal_ops = &vimc_scaler_internal_ops; - vscaler->ved.process_frame = vimc_scaler_process_frame; vscaler->ved.dev = vimc->mdev.dev; diff --git a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c index 11df18332865..5c31d9435cdd 100644 --- a/drivers/media/test-drivers/vimc/vimc-sensor.c +++ b/drivers/media/test-drivers/vimc/vimc-sensor.c @@ -424,12 +424,10 @@ static struct vimc_ent_device *vimc_sensor_add(struct vimc_device *vimc, ret = vimc_ent_sd_register(&vsensor->ved, &vsensor->sd, v4l2_dev, vcfg_name, MEDIA_ENT_F_CAM_SENSOR, 1, &vsensor->pad, - &vimc_sensor_ops); + &vimc_sensor_internal_ops, &vimc_sensor_ops); if (ret) goto err_free_tpg; - vsensor->sd.internal_ops = &vimc_sensor_internal_ops; - vsensor->ved.process_frame = vimc_sensor_process_frame; vsensor->ved.dev = vimc->mdev.dev; -- cgit v1.2.3 From b3f73b21887ecda4c03a61085c7290ce585016d0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 25 Apr 2024 02:57:38 +0300 Subject: media: vimc: Initialize subdev active state Finalize subdev initialization for all subdevs that provide a .init_state() operation. This creates an active state for all those subdevs, which subsequent patches will use to simplify the implementation of individual vimc entities. Signed-off-by: Laurent Pinchart Signed-off-by: Shuah Khan Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vimc/vimc-common.c | 23 ++++++++++++++++++++++- drivers/media/test-drivers/vimc/vimc-debayer.c | 1 + drivers/media/test-drivers/vimc/vimc-lens.c | 1 + drivers/media/test-drivers/vimc/vimc-scaler.c | 1 + drivers/media/test-drivers/vimc/vimc-sensor.c | 1 + 5 files changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/media/test-drivers/vimc/vimc-common.c b/drivers/media/test-drivers/vimc/vimc-common.c index 3da2271215c6..4f4fcb26e236 100644 --- a/drivers/media/test-drivers/vimc/vimc-common.c +++ b/drivers/media/test-drivers/vimc/vimc-common.c @@ -8,6 +8,8 @@ #include #include +#include + #include "vimc-common.h" /* @@ -385,17 +387,36 @@ int vimc_ent_sd_register(struct vimc_ent_device *ved, if (ret) return ret; + /* + * Finalize the subdev initialization if it supports active states. Use + * the control handler lock as the state lock if available. + */ + if (int_ops && int_ops->init_state) { + if (sd->ctrl_handler) + sd->state_lock = sd->ctrl_handler->lock; + + ret = v4l2_subdev_init_finalize(sd); + if (ret) { + dev_err(v4l2_dev->dev, + "%s: subdev initialization failed (err=%d)\n", + name, ret); + goto err_clean_m_ent; + } + } + /* Register the subdev with the v4l2 and the media framework */ ret = v4l2_device_register_subdev(v4l2_dev, sd); if (ret) { dev_err(v4l2_dev->dev, "%s: subdev register failed (err=%d)\n", name, ret); - goto err_clean_m_ent; + goto err_clean_sd; } return 0; +err_clean_sd: + v4l2_subdev_cleanup(sd); err_clean_m_ent: media_entity_cleanup(&sd->entity); return ret; diff --git a/drivers/media/test-drivers/vimc/vimc-debayer.c b/drivers/media/test-drivers/vimc/vimc-debayer.c index d4ca57b3672d..9f8a31fb0695 100644 --- a/drivers/media/test-drivers/vimc/vimc-debayer.c +++ b/drivers/media/test-drivers/vimc/vimc-debayer.c @@ -540,6 +540,7 @@ static void vimc_debayer_release(struct vimc_ent_device *ved) container_of(ved, struct vimc_debayer_device, ved); v4l2_ctrl_handler_free(&vdebayer->hdl); + v4l2_subdev_cleanup(&vdebayer->sd); media_entity_cleanup(vdebayer->ved.ent); kfree(vdebayer); } diff --git a/drivers/media/test-drivers/vimc/vimc-lens.c b/drivers/media/test-drivers/vimc/vimc-lens.c index 42c58a3e3f1b..96399057a2b5 100644 --- a/drivers/media/test-drivers/vimc/vimc-lens.c +++ b/drivers/media/test-drivers/vimc/vimc-lens.c @@ -92,6 +92,7 @@ static void vimc_lens_release(struct vimc_ent_device *ved) container_of(ved, struct vimc_lens_device, ved); v4l2_ctrl_handler_free(&vlens->hdl); + v4l2_subdev_cleanup(&vlens->sd); media_entity_cleanup(vlens->ved.ent); kfree(vlens); } diff --git a/drivers/media/test-drivers/vimc/vimc-scaler.c b/drivers/media/test-drivers/vimc/vimc-scaler.c index 4f9c44a663e1..f8639f5b4d0c 100644 --- a/drivers/media/test-drivers/vimc/vimc-scaler.c +++ b/drivers/media/test-drivers/vimc/vimc-scaler.c @@ -398,6 +398,7 @@ static void vimc_scaler_release(struct vimc_ent_device *ved) struct vimc_scaler_device *vscaler = container_of(ved, struct vimc_scaler_device, ved); + v4l2_subdev_cleanup(&vscaler->sd); media_entity_cleanup(vscaler->ved.ent); kfree(vscaler); } diff --git a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c index 5c31d9435cdd..9eb24c4791bf 100644 --- a/drivers/media/test-drivers/vimc/vimc-sensor.c +++ b/drivers/media/test-drivers/vimc/vimc-sensor.c @@ -340,6 +340,7 @@ static void vimc_sensor_release(struct vimc_ent_device *ved) v4l2_ctrl_handler_free(&vsensor->hdl); tpg_free(&vsensor->tpg); + v4l2_subdev_cleanup(&vsensor->sd); media_entity_cleanup(vsensor->ved.ent); kfree(vsensor); } -- cgit v1.2.3 From cf2552d87ac0e50f98c1e18d233a74ca58af819f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 25 Apr 2024 02:57:39 +0300 Subject: media: vimc: sensor: Use subdev active state Store the active formats and crop rectangle in the subdevice active state. This simplifies implementation of the format and selection accessors, and allows using the v4l2_subdev_get_fmt() helper to implement the .get_fmt() operation. The active configuration that is used in the .process_frame() handler is still stored in the vimc_sensor_device structure. The driver could instead access the active state in the .process_frame() handler, but the required locking could interfere with the real time constraints of the frame processing. This data would be stored in registers in the .s_stream() handler for real hardware, storing it in dedicated storage thus mimics a real driver. To differentiate them from the rest of the device private data, move the corresponding fields to a sub-structure of vimc_sensor_device named hw. Signed-off-by: Laurent Pinchart Signed-off-by: Shuah Khan Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vimc/vimc-sensor.c | 108 ++++++++++++-------------- 1 file changed, 50 insertions(+), 58 deletions(-) diff --git a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c index 9eb24c4791bf..027767777763 100644 --- a/drivers/media/test-drivers/vimc/vimc-sensor.c +++ b/drivers/media/test-drivers/vimc/vimc-sensor.c @@ -24,13 +24,20 @@ struct vimc_sensor_device { struct vimc_ent_device ved; struct v4l2_subdev sd; struct tpg_data tpg; - u8 *frame; - enum vimc_sensor_osd_mode osd_value; - u64 start_stream_ts; - /* The active format */ - struct v4l2_mbus_framefmt mbus_format; struct v4l2_ctrl_handler hdl; struct media_pad pad; + + u8 *frame; + + /* + * Virtual "hardware" configuration, filled when the stream starts or + * when controls are set. + */ + struct { + struct v4l2_area size; + enum vimc_sensor_osd_mode osd_value; + u64 start_stream_ts; + } hw; }; static const struct v4l2_mbus_framefmt fmt_default = { @@ -88,36 +95,22 @@ static int vimc_sensor_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int vimc_sensor_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) +static void vimc_sensor_tpg_s_format(struct vimc_sensor_device *vsensor, + const struct v4l2_mbus_framefmt *format) { - struct vimc_sensor_device *vsensor = - container_of(sd, struct vimc_sensor_device, sd); - - fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? - *v4l2_subdev_state_get_format(sd_state, fmt->pad) : - vsensor->mbus_format; + const struct vimc_pix_map *vpix = vimc_pix_map_by_code(format->code); - return 0; -} - -static void vimc_sensor_tpg_s_format(struct vimc_sensor_device *vsensor) -{ - const struct vimc_pix_map *vpix = - vimc_pix_map_by_code(vsensor->mbus_format.code); - - tpg_reset_source(&vsensor->tpg, vsensor->mbus_format.width, - vsensor->mbus_format.height, vsensor->mbus_format.field); - tpg_s_bytesperline(&vsensor->tpg, 0, vsensor->mbus_format.width * vpix->bpp); - tpg_s_buf_height(&vsensor->tpg, vsensor->mbus_format.height); + tpg_reset_source(&vsensor->tpg, format->width, format->height, + format->field); + tpg_s_bytesperline(&vsensor->tpg, 0, format->width * vpix->bpp); + tpg_s_buf_height(&vsensor->tpg, format->height); tpg_s_fourcc(&vsensor->tpg, vpix->pixelformat); /* TODO: add support for V4L2_FIELD_ALTERNATE */ - tpg_s_field(&vsensor->tpg, vsensor->mbus_format.field, false); - tpg_s_colorspace(&vsensor->tpg, vsensor->mbus_format.colorspace); - tpg_s_ycbcr_enc(&vsensor->tpg, vsensor->mbus_format.ycbcr_enc); - tpg_s_quantization(&vsensor->tpg, vsensor->mbus_format.quantization); - tpg_s_xfer_func(&vsensor->tpg, vsensor->mbus_format.xfer_func); + tpg_s_field(&vsensor->tpg, format->field, false); + tpg_s_colorspace(&vsensor->tpg, format->colorspace); + tpg_s_ycbcr_enc(&vsensor->tpg, format->ycbcr_enc); + tpg_s_quantization(&vsensor->tpg, format->quantization); + tpg_s_xfer_func(&vsensor->tpg, format->xfer_func); } static void vimc_sensor_adjust_fmt(struct v4l2_mbus_framefmt *fmt) @@ -148,15 +141,11 @@ static int vimc_sensor_set_fmt(struct v4l2_subdev *sd, struct vimc_sensor_device *vsensor = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf; - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - /* Do not change the format while stream is on */ - if (vsensor->frame) - return -EBUSY; + /* Do not change the format while stream is on */ + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE && vsensor->frame) + return -EBUSY; - mf = &vsensor->mbus_format; - } else { - mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); - } + mf = v4l2_subdev_state_get_format(sd_state, fmt->pad); /* Set the new format */ vimc_sensor_adjust_fmt(&fmt->format); @@ -181,7 +170,7 @@ static int vimc_sensor_set_fmt(struct v4l2_subdev *sd, static const struct v4l2_subdev_pad_ops vimc_sensor_pad_ops = { .enum_mbus_code = vimc_sensor_enum_mbus_code, .enum_frame_size = vimc_sensor_enum_frame_size, - .get_fmt = vimc_sensor_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = vimc_sensor_set_fmt, }; @@ -198,7 +187,7 @@ static void *vimc_sensor_process_frame(struct vimc_ent_device *ved, tpg_fill_plane_buffer(&vsensor->tpg, 0, 0, vsensor->frame); tpg_calc_text_basep(&vsensor->tpg, basep, 0, vsensor->frame); - switch (vsensor->osd_value) { + switch (vsensor->hw.osd_value) { case VIMC_SENSOR_OSD_SHOW_ALL: { const char *order = tpg_g_color_order(&vsensor->tpg); @@ -212,15 +201,14 @@ static void *vimc_sensor_process_frame(struct vimc_ent_device *ved, vsensor->tpg.hue); tpg_gen_text(&vsensor->tpg, basep, line++ * line_height, 16, str); snprintf(str, sizeof(str), "sensor size: %dx%d", - vsensor->mbus_format.width, - vsensor->mbus_format.height); + vsensor->hw.size.width, vsensor->hw.size.height); tpg_gen_text(&vsensor->tpg, basep, line++ * line_height, 16, str); fallthrough; } case VIMC_SENSOR_OSD_SHOW_COUNTERS: { unsigned int ms; - ms = div_u64(ktime_get_ns() - vsensor->start_stream_ts, 1000000); + ms = div_u64(ktime_get_ns() - vsensor->hw.start_stream_ts, 1000000); snprintf(str, sizeof(str), "%02d:%02d:%02d:%03d", (ms / (60 * 60 * 1000)) % 24, (ms / (60 * 1000)) % 60, @@ -243,15 +231,25 @@ static int vimc_sensor_s_stream(struct v4l2_subdev *sd, int enable) container_of(sd, struct vimc_sensor_device, sd); if (enable) { + const struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; const struct vimc_pix_map *vpix; unsigned int frame_size; - vsensor->start_stream_ts = ktime_get_ns(); + state = v4l2_subdev_lock_and_get_active_state(sd); + format = v4l2_subdev_state_get_format(state, 0); - /* Calculate the frame size */ - vpix = vimc_pix_map_by_code(vsensor->mbus_format.code); - frame_size = vsensor->mbus_format.width * vpix->bpp * - vsensor->mbus_format.height; + /* Configure the test pattern generator. */ + vimc_sensor_tpg_s_format(vsensor, format); + + /* Calculate the frame size. */ + vpix = vimc_pix_map_by_code(format->code); + frame_size = format->width * vpix->bpp * format->height; + + vsensor->hw.size.width = format->width; + vsensor->hw.size.height = format->height; + + v4l2_subdev_unlock_state(state); /* * Allocate the frame buffer. Use vmalloc to be able to @@ -261,9 +259,7 @@ static int vimc_sensor_s_stream(struct v4l2_subdev *sd, int enable) if (!vsensor->frame) return -ENOMEM; - /* configure the test pattern generator */ - vimc_sensor_tpg_s_format(vsensor); - + vsensor->hw.start_stream_ts = ktime_get_ns(); } else { vfree(vsensor->frame); @@ -321,7 +317,7 @@ static int vimc_sensor_s_ctrl(struct v4l2_ctrl *ctrl) tpg_s_saturation(&vsensor->tpg, ctrl->val); break; case VIMC_CID_OSD_TEXT_MODE: - vsensor->osd_value = ctrl->val; + vsensor->hw.osd_value = ctrl->val; break; default: return -EINVAL; @@ -414,8 +410,7 @@ static struct vimc_ent_device *vimc_sensor_add(struct vimc_device *vimc, } /* Initialize the test pattern generator */ - tpg_init(&vsensor->tpg, vsensor->mbus_format.width, - vsensor->mbus_format.height); + tpg_init(&vsensor->tpg, fmt_default.width, fmt_default.height); ret = tpg_alloc(&vsensor->tpg, VIMC_FRAME_MAX_WIDTH); if (ret) goto err_free_hdl; @@ -432,9 +427,6 @@ static struct vimc_ent_device *vimc_sensor_add(struct vimc_device *vimc, vsensor->ved.process_frame = vimc_sensor_process_frame; vsensor->ved.dev = vimc->mdev.dev; - /* Initialize the frame format */ - vsensor->mbus_format = fmt_default; - return &vsensor->ved; err_free_tpg: -- cgit v1.2.3 From 4c46cb2a7f1d63fa7b027af05e0393e4fe9ccb52 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 25 Apr 2024 02:57:40 +0300 Subject: media: vimc: debayer: Use subdev active state Store the active formats and crop rectangle in the subdevice active state. This simplifies implementation of the format and selection accessors, and allows using the v4l2_subdev_get_fmt() helper to implement the .get_fmt() operation. The active configuration that is used in the .process_frame() handler is still stored in the vimc_debayer_device structure. The driver could instead access the active state in the .process_frame() handler, but the required locking could interfere with the real time constraints of the frame processing. This data would be stored in registers in the .s_stream() handler for real hardware, storing it in dedicated storage thus mimics a real driver. To differentiate them from the rest of the device private data, move the corresponding fields to a sub-structure of vimc_debayer_device named hw. Signed-off-by: Laurent Pinchart Signed-off-by: Shuah Khan Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vimc/vimc-debayer.c | 186 +++++++++++-------------- 1 file changed, 85 insertions(+), 101 deletions(-) diff --git a/drivers/media/test-drivers/vimc/vimc-debayer.c b/drivers/media/test-drivers/vimc/vimc-debayer.c index 9f8a31fb0695..bbb7c7a86df0 100644 --- a/drivers/media/test-drivers/vimc/vimc-debayer.c +++ b/drivers/media/test-drivers/vimc/vimc-debayer.c @@ -15,6 +15,9 @@ #include "vimc-common.h" +/* TODO: Add support for more output formats, we only support RGB888 for now. */ +#define VIMC_DEBAYER_SOURCE_MBUS_FMT MEDIA_BUS_FMT_RGB888_1X24 + enum vimc_debayer_rgb_colors { VIMC_DEBAYER_RED = 0, VIMC_DEBAYER_GREEN = 1, @@ -29,19 +32,26 @@ struct vimc_debayer_pix_map { struct vimc_debayer_device { struct vimc_ent_device ved; struct v4l2_subdev sd; - /* The active format */ - struct v4l2_mbus_framefmt sink_fmt; - u32 src_code; + struct v4l2_ctrl_handler hdl; + struct media_pad pads[2]; + + u8 *src_frame; + void (*set_rgb_src)(struct vimc_debayer_device *vdebayer, unsigned int lin, unsigned int col, unsigned int rgb[3]); - /* Values calculated when the stream starts */ - u8 *src_frame; - const struct vimc_debayer_pix_map *sink_pix_map; - unsigned int sink_bpp; - unsigned int mean_win_size; - struct v4l2_ctrl_handler hdl; - struct media_pad pads[2]; + + /* + * Virtual "hardware" configuration, filled when the stream starts or + * when controls are set. + */ + struct { + const struct vimc_debayer_pix_map *sink_pix_map; + unsigned int sink_bpp; + struct v4l2_area size; + unsigned int mean_win_size; + u32 src_code; + } hw; }; static const struct v4l2_mbus_framefmt sink_fmt_default = { @@ -153,7 +163,6 @@ static bool vimc_debayer_src_code_is_valid(u32 code) static int vimc_debayer_init_state(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state) { - struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *mf; mf = v4l2_subdev_state_get_format(sd_state, 0); @@ -161,7 +170,7 @@ static int vimc_debayer_init_state(struct v4l2_subdev *sd, mf = v4l2_subdev_state_get_format(sd_state, 1); *mf = sink_fmt_default; - mf->code = vdebayer->src_code; + mf->code = VIMC_DEBAYER_SOURCE_MBUS_FMT; return 0; } @@ -210,24 +219,6 @@ static int vimc_debayer_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int vimc_debayer_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); - - /* Get the current sink format */ - fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? - *v4l2_subdev_state_get_format(sd_state, 0) : - vdebayer->sink_fmt; - - /* Set the right code for the source pad */ - if (VIMC_IS_SRC(fmt->pad)) - fmt->format.code = vdebayer->src_code; - - return 0; -} - static void vimc_debayer_adjust_sink_fmt(struct v4l2_mbus_framefmt *fmt) { const struct vimc_debayer_pix_map *vpix; @@ -253,52 +244,42 @@ static int vimc_debayer_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *fmt) { struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *sink_fmt; - u32 *src_code; - - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - /* Do not change the format while stream is on */ - if (vdebayer->src_frame) - return -EBUSY; + struct v4l2_mbus_framefmt *format; - sink_fmt = &vdebayer->sink_fmt; - src_code = &vdebayer->src_code; - } else { - sink_fmt = v4l2_subdev_state_get_format(sd_state, 0); - src_code = &v4l2_subdev_state_get_format(sd_state, 1)->code; - } + /* Do not change the format while stream is on. */ + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE && vdebayer->src_frame) + return -EBUSY; /* - * Do not change the format of the source pad, - * it is propagated from the sink + * Do not change the format of the source pad, it is propagated from + * the sink. */ - if (VIMC_IS_SRC(fmt->pad)) { - u32 code = fmt->format.code; + if (VIMC_IS_SRC(fmt->pad)) + return v4l2_subdev_get_fmt(sd, sd_state, fmt); - fmt->format = *sink_fmt; + /* Set the new format in the sink pad. */ + vimc_debayer_adjust_sink_fmt(&fmt->format); - if (vimc_debayer_src_code_is_valid(code)) - *src_code = code; + format = v4l2_subdev_state_get_format(sd_state, 0); - fmt->format.code = *src_code; - } else { - /* Set the new format in the sink pad */ - vimc_debayer_adjust_sink_fmt(&fmt->format); - - dev_dbg(vdebayer->ved.dev, "%s: sink format update: " - "old:%dx%d (0x%x, %d, %d, %d, %d) " - "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vdebayer->sd.name, - /* old */ - sink_fmt->width, sink_fmt->height, sink_fmt->code, - sink_fmt->colorspace, sink_fmt->quantization, - sink_fmt->xfer_func, sink_fmt->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); - - *sink_fmt = fmt->format; - } + dev_dbg(vdebayer->ved.dev, "%s: sink format update: " + "old:%dx%d (0x%x, %d, %d, %d, %d) " + "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vdebayer->sd.name, + /* old */ + format->width, format->height, format->code, + format->colorspace, format->quantization, + format->xfer_func, format->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); + + *format = fmt->format; + + /* Propagate the format to the source pad. */ + format = v4l2_subdev_state_get_format(sd_state, 1); + *format = fmt->format; + format->code = VIMC_DEBAYER_SOURCE_MBUS_FMT; return 0; } @@ -306,7 +287,7 @@ static int vimc_debayer_set_fmt(struct v4l2_subdev *sd, static const struct v4l2_subdev_pad_ops vimc_debayer_pad_ops = { .enum_mbus_code = vimc_debayer_enum_mbus_code, .enum_frame_size = vimc_debayer_enum_frame_size, - .get_fmt = vimc_debayer_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = vimc_debayer_set_fmt, }; @@ -318,8 +299,8 @@ static void vimc_debayer_process_rgb_frame(struct vimc_debayer_device *vdebayer, const struct vimc_pix_map *vpix; unsigned int i, index; - vpix = vimc_pix_map_by_code(vdebayer->src_code); - index = VIMC_FRAME_INDEX(lin, col, vdebayer->sink_fmt.width, 3); + vpix = vimc_pix_map_by_code(vdebayer->hw.src_code); + index = VIMC_FRAME_INDEX(lin, col, vdebayer->hw.size.width, 3); for (i = 0; i < 3; i++) { switch (vpix->pixelformat) { case V4L2_PIX_FMT_RGB24: @@ -337,24 +318,37 @@ static int vimc_debayer_s_stream(struct v4l2_subdev *sd, int enable) struct vimc_debayer_device *vdebayer = v4l2_get_subdevdata(sd); if (enable) { + const struct v4l2_mbus_framefmt *sink_fmt; + const struct v4l2_mbus_framefmt *src_fmt; + struct v4l2_subdev_state *state; const struct vimc_pix_map *vpix; unsigned int frame_size; if (vdebayer->src_frame) return 0; + state = v4l2_subdev_lock_and_get_active_state(sd); + sink_fmt = v4l2_subdev_state_get_format(state, 0); + src_fmt = v4l2_subdev_state_get_format(state, 1); + /* Calculate the frame size of the source pad */ - vpix = vimc_pix_map_by_code(vdebayer->src_code); - frame_size = vdebayer->sink_fmt.width * vdebayer->sink_fmt.height * - vpix->bpp; + vpix = vimc_pix_map_by_code(src_fmt->code); + frame_size = src_fmt->width * src_fmt->height * vpix->bpp; /* Save the bytes per pixel of the sink */ - vpix = vimc_pix_map_by_code(vdebayer->sink_fmt.code); - vdebayer->sink_bpp = vpix->bpp; + vpix = vimc_pix_map_by_code(sink_fmt->code); + vdebayer->hw.sink_bpp = vpix->bpp; /* Get the corresponding pixel map from the table */ - vdebayer->sink_pix_map = - vimc_debayer_pix_map_by_code(vdebayer->sink_fmt.code); + vdebayer->hw.sink_pix_map = + vimc_debayer_pix_map_by_code(sink_fmt->code); + + vdebayer->hw.size.width = sink_fmt->width; + vdebayer->hw.size.height = sink_fmt->height; + + vdebayer->hw.src_code = src_fmt->code; + + v4l2_subdev_unlock_state(state); /* * Allocate the frame buffer. Use vmalloc to be able to @@ -363,7 +357,6 @@ static int vimc_debayer_s_stream(struct v4l2_subdev *sd, int enable) vdebayer->src_frame = vmalloc(frame_size); if (!vdebayer->src_frame) return -ENOMEM; - } else { if (!vdebayer->src_frame) return 0; @@ -424,13 +417,13 @@ static void vimc_debayer_calc_rgb_sink(struct vimc_debayer_device *vdebayer, * the top left corner of the mean window (considering the current * pixel as the center) */ - seek = vdebayer->mean_win_size / 2; + seek = vdebayer->hw.mean_win_size / 2; /* Sum the values of the colors in the mean window */ dev_dbg(vdebayer->ved.dev, "deb: %s: --- Calc pixel %dx%d, window mean %d, seek %d ---\n", - vdebayer->sd.name, lin, col, vdebayer->sink_fmt.height, seek); + vdebayer->sd.name, lin, col, vdebayer->hw.size.height, seek); /* * Iterate through all the lines in the mean window, start @@ -439,7 +432,7 @@ static void vimc_debayer_calc_rgb_sink(struct vimc_debayer_device *vdebayer, * frame */ for (wlin = seek > lin ? 0 : lin - seek; - wlin < lin + seek + 1 && wlin < vdebayer->sink_fmt.height; + wlin < lin + seek + 1 && wlin < vdebayer->hw.size.height; wlin++) { /* @@ -449,17 +442,17 @@ static void vimc_debayer_calc_rgb_sink(struct vimc_debayer_device *vdebayer, * frame */ for (wcol = seek > col ? 0 : col - seek; - wcol < col + seek + 1 && wcol < vdebayer->sink_fmt.width; + wcol < col + seek + 1 && wcol < vdebayer->hw.size.width; wcol++) { enum vimc_debayer_rgb_colors color; unsigned int index; /* Check which color this pixel is */ - color = vdebayer->sink_pix_map->order[wlin % 2][wcol % 2]; + color = vdebayer->hw.sink_pix_map->order[wlin % 2][wcol % 2]; index = VIMC_FRAME_INDEX(wlin, wcol, - vdebayer->sink_fmt.width, - vdebayer->sink_bpp); + vdebayer->hw.size.width, + vdebayer->hw.sink_bpp); dev_dbg(vdebayer->ved.dev, "deb: %s: RGB CALC: frame index %d, win pos %dx%d, color %d\n", @@ -468,7 +461,7 @@ static void vimc_debayer_calc_rgb_sink(struct vimc_debayer_device *vdebayer, /* Get its value */ rgb[color] = rgb[color] + vimc_debayer_get_val(&frame[index], - vdebayer->sink_bpp); + vdebayer->hw.sink_bpp); /* Save how many values we already added */ n_rgb[color]++; @@ -506,8 +499,8 @@ static void *vimc_debayer_process_frame(struct vimc_ent_device *ved, if (!vdebayer->src_frame) return ERR_PTR(-EINVAL); - for (i = 0; i < vdebayer->sink_fmt.height; i++) - for (j = 0; j < vdebayer->sink_fmt.width; j++) { + for (i = 0; i < vdebayer->hw.size.height; i++) + for (j = 0; j < vdebayer->hw.size.width; j++) { vimc_debayer_calc_rgb_sink(vdebayer, sink_frame, i, j, rgb); vdebayer->set_rgb_src(vdebayer, i, j, rgb); } @@ -522,7 +515,7 @@ static int vimc_debayer_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case VIMC_CID_MEAN_WIN_SIZE: - vdebayer->mean_win_size = ctrl->val; + vdebayer->hw.mean_win_size = ctrl->val; break; default: return -EINVAL; @@ -599,17 +592,8 @@ static struct vimc_ent_device *vimc_debayer_add(struct vimc_device *vimc, vdebayer->ved.process_frame = vimc_debayer_process_frame; vdebayer->ved.dev = vimc->mdev.dev; - vdebayer->mean_win_size = vimc_debayer_ctrl_mean_win_size.def; + vdebayer->hw.mean_win_size = vimc_debayer_ctrl_mean_win_size.def; - /* Initialize the frame format */ - vdebayer->sink_fmt = sink_fmt_default; - /* - * TODO: Add support for more output formats, we only support - * RGB888 for now - * NOTE: the src format is always the same as the sink, except - * for the code - */ - vdebayer->src_code = MEDIA_BUS_FMT_RGB888_1X24; vdebayer->set_rgb_src = vimc_debayer_process_rgb_frame; return &vdebayer->ved; -- cgit v1.2.3 From 7603ac5a8a1cce6513fa5956b089843b1965689c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 25 Apr 2024 02:57:41 +0300 Subject: media: vimc: scaler: Use subdev active state Store the active formats and crop rectangle in the subdevice active state. This simplifies implementation of the format and selection accessors, and allows using the v4l2_subdev_get_fmt() helper to implement the .get_fmt() operation. The active configuration that is used in the .process_frame() handler is still stored in the vimc_scaler_device structure. The driver could instead access the active state in the .process_frame() handler, but the required locking could interfere with the real time constraints of the frame processing. This data would be stored in registers in the .s_stream() handler for real hardware, storing it in dedicated storage thus mimics a real driver. To differentiate them from the rest of the device private data, move the corresponding fields to a sub-structure of vimc_scaler_device named hw. Signed-off-by: Laurent Pinchart Signed-off-by: Shuah Khan Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vimc/vimc-scaler.c | 124 ++++++++++---------------- 1 file changed, 49 insertions(+), 75 deletions(-) diff --git a/drivers/media/test-drivers/vimc/vimc-scaler.c b/drivers/media/test-drivers/vimc/vimc-scaler.c index f8639f5b4d0c..47d0d63865a0 100644 --- a/drivers/media/test-drivers/vimc/vimc-scaler.c +++ b/drivers/media/test-drivers/vimc/vimc-scaler.c @@ -26,13 +26,20 @@ enum vimc_scaler_pad { struct vimc_scaler_device { struct vimc_ent_device ved; struct v4l2_subdev sd; - struct v4l2_rect crop_rect; - /* Frame format for both sink and src pad */ - struct v4l2_mbus_framefmt fmt[2]; - /* Values calculated when the stream starts */ - u8 *src_frame; - unsigned int bpp; struct media_pad pads[2]; + + u8 *src_frame; + + /* + * Virtual "hardware" configuration, filled when the stream starts or + * when controls are set. + */ + struct { + struct v4l2_mbus_framefmt sink_fmt; + struct v4l2_mbus_framefmt src_fmt; + struct v4l2_rect sink_crop; + unsigned int bpp; + } hw; }; static const struct v4l2_mbus_framefmt fmt_default = { @@ -132,39 +139,6 @@ static int vimc_scaler_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static struct v4l2_mbus_framefmt * -vimc_scaler_pad_format(struct vimc_scaler_device *vscaler, - struct v4l2_subdev_state *sd_state, u32 pad, - enum v4l2_subdev_format_whence which) -{ - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_state_get_format(sd_state, pad); - else - return &vscaler->fmt[pad]; -} - -static struct v4l2_rect * -vimc_scaler_pad_crop(struct vimc_scaler_device *vscaler, - struct v4l2_subdev_state *sd_state, - enum v4l2_subdev_format_whence which) -{ - if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK); - else - return &vscaler->crop_rect; -} - -static int vimc_scaler_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd); - - format->format = *vimc_scaler_pad_format(vscaler, sd_state, format->pad, - format->which); - return 0; -} - static int vimc_scaler_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) @@ -176,7 +150,7 @@ static int vimc_scaler_set_fmt(struct v4l2_subdev *sd, if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && vscaler->src_frame) return -EBUSY; - fmt = vimc_scaler_pad_format(vscaler, sd_state, format->pad, format->which); + fmt = v4l2_subdev_state_get_format(sd_state, format->pad); /* * The media bus code and colorspace can only be changed on the sink @@ -214,14 +188,13 @@ static int vimc_scaler_set_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *src_fmt; struct v4l2_rect *crop; - crop = vimc_scaler_pad_crop(vscaler, sd_state, format->which); + crop = v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK); crop->width = fmt->width; crop->height = fmt->height; crop->top = 0; crop->left = 0; - src_fmt = vimc_scaler_pad_format(vscaler, sd_state, VIMC_SCALER_SRC, - format->which); + src_fmt = v4l2_subdev_state_get_format(sd_state, VIMC_SCALER_SRC); *src_fmt = *fmt; } @@ -234,7 +207,6 @@ static int vimc_scaler_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *sink_fmt; if (VIMC_IS_SRC(sel->pad)) @@ -242,11 +214,10 @@ static int vimc_scaler_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *vimc_scaler_pad_crop(vscaler, sd_state, sel->which); + sel->r = *v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK); break; case V4L2_SEL_TGT_CROP_BOUNDS: - sink_fmt = vimc_scaler_pad_format(vscaler, sd_state, VIMC_SCALER_SINK, - sel->which); + sink_fmt = v4l2_subdev_state_get_format(sd_state, VIMC_SCALER_SINK); sel->r = vimc_scaler_get_crop_bound_sink(sink_fmt); break; default: @@ -282,9 +253,8 @@ static int vimc_scaler_set_selection(struct v4l2_subdev *sd, if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE && vscaler->src_frame) return -EBUSY; - crop_rect = vimc_scaler_pad_crop(vscaler, sd_state, sel->which); - sink_fmt = vimc_scaler_pad_format(vscaler, sd_state, VIMC_SCALER_SINK, - sel->which); + crop_rect = v4l2_subdev_state_get_crop(sd_state, VIMC_SCALER_SINK); + sink_fmt = v4l2_subdev_state_get_format(sd_state, VIMC_SCALER_SINK); vimc_scaler_adjust_sink_crop(&sel->r, sink_fmt); *crop_rect = sel->r; @@ -294,7 +264,7 @@ static int vimc_scaler_set_selection(struct v4l2_subdev *sd, static const struct v4l2_subdev_pad_ops vimc_scaler_pad_ops = { .enum_mbus_code = vimc_scaler_enum_mbus_code, .enum_frame_size = vimc_scaler_enum_frame_size, - .get_fmt = vimc_scaler_get_fmt, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = vimc_scaler_set_fmt, .get_selection = vimc_scaler_get_selection, .set_selection = vimc_scaler_set_selection, @@ -305,27 +275,38 @@ static int vimc_scaler_s_stream(struct v4l2_subdev *sd, int enable) struct vimc_scaler_device *vscaler = v4l2_get_subdevdata(sd); if (enable) { - const struct vimc_pix_map *vpix; + struct v4l2_subdev_state *state; + const struct v4l2_mbus_framefmt *format; + const struct v4l2_rect *rect; unsigned int frame_size; if (vscaler->src_frame) return 0; - /* Save the bytes per pixel of the sink */ - vpix = vimc_pix_map_by_code(vscaler->fmt[VIMC_SCALER_SINK].code); - vscaler->bpp = vpix->bpp; + state = v4l2_subdev_lock_and_get_active_state(sd); + + /* Save the bytes per pixel of the sink. */ + format = v4l2_subdev_state_get_format(state, VIMC_SCALER_SINK); + vscaler->hw.sink_fmt = *format; + vscaler->hw.bpp = vimc_pix_map_by_code(format->code)->bpp; + + /* Calculate the frame size of the source pad. */ + format = v4l2_subdev_state_get_format(state, VIMC_SCALER_SRC); + vscaler->hw.src_fmt = *format; + frame_size = format->width * format->height * vscaler->hw.bpp; + + rect = v4l2_subdev_state_get_crop(state, VIMC_SCALER_SINK); + vscaler->hw.sink_crop = *rect; - /* Calculate the frame size of the source pad */ - frame_size = vscaler->fmt[VIMC_SCALER_SRC].width - * vscaler->fmt[VIMC_SCALER_SRC].height * vscaler->bpp; + v4l2_subdev_unlock_state(state); - /* Allocate the frame buffer. Use vmalloc to be able to - * allocate a large amount of memory + /* + * Allocate the frame buffer. Use vmalloc to be able to allocate + * a large amount of memory. */ vscaler->src_frame = vmalloc(frame_size); if (!vscaler->src_frame) return -ENOMEM; - } else { if (!vscaler->src_frame) return 0; @@ -353,9 +334,9 @@ static const struct v4l2_subdev_internal_ops vimc_scaler_internal_ops = { static void vimc_scaler_fill_src_frame(const struct vimc_scaler_device *const vscaler, const u8 *const sink_frame) { - const struct v4l2_mbus_framefmt *src_fmt = &vscaler->fmt[VIMC_SCALER_SRC]; - const struct v4l2_rect *r = &vscaler->crop_rect; - unsigned int snk_width = vscaler->fmt[VIMC_SCALER_SINK].width; + const struct v4l2_mbus_framefmt *sink_fmt = &vscaler->hw.sink_fmt; + const struct v4l2_mbus_framefmt *src_fmt = &vscaler->hw.src_fmt; + const struct v4l2_rect *r = &vscaler->hw.sink_crop; unsigned int src_x, src_y; u8 *walker = vscaler->src_frame; @@ -364,16 +345,16 @@ static void vimc_scaler_fill_src_frame(const struct vimc_scaler_device *const vs unsigned int snk_y, y_offset; snk_y = (src_y * r->height) / src_fmt->height + r->top; - y_offset = snk_y * snk_width * vscaler->bpp; + y_offset = snk_y * sink_fmt->width * vscaler->hw.bpp; for (src_x = 0; src_x < src_fmt->width; src_x++) { unsigned int snk_x, x_offset, index; snk_x = (src_x * r->width) / src_fmt->width + r->left; - x_offset = snk_x * vscaler->bpp; + x_offset = snk_x * vscaler->hw.bpp; index = y_offset + x_offset; - memcpy(walker, &sink_frame[index], vscaler->bpp); - walker += vscaler->bpp; + memcpy(walker, &sink_frame[index], vscaler->hw.bpp); + walker += vscaler->hw.bpp; } } } @@ -432,13 +413,6 @@ static struct vimc_ent_device *vimc_scaler_add(struct vimc_device *vimc, vscaler->ved.process_frame = vimc_scaler_process_frame; vscaler->ved.dev = vimc->mdev.dev; - /* Initialize the frame format */ - vscaler->fmt[VIMC_SCALER_SINK] = fmt_default; - vscaler->fmt[VIMC_SCALER_SRC] = fmt_default; - - /* Initialize the crop selection */ - vscaler->crop_rect = crop_rect_default; - return &vscaler->ved; } -- cgit v1.2.3 From 0fd7c0c2c156270dceb8c15fad3120cdce03e539 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 26 Jun 2024 12:59:13 +0200 Subject: media: vivid: fix wrong sizeimage value for mplane In several places a division by fmt->vdownsampling[p] was missing in the sizeimage[p] calculation, causing incorrect behavior for multiplanar formats were some planes are smaller than the first plane. Found by new v4l2-compliance tests. Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vivid/vivid-vid-cap.c | 5 +++-- drivers/media/test-drivers/vivid/vivid-vid-out.c | 16 +++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index 2804975fe278..3a3041a0378f 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -106,8 +106,9 @@ static int vid_cap_queue_setup(struct vb2_queue *vq, if (*nplanes != buffers) return -EINVAL; for (p = 0; p < buffers; p++) { - if (sizes[p] < tpg_g_line_width(&dev->tpg, p) * h + - dev->fmt_cap->data_offset[p]) + if (sizes[p] < tpg_g_line_width(&dev->tpg, p) * h / + dev->fmt_cap->vdownsampling[p] + + dev->fmt_cap->data_offset[p]) return -EINVAL; } } else { diff --git a/drivers/media/test-drivers/vivid/vivid-vid-out.c b/drivers/media/test-drivers/vivid/vivid-vid-out.c index 1653b2988f7e..7a0f4c61ac80 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-out.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-out.c @@ -63,14 +63,16 @@ static int vid_out_queue_setup(struct vb2_queue *vq, if (sizes[0] < size) return -EINVAL; for (p = 1; p < planes; p++) { - if (sizes[p] < dev->bytesperline_out[p] * h + - vfmt->data_offset[p]) + if (sizes[p] < dev->bytesperline_out[p] * h / + vfmt->vdownsampling[p] + + vfmt->data_offset[p]) return -EINVAL; } } else { for (p = 0; p < planes; p++) - sizes[p] = p ? dev->bytesperline_out[p] * h + - vfmt->data_offset[p] : size; + sizes[p] = p ? dev->bytesperline_out[p] * h / + vfmt->vdownsampling[p] + + vfmt->data_offset[p] : size; } *nplanes = planes; @@ -124,7 +126,7 @@ static int vid_out_buf_prepare(struct vb2_buffer *vb) for (p = 0; p < planes; p++) { if (p) - size = dev->bytesperline_out[p] * h; + size = dev->bytesperline_out[p] * h / vfmt->vdownsampling[p]; size += vb->planes[p].data_offset; if (vb2_get_plane_payload(vb, p) < size) { @@ -331,8 +333,8 @@ int vivid_g_fmt_vid_out(struct file *file, void *priv, for (p = 0; p < mp->num_planes; p++) { mp->plane_fmt[p].bytesperline = dev->bytesperline_out[p]; mp->plane_fmt[p].sizeimage = - mp->plane_fmt[p].bytesperline * mp->height + - fmt->data_offset[p]; + mp->plane_fmt[p].bytesperline * mp->height / + fmt->vdownsampling[p] + fmt->data_offset[p]; } for (p = fmt->buffers; p < fmt->planes; p++) { unsigned stride = dev->bytesperline_out[p]; -- cgit v1.2.3 From 23558d802a8e950783b956c457c002a612c09c2c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 26 Jun 2024 13:00:49 +0200 Subject: media: vivid: fix CREATE_BUFS support in queue_setup() While queue_setup was correct for CREATE_BUFS support for video devices, for VBI, SDR and touch devices it was wrong. This was found after adding new v4l2-compliance tests. Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vivid/vivid-sdr-cap.c | 7 ++++++- drivers/media/test-drivers/vivid/vivid-touch-cap.c | 6 +++--- drivers/media/test-drivers/vivid/vivid-vbi-cap.c | 2 ++ drivers/media/test-drivers/vivid/vivid-vbi-out.c | 2 ++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/media/test-drivers/vivid/vivid-sdr-cap.c b/drivers/media/test-drivers/vivid/vivid-sdr-cap.c index a81f26b76988..38cda33dffb2 100644 --- a/drivers/media/test-drivers/vivid/vivid-sdr-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-sdr-cap.c @@ -219,8 +219,13 @@ static int sdr_cap_queue_setup(struct vb2_queue *vq, unsigned sizes[], struct device *alloc_devs[]) { /* 2 = max 16-bit sample returned */ - sizes[0] = SDR_CAP_SAMPLES_PER_BUF * 2; + u32 size = SDR_CAP_SAMPLES_PER_BUF * 2; + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + *nplanes = 1; + sizes[0] = size; return 0; } diff --git a/drivers/media/test-drivers/vivid/vivid-touch-cap.c b/drivers/media/test-drivers/vivid/vivid-touch-cap.c index 3888c21b4d0c..3600b084bca5 100644 --- a/drivers/media/test-drivers/vivid/vivid-touch-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-touch-cap.c @@ -17,13 +17,13 @@ static int touch_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int size = f->sizeimage; if (*nplanes) { - if (sizes[0] < size) + if (*nplanes != 1) return -EINVAL; - } else { - sizes[0] = size; + return sizes[0] < size ? -EINVAL : 0; } *nplanes = 1; + sizes[0] = size; return 0; } diff --git a/drivers/media/test-drivers/vivid/vivid-vbi-cap.c b/drivers/media/test-drivers/vivid/vivid-vbi-cap.c index 3840b3a664ac..95387d57eb93 100644 --- a/drivers/media/test-drivers/vivid/vivid-vbi-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vbi-cap.c @@ -132,6 +132,8 @@ static int vbi_cap_queue_setup(struct vb2_queue *vq, if (!vivid_is_sdtv_cap(dev)) return -EINVAL; + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; sizes[0] = size; *nplanes = 1; diff --git a/drivers/media/test-drivers/vivid/vivid-vbi-out.c b/drivers/media/test-drivers/vivid/vivid-vbi-out.c index 434a10676417..871a56d93425 100644 --- a/drivers/media/test-drivers/vivid/vivid-vbi-out.c +++ b/drivers/media/test-drivers/vivid/vivid-vbi-out.c @@ -28,6 +28,8 @@ static int vbi_out_queue_setup(struct vb2_queue *vq, if (!vivid_is_svid_out(dev)) return -EINVAL; + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; sizes[0] = size; *nplanes = 1; -- cgit v1.2.3 From dd6aa1e1de44e9bb4b0f5d96dbf3f84d1e3f35b1 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 26 Jun 2024 20:14:31 +0200 Subject: media: uapi: pixfmt-luma: Document MIPI CSI-2 packing The Y10P, Y12P and Y14P format variants are packed according to the RAW10, RAW12 and RAW14 formats as defined by the MIPI CSI-2 specification. Document it. Signed-off-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Reviewed-by: Naushir Patuck Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- Documentation/userspace-api/media/v4l/pixfmt-yuv-luma.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/userspace-api/media/v4l/pixfmt-yuv-luma.rst b/Documentation/userspace-api/media/v4l/pixfmt-yuv-luma.rst index b3c5779521d8..f02e6cf3516a 100644 --- a/Documentation/userspace-api/media/v4l/pixfmt-yuv-luma.rst +++ b/Documentation/userspace-api/media/v4l/pixfmt-yuv-luma.rst @@ -209,3 +209,7 @@ are often referred to as greyscale formats. For Y012 and Y12 formats, Y012 places its data in the 12 high bits, with padding zeros in the 4 low bits, in contrast to the Y12 format, which has its padding located in the most significant bits of the 16 bit word. + + The 'P' variations of the Y10, Y12 and Y14 formats are packed according to + the RAW10, RAW12 and RAW14 packing scheme as defined by the MIPI CSI-2 + specification. -- cgit v1.2.3 From d1741141d03f8f5535adaf5a2d5000da3be9fe90 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 26 Jun 2024 20:14:32 +0200 Subject: media: uapi: Add a pixel format for BGR48 and RGB48 Add BGR48 and RGB48 16-bit per component image formats. Signed-off-by: Jacopo Mondi Reviewed-by: Kieran Bingham Reviewed-by: Naushir Patuck Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .../userspace-api/media/v4l/pixfmt-rgb.rst | 54 ++++++++++++++++++++++ drivers/media/v4l2-core/v4l2-common.c | 2 + drivers/media/v4l2-core/v4l2-ioctl.c | 2 + include/uapi/linux/videodev2.h | 2 + 4 files changed, 60 insertions(+) diff --git a/Documentation/userspace-api/media/v4l/pixfmt-rgb.rst b/Documentation/userspace-api/media/v4l/pixfmt-rgb.rst index b71b80d634d6..5ed4d62df909 100644 --- a/Documentation/userspace-api/media/v4l/pixfmt-rgb.rst +++ b/Documentation/userspace-api/media/v4l/pixfmt-rgb.rst @@ -996,6 +996,60 @@ arranged in little endian order. \normalsize +16 Bits Per Component +===================== + +These formats store an RGB triplet in six bytes, with 16 bits per component +stored in memory in little endian byte order. They are named based on the order +of the RGB components as stored in memory. For instance, RGB48 stores R\ +:sub:`7:0` and R\ :sub:`15:8` in bytes 0 and 1 respectively. This differs from +the DRM format nomenclature that instead uses the order of components as seen in +the 48-bits little endian word. + +.. raw:: latex + + \small + +.. flat-table:: RGB Formats With 16 Bits Per Component + :header-rows: 1 + + * - Identifier + - Code + - Byte 0 + - Byte 1 + - Byte 2 + - Byte 3 + - Byte 4 + - Byte 5 + + * .. _V4L2-PIX-FMT-BGR48: + + - ``V4L2_PIX_FMT_BGR48`` + - 'BGR6' + + - B\ :sub:`7-0` + - B\ :sub:`15-8` + - G\ :sub:`7-0` + - G\ :sub:`15-8` + - R\ :sub:`7-0` + - R\ :sub:`15-8` + + * .. _V4L2-PIX-FMT-RGB48: + + - ``V4L2_PIX_FMT_RGB48`` + - 'RGB6' + + - R\ :sub:`7-0` + - R\ :sub:`15-8` + - G\ :sub:`7-0` + - G\ :sub:`15-8` + - B\ :sub:`7-0` + - B\ :sub:`15-8` + +.. raw:: latex + + \normalsize + Deprecated RGB Formats ====================== diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 4165c815faef..0a2f4f0d0a07 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -253,6 +253,8 @@ const struct v4l2_format_info *v4l2_format_info(u32 format) { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_BGR666, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_BGR48_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_BGR48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, + { .format = V4L2_PIX_FMT_RGB48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, { .format = V4L2_PIX_FMT_RGBX1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 }, diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 4c76d17b4629..ee97643ff3a7 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1307,6 +1307,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_RGBX1010102: descr = "32-bit RGBX 10-10-10-2"; break; case V4L2_PIX_FMT_RGBA1010102: descr = "32-bit RGBA 10-10-10-2"; break; case V4L2_PIX_FMT_ARGB2101010: descr = "32-bit ARGB 2-10-10-10"; break; + case V4L2_PIX_FMT_BGR48: descr = "48-bit BGR 16-16-16"; break; + case V4L2_PIX_FMT_RGB48: descr = "48-bit RGB 16-16-16"; break; case V4L2_PIX_FMT_BGR48_12: descr = "12-bit Depth BGR"; break; case V4L2_PIX_FMT_ABGR64_12: descr = "12-bit Depth BGRA"; break; case V4L2_PIX_FMT_GREY: descr = "8-bit Greyscale"; break; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index fe6b67e83751..dd6876380fe3 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -582,6 +582,8 @@ struct v4l2_pix_format { /* RGB formats (6 or 8 bytes per pixel) */ #define V4L2_PIX_FMT_BGR48_12 v4l2_fourcc('B', '3', '1', '2') /* 48 BGR 12-bit per component */ +#define V4L2_PIX_FMT_BGR48 v4l2_fourcc('B', 'G', 'R', '6') /* 48 BGR 16-bit per component */ +#define V4L2_PIX_FMT_RGB48 v4l2_fourcc('R', 'G', 'B', '6') /* 48 RGB 16-bit per component */ #define V4L2_PIX_FMT_ABGR64_12 v4l2_fourcc('B', '4', '1', '2') /* 64 BGRA 12-bit per component */ /* Grey formats */ -- cgit v1.2.3 From c6c49bac8770ef95518faf20083e8b3e6150f45f Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 26 Jun 2024 20:14:33 +0200 Subject: media: uapi: Add Raspberry Pi PiSP Back End uAPI Add the Raspberry Pi PiSP Back End uAPI header. The header defines the data type used to configure the PiSP Back End ISP. The detailed description of the types and of the ISP configuration procedure is available at https://datasheets.raspberrypi.com/camera/raspberry-pi-image-signal-processor-specification.pdf Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- MAINTAINERS | 7 + .../uapi/linux/media/raspberrypi/pisp_be_config.h | 927 +++++++++++++++++++++ include/uapi/linux/media/raspberrypi/pisp_common.h | 199 +++++ 3 files changed, 1133 insertions(+) create mode 100644 include/uapi/linux/media/raspberrypi/pisp_be_config.h create mode 100644 include/uapi/linux/media/raspberrypi/pisp_common.h diff --git a/MAINTAINERS b/MAINTAINERS index ad1ecb2c4aed..861ee24cc1f7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18805,6 +18805,13 @@ L: linux-edac@vger.kernel.org S: Maintained F: drivers/ras/amd/fmpm.c +RASPBERRY PI PISP BACK END +M: Jacopo Mondi +L: Raspberry Pi Kernel Maintenance +L: linux-media@vger.kernel.org +S: Maintained +F: include/uapi/linux/media/raspberrypi/ + RC-CORE / LIRC FRAMEWORK M: Sean Young L: linux-media@vger.kernel.org diff --git a/include/uapi/linux/media/raspberrypi/pisp_be_config.h b/include/uapi/linux/media/raspberrypi/pisp_be_config.h new file mode 100644 index 000000000000..1684ae068d4f --- /dev/null +++ b/include/uapi/linux/media/raspberrypi/pisp_be_config.h @@ -0,0 +1,927 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * PiSP Back End configuration definitions. + * + * Copyright (C) 2021 - Raspberry Pi Ltd + * + */ +#ifndef _UAPI_PISP_BE_CONFIG_H_ +#define _UAPI_PISP_BE_CONFIG_H_ + +#include + +#include "pisp_common.h" + +/* byte alignment for inputs */ +#define PISP_BACK_END_INPUT_ALIGN 4u +/* alignment for compressed inputs */ +#define PISP_BACK_END_COMPRESSED_ALIGN 8u +/* minimum required byte alignment for outputs */ +#define PISP_BACK_END_OUTPUT_MIN_ALIGN 16u +/* preferred byte alignment for outputs */ +#define PISP_BACK_END_OUTPUT_MAX_ALIGN 64u + +/* minimum allowed tile width anywhere in the pipeline */ +#define PISP_BACK_END_MIN_TILE_WIDTH 16u +/* minimum allowed tile width anywhere in the pipeline */ +#define PISP_BACK_END_MIN_TILE_HEIGHT 16u + +#define PISP_BACK_END_NUM_OUTPUTS 2 +#define PISP_BACK_END_HOG_OUTPUT 1 + +#define PISP_BACK_END_NUM_TILES 64 + +enum pisp_be_bayer_enable { + PISP_BE_BAYER_ENABLE_INPUT = 0x000001, + PISP_BE_BAYER_ENABLE_DECOMPRESS = 0x000002, + PISP_BE_BAYER_ENABLE_DPC = 0x000004, + PISP_BE_BAYER_ENABLE_GEQ = 0x000008, + PISP_BE_BAYER_ENABLE_TDN_INPUT = 0x000010, + PISP_BE_BAYER_ENABLE_TDN_DECOMPRESS = 0x000020, + PISP_BE_BAYER_ENABLE_TDN = 0x000040, + PISP_BE_BAYER_ENABLE_TDN_COMPRESS = 0x000080, + PISP_BE_BAYER_ENABLE_TDN_OUTPUT = 0x000100, + PISP_BE_BAYER_ENABLE_SDN = 0x000200, + PISP_BE_BAYER_ENABLE_BLC = 0x000400, + PISP_BE_BAYER_ENABLE_STITCH_INPUT = 0x000800, + PISP_BE_BAYER_ENABLE_STITCH_DECOMPRESS = 0x001000, + PISP_BE_BAYER_ENABLE_STITCH = 0x002000, + PISP_BE_BAYER_ENABLE_STITCH_COMPRESS = 0x004000, + PISP_BE_BAYER_ENABLE_STITCH_OUTPUT = 0x008000, + PISP_BE_BAYER_ENABLE_WBG = 0x010000, + PISP_BE_BAYER_ENABLE_CDN = 0x020000, + PISP_BE_BAYER_ENABLE_LSC = 0x040000, + PISP_BE_BAYER_ENABLE_TONEMAP = 0x080000, + PISP_BE_BAYER_ENABLE_CAC = 0x100000, + PISP_BE_BAYER_ENABLE_DEBIN = 0x200000, + PISP_BE_BAYER_ENABLE_DEMOSAIC = 0x400000, +}; + +enum pisp_be_rgb_enable { + PISP_BE_RGB_ENABLE_INPUT = 0x000001, + PISP_BE_RGB_ENABLE_CCM = 0x000002, + PISP_BE_RGB_ENABLE_SAT_CONTROL = 0x000004, + PISP_BE_RGB_ENABLE_YCBCR = 0x000008, + PISP_BE_RGB_ENABLE_FALSE_COLOUR = 0x000010, + PISP_BE_RGB_ENABLE_SHARPEN = 0x000020, + /* Preferred colours would occupy 0x000040 */ + PISP_BE_RGB_ENABLE_YCBCR_INVERSE = 0x000080, + PISP_BE_RGB_ENABLE_GAMMA = 0x000100, + PISP_BE_RGB_ENABLE_CSC0 = 0x000200, + PISP_BE_RGB_ENABLE_CSC1 = 0x000400, + PISP_BE_RGB_ENABLE_DOWNSCALE0 = 0x001000, + PISP_BE_RGB_ENABLE_DOWNSCALE1 = 0x002000, + PISP_BE_RGB_ENABLE_RESAMPLE0 = 0x008000, + PISP_BE_RGB_ENABLE_RESAMPLE1 = 0x010000, + PISP_BE_RGB_ENABLE_OUTPUT0 = 0x040000, + PISP_BE_RGB_ENABLE_OUTPUT1 = 0x080000, + PISP_BE_RGB_ENABLE_HOG = 0x200000 +}; + +#define PISP_BE_RGB_ENABLE_CSC(i) (PISP_BE_RGB_ENABLE_CSC0 << (i)) +#define PISP_BE_RGB_ENABLE_DOWNSCALE(i) (PISP_BE_RGB_ENABLE_DOWNSCALE0 << (i)) +#define PISP_BE_RGB_ENABLE_RESAMPLE(i) (PISP_BE_RGB_ENABLE_RESAMPLE0 << (i)) +#define PISP_BE_RGB_ENABLE_OUTPUT(i) (PISP_BE_RGB_ENABLE_OUTPUT0 << (i)) + +/* + * We use the enable flags to show when blocks are "dirty", but we need some + * extra ones too. + */ +enum pisp_be_dirty { + PISP_BE_DIRTY_GLOBAL = 0x0001, + PISP_BE_DIRTY_SH_FC_COMBINE = 0x0002, + PISP_BE_DIRTY_CROP = 0x0004 +}; + +/** + * struct pisp_be_global_config - PiSP global enable bitmaps + * @bayer_enables: Bayer input enable flags + * @rgb_enables: RGB output enable flags + * @bayer_order: Bayer input format ordering + * @pad: Padding bytes + */ +struct pisp_be_global_config { + __u32 bayer_enables; + __u32 rgb_enables; + __u8 bayer_order; + __u8 pad[3]; +} __attribute__((packed)); + +/** + * struct pisp_be_input_buffer_config - PiSP Back End input buffer + * @addr: Input buffer address + */ +struct pisp_be_input_buffer_config { + /* low 32 bits followed by high 32 bits (for each of up to 3 planes) */ + __u32 addr[3][2]; +} __attribute__((packed)); + +/** + * struct pisp_be_dpc_config - PiSP Back End DPC config + * + * Defective Pixel Correction configuration + * + * @coeff_level: Coefficient for the darkest neighbouring pixel value + * @coeff_range: Coefficient for the range of pixels for this Bayer channel + * @pad: Padding byte + * @flags: DPC configuration flags + */ +struct pisp_be_dpc_config { + __u8 coeff_level; + __u8 coeff_range; + __u8 pad; +#define PISP_BE_DPC_FLAG_FOLDBACK 1 + __u8 flags; +} __attribute__((packed)); + +/** + * struct pisp_be_geq_config - PiSP Back End GEQ config + * + * Green Equalisation configuration + * + * @offset: Offset value for threshold calculation + * @slope_sharper: Slope/Sharper configuration + * @min: Minimum value the threshold may have + * @max: Maximum value the threshold may have + */ +struct pisp_be_geq_config { + __u16 offset; +#define PISP_BE_GEQ_SHARPER BIT(15) +#define PISP_BE_GEQ_SLOPE ((1 << 10) - 1) + /* top bit is the "sharper" flag, slope value is bottom 10 bits */ + __u16 slope_sharper; + __u16 min; + __u16 max; +} __attribute__((packed)); + +/** + * struct pisp_be_tdn_input_buffer_config - PiSP Back End TDN input buffer + * @addr: TDN input buffer address + */ +struct pisp_be_tdn_input_buffer_config { + /* low 32 bits followed by high 32 bits */ + __u32 addr[2]; +} __attribute__((packed)); + +/** + * struct pisp_be_tdn_config - PiSP Back End TDN config + * + * Temporal Denoise configuration + * + * @black_level: Black level value subtracted from pixels + * @ratio: Multiplier for the LTA input frame + * @noise_constant: Constant offset value used in noise estimation + * @noise_slope: Noise estimation multiplier + * @threshold: Threshold for TDN operations + * @reset: Disable TDN operations + * @pad: Padding byte + */ +struct pisp_be_tdn_config { + __u16 black_level; + __u16 ratio; + __u16 noise_constant; + __u16 noise_slope; + __u16 threshold; + __u8 reset; + __u8 pad; +} __attribute__((packed)); + +/** + * struct pisp_be_tdn_output_buffer_config - PiSP Back End TDN output buffer + * @addr: TDN output buffer address + */ +struct pisp_be_tdn_output_buffer_config { + /* low 32 bits followed by high 32 bits */ + __u32 addr[2]; +} __attribute__((packed)); + +/** + * struct pisp_be_sdn_config - PiSP Back End SDN config + * + * Spatial Denoise configuration + * + * @black_level: Black level subtracted from pixel for noise estimation + * @leakage: Proportion of the original undenoised value to mix in + * denoised output + * @pad: Padding byte + * @noise_constant: Noise constant used for noise estimation + * @noise_slope: Noise slope value used for noise estimation + * @noise_constant2: Second noise constant used for noise estimation + * @noise_slope2: Second slope value used for noise estimation + */ +struct pisp_be_sdn_config { + __u16 black_level; + __u8 leakage; + __u8 pad; + __u16 noise_constant; + __u16 noise_slope; + __u16 noise_constant2; + __u16 noise_slope2; +} __attribute__((packed)); + +/** + * struct pisp_be_stitch_input_buffer_config - PiSP Back End Stitch input + * @addr: Stitch input buffer address + */ +struct pisp_be_stitch_input_buffer_config { + /* low 32 bits followed by high 32 bits */ + __u32 addr[2]; +} __attribute__((packed)); + +#define PISP_BE_STITCH_STREAMING_LONG 0x8000 +#define PISP_BE_STITCH_EXPOSURE_RATIO_MASK 0x7fff + +/** + * struct pisp_be_stitch_config - PiSP Back End Stitch config + * + * Stitch block configuration + * + * @threshold_lo: Low threshold value + * @threshold_diff_power: Low and high threshold difference + * @pad: Padding bytes + * @exposure_ratio: Multiplier to convert long exposure pixels into + * short exposure pixels + * @motion_threshold_256: Motion threshold above which short exposure + * pixels are used + * @motion_threshold_recip: Reciprocal of motion_threshold_256 value + */ +struct pisp_be_stitch_config { + __u16 threshold_lo; + __u8 threshold_diff_power; + __u8 pad; + + /* top bit indicates whether streaming input is the long exposure */ + __u16 exposure_ratio; + + __u8 motion_threshold_256; + __u8 motion_threshold_recip; +} __attribute__((packed)); + +/** + * struct pisp_be_stitch_output_buffer_config - PiSP Back End Stitch output + * @addr: Stitch input buffer address + */ +struct pisp_be_stitch_output_buffer_config { + /* low 32 bits followed by high 32 bits */ + __u32 addr[2]; +} __attribute__((packed)); + +/** + * struct pisp_be_cdn_config - PiSP Back End CDN config + * + * Colour Denoise configuration + * + * @thresh: Constant for noise estimation + * @iir_strength: Relative strength of the IIR part of the filter + * @g_adjust: Proportion of the change assigned to the G channel + */ +struct pisp_be_cdn_config { + __u16 thresh; + __u8 iir_strength; + __u8 g_adjust; +} __attribute__((packed)); + +#define PISP_BE_LSC_LOG_GRID_SIZE 5 +#define PISP_BE_LSC_GRID_SIZE (1 << PISP_BE_LSC_LOG_GRID_SIZE) +#define PISP_BE_LSC_STEP_PRECISION 18 + +/** + * struct pisp_be_lsc_config - PiSP Back End LSC config + * + * Lens Shading Correction configuration + * + * @grid_step_x: Reciprocal of cell size width + * @grid_step_y: Reciprocal of cell size height + * @lut_packed: Jointly-coded RGB gains for each LSC grid + */ +struct pisp_be_lsc_config { + /* (1<<18) / grid_cell_width */ + __u16 grid_step_x; + /* (1<<18) / grid_cell_height */ + __u16 grid_step_y; + /* RGB gains jointly encoded in 32 bits */ +#define PISP_BE_LSC_LUT_SIZE (PISP_BE_LSC_GRID_SIZE + 1) + __u32 lut_packed[PISP_BE_LSC_LUT_SIZE][PISP_BE_LSC_LUT_SIZE]; +} __attribute__((packed)); + +/** + * struct pisp_be_lsc_extra - PiSP Back End LSC Extra config + * @offset_x: Horizontal offset into the LSC table of this tile + * @offset_y: Vertical offset into the LSC table of this tile + */ +struct pisp_be_lsc_extra { + __u16 offset_x; + __u16 offset_y; +} __attribute__((packed)); + +#define PISP_BE_CAC_LOG_GRID_SIZE 3 +#define PISP_BE_CAC_GRID_SIZE (1 << PISP_BE_CAC_LOG_GRID_SIZE) +#define PISP_BE_CAC_STEP_PRECISION 20 + +/** + * struct pisp_be_cac_config - PiSP Back End CAC config + * + * Chromatic Aberration Correction config + * + * @grid_step_x: Reciprocal of cell size width + * @grid_step_y: Reciprocal of cell size height + * @lut: Pixel shift for the CAC grid + */ +struct pisp_be_cac_config { + /* (1<<20) / grid_cell_width */ + __u16 grid_step_x; + /* (1<<20) / grid_cell_height */ + __u16 grid_step_y; + /* [gridy][gridx][rb][xy] */ +#define PISP_BE_CAC_LUT_SIZE (PISP_BE_CAC_GRID_SIZE + 1) + __s8 lut[PISP_BE_CAC_LUT_SIZE][PISP_BE_CAC_LUT_SIZE][2][2]; +} __attribute__((packed)); + +/** + * struct pisp_be_cac_extra - PiSP Back End CAC extra config + * @offset_x: Horizontal offset into the CAC table of this tile + * @offset_y: Horizontal offset into the CAC table of this tile + */ +struct pisp_be_cac_extra { + __u16 offset_x; + __u16 offset_y; +} __attribute__((packed)); + +#define PISP_BE_DEBIN_NUM_COEFFS 4 + +/** + * struct pisp_be_debin_config - PiSP Back End Debin config + * + * Debinning configuration + * + * @coeffs: Filter coefficients for debinning + * @h_enable: Horizontal debinning enable + * @v_enable: Vertical debinning enable + * @pad: Padding bytes + */ +struct pisp_be_debin_config { + __s8 coeffs[PISP_BE_DEBIN_NUM_COEFFS]; + __s8 h_enable; + __s8 v_enable; + __s8 pad[2]; +} __attribute__((packed)); + +#define PISP_BE_TONEMAP_LUT_SIZE 64 + +/** + * struct pisp_be_tonemap_config - PiSP Back End Tonemap config + * + * Tonemapping configuration + * + * @detail_constant: Constant value for threshold calculation + * @detail_slope: Slope value for threshold calculation + * @iir_strength: Relative strength of the IIR fiter + * @strength: Strength factor + * @lut: Look-up table for tonemap curve + */ +struct pisp_be_tonemap_config { + __u16 detail_constant; + __u16 detail_slope; + __u16 iir_strength; + __u16 strength; + __u32 lut[PISP_BE_TONEMAP_LUT_SIZE]; +} __attribute__((packed)); + +/** + * struct pisp_be_demosaic_config - PiSP Back End Demosaic config + * + * Demosaic configuration + * + * @sharper: Use other Bayer channels to increase sharpness + * @fc_mode: Built-in false colour suppression mode + * @pad: Padding bytes + */ +struct pisp_be_demosaic_config { + __u8 sharper; + __u8 fc_mode; + __u8 pad[2]; +} __attribute__((packed)); + +/** + * struct pisp_be_ccm_config - PiSP Back End CCM config + * + * Colour Correction Matrix configuration + * + * @coeffs: Matrix coefficients + * @pad: Padding bytes + * @offsets: Offsets triplet + */ +struct pisp_be_ccm_config { + __s16 coeffs[9]; + __u8 pad[2]; + __s32 offsets[3]; +} __attribute__((packed)); + +/** + * struct pisp_be_sat_control_config - PiSP Back End SAT config + * + * Saturation Control configuration + * + * @shift_r: Left shift for Red colour channel + * @shift_g: Left shift for Green colour channel + * @shift_b: Left shift for Blue colour channel + * @pad: Padding byte + */ +struct pisp_be_sat_control_config { + __u8 shift_r; + __u8 shift_g; + __u8 shift_b; + __u8 pad; +} __attribute__((packed)); + +/** + * struct pisp_be_false_colour_config - PiSP Back End False Colour config + * + * False Colour configuration + * + * @distance: Distance of neighbouring pixels, either 1 or 2 + * @pad: Padding bytes + */ +struct pisp_be_false_colour_config { + __u8 distance; + __u8 pad[3]; +} __attribute__((packed)); + +#define PISP_BE_SHARPEN_SIZE 5 +#define PISP_BE_SHARPEN_FUNC_NUM_POINTS 9 + +/** + * struct pisp_be_sharpen_config - PiSP Back End Sharpening config + * + * Sharpening configuration + * + * @kernel0: Coefficient for filter 0 + * @pad0: Padding byte + * @kernel1: Coefficient for filter 1 + * @pad1: Padding byte + * @kernel2: Coefficient for filter 2 + * @pad2: Padding byte + * @kernel3: Coefficient for filter 3 + * @pad3: Padding byte + * @kernel4: Coefficient for filter 4 + * @pad4: Padding byte + * @threshold_offset0: Offset for filter 0 response calculation + * @threshold_slope0: Slope multiplier for the filter 0 response calculation + * @scale0: Scale factor for filter 0 response calculation + * @pad5: Padding byte + * @threshold_offset1: Offset for filter 0 response calculation + * @threshold_slope1: Slope multiplier for the filter 0 response calculation + * @scale1: Scale factor for filter 0 response calculation + * @pad6: Padding byte + * @threshold_offset2: Offset for filter 0 response calculation + * @threshold_slope2: Slope multiplier for the filter 0 response calculation + * @scale2: Scale factor for filter 0 response calculation + * @pad7: Padding byte + * @threshold_offset3: Offset for filter 0 response calculation + * @threshold_slope3: Slope multiplier for the filter 0 response calculation + * @scale3: Scale factor for filter 0 response calculation + * @pad8: Padding byte + * @threshold_offset4: Offset for filter 0 response calculation + * @threshold_slope4: Slope multiplier for the filter 0 response calculation + * @scale4: Scale factor for filter 0 response calculation + * @pad9: Padding byte + * @positive_strength: Factor to scale the positive sharpening strength + * @positive_pre_limit: Maximum allowed possible positive sharpening value + * @positive_func: Gain factor applied to positive sharpening response + * @positive_limit: Final gain factor applied to positive sharpening + * @negative_strength: Factor to scale the negative sharpening strength + * @negative_pre_limit: Maximum allowed possible negative sharpening value + * @negative_func: Gain factor applied to negative sharpening response + * @negative_limit: Final gain factor applied to negative sharpening + * @enables: Filter enable mask + * @white: White output pixel filter mask + * @black: Black output pixel filter mask + * @grey: Grey output pixel filter mask + */ +struct pisp_be_sharpen_config { + __s8 kernel0[PISP_BE_SHARPEN_SIZE * PISP_BE_SHARPEN_SIZE]; + __s8 pad0[3]; + __s8 kernel1[PISP_BE_SHARPEN_SIZE * PISP_BE_SHARPEN_SIZE]; + __s8 pad1[3]; + __s8 kernel2[PISP_BE_SHARPEN_SIZE * PISP_BE_SHARPEN_SIZE]; + __s8 pad2[3]; + __s8 kernel3[PISP_BE_SHARPEN_SIZE * PISP_BE_SHARPEN_SIZE]; + __s8 pad3[3]; + __s8 kernel4[PISP_BE_SHARPEN_SIZE * PISP_BE_SHARPEN_SIZE]; + __s8 pad4[3]; + __u16 threshold_offset0; + __u16 threshold_slope0; + __u16 scale0; + __u16 pad5; + __u16 threshold_offset1; + __u16 threshold_slope1; + __u16 scale1; + __u16 pad6; + __u16 threshold_offset2; + __u16 threshold_slope2; + __u16 scale2; + __u16 pad7; + __u16 threshold_offset3; + __u16 threshold_slope3; + __u16 scale3; + __u16 pad8; + __u16 threshold_offset4; + __u16 threshold_slope4; + __u16 scale4; + __u16 pad9; + __u16 positive_strength; + __u16 positive_pre_limit; + __u16 positive_func[PISP_BE_SHARPEN_FUNC_NUM_POINTS]; + __u16 positive_limit; + __u16 negative_strength; + __u16 negative_pre_limit; + __u16 negative_func[PISP_BE_SHARPEN_FUNC_NUM_POINTS]; + __u16 negative_limit; + __u8 enables; + __u8 white; + __u8 black; + __u8 grey; +} __attribute__((packed)); + +/** + * struct pisp_be_sh_fc_combine_config - PiSP Back End Sharpening and + * False Colour config + * + * Sharpening and False Colour configuration + * + * @y_factor: Control amount of desaturation of pixels being darkened + * @c1_factor: Control amount of brightening of a pixel for the Cb + * channel + * @c2_factor: Control amount of brightening of a pixel for the Cr + * channel + * @pad: Padding byte + */ +struct pisp_be_sh_fc_combine_config { + __u8 y_factor; + __u8 c1_factor; + __u8 c2_factor; + __u8 pad; +} __attribute__((packed)); + +#define PISP_BE_GAMMA_LUT_SIZE 64 + +/** + * struct pisp_be_gamma_config - PiSP Back End Gamma configuration + * @lut: Gamma curve look-up table + */ +struct pisp_be_gamma_config { + __u32 lut[PISP_BE_GAMMA_LUT_SIZE]; +} __attribute__((packed)); + +/** + * struct pisp_be_crop_config - PiSP Back End Crop config + * + * Crop configuration + * + * @offset_x: Number of pixels cropped from the left of the tile + * @offset_y: Number of pixels cropped from the top of the tile + * @width: Width of the cropped tile output + * @height: Height of the cropped tile output + */ +struct pisp_be_crop_config { + __u16 offset_x, offset_y; + __u16 width, height; +} __attribute__((packed)); + +#define PISP_BE_RESAMPLE_FILTER_SIZE 96 + +/** + * struct pisp_be_resample_config - PiSP Back End Resampling config + * + * Resample configuration + * + * @scale_factor_h: Horizontal scale factor + * @scale_factor_v: Vertical scale factor + * @coef: Resample coefficients + */ +struct pisp_be_resample_config { + __u16 scale_factor_h, scale_factor_v; + __s16 coef[PISP_BE_RESAMPLE_FILTER_SIZE]; +} __attribute__((packed)); + +/** + * struct pisp_be_resample_extra - PiSP Back End Resample config + * + * Resample configuration + * + * @scaled_width: Width in pixels of the scaled output + * @scaled_height: Height in pixels of the scaled output + * @initial_phase_h: Initial horizontal phase + * @initial_phase_v: Initial vertical phase + */ +struct pisp_be_resample_extra { + __u16 scaled_width; + __u16 scaled_height; + __s16 initial_phase_h[3]; + __s16 initial_phase_v[3]; +} __attribute__((packed)); + +/** + * struct pisp_be_downscale_config - PiSP Back End Downscale config + * + * Downscale configuration + * + * @scale_factor_h: Horizontal scale factor + * @scale_factor_v: Vertical scale factor + * @scale_recip_h: Horizontal reciprocal factor + * @scale_recip_v: Vertical reciprocal factor + */ +struct pisp_be_downscale_config { + __u16 scale_factor_h; + __u16 scale_factor_v; + __u16 scale_recip_h; + __u16 scale_recip_v; +} __attribute__((packed)); + +/** + * struct pisp_be_downscale_extra - PiSP Back End Downscale Extra config + * @scaled_width: Scaled image width + * @scaled_height: Scaled image height + */ +struct pisp_be_downscale_extra { + __u16 scaled_width; + __u16 scaled_height; +} __attribute__((packed)); + +/** + * struct pisp_be_hog_config - PiSP Back End HOG config + * + * Histogram of Oriented Gradients configuration + * + * @compute_signed: Set 0 for unsigned gradients, 1 for signed + * @channel_mix: Channels proportions to use + * @stride: Stride in bytes between blocks directly below + */ +struct pisp_be_hog_config { + __u8 compute_signed; + __u8 channel_mix[3]; + __u32 stride; +} __attribute__((packed)); + +struct pisp_be_axi_config { + __u8 r_qos; /* Read QoS */ + __u8 r_cache_prot; /* Read { prot[2:0], cache[3:0] } */ + __u8 w_qos; /* Write QoS */ + __u8 w_cache_prot; /* Write { prot[2:0], cache[3:0] } */ +} __attribute__((packed)); + +/** + * enum pisp_be_transform - PiSP Back End Transform flags + * @PISP_BE_TRANSFORM_NONE: No transform + * @PISP_BE_TRANSFORM_HFLIP: Horizontal flip + * @PISP_BE_TRANSFORM_VFLIP: Vertical flip + * @PISP_BE_TRANSFORM_ROT180: 180 degress rotation + */ +enum pisp_be_transform { + PISP_BE_TRANSFORM_NONE = 0x0, + PISP_BE_TRANSFORM_HFLIP = 0x1, + PISP_BE_TRANSFORM_VFLIP = 0x2, + PISP_BE_TRANSFORM_ROT180 = + (PISP_BE_TRANSFORM_HFLIP | PISP_BE_TRANSFORM_VFLIP) +}; + +struct pisp_be_output_format_config { + struct pisp_image_format_config image; + __u8 transform; + __u8 pad[3]; + __u16 lo; + __u16 hi; + __u16 lo2; + __u16 hi2; +} __attribute__((packed)); + +/** + * struct pisp_be_output_buffer_config - PiSP Back End Output buffer + * @addr: Output buffer address + */ +struct pisp_be_output_buffer_config { + /* low 32 bits followed by high 32 bits (for each of 3 planes) */ + __u32 addr[3][2]; +} __attribute__((packed)); + +/** + * struct pisp_be_hog_buffer_config - PiSP Back End HOG buffer + * @addr: HOG buffer address + */ +struct pisp_be_hog_buffer_config { + /* low 32 bits followed by high 32 bits */ + __u32 addr[2]; +} __attribute__((packed)); + +/** + * struct pisp_be_config - RaspberryPi PiSP Back End Processing configuration + * + * @global: Global PiSP configuration + * @input_format: Input image format + * @decompress: Decompress configuration + * @dpc: Defective Pixel Correction configuration + * @geq: Green Equalisation configuration + * @tdn_input_format: Temporal Denoise input format + * @tdn_decompress: Temporal Denoise decompress configuration + * @tdn: Temporal Denoise configuration + * @tdn_compress: Temporal Denoise compress configuration + * @tdn_output_format: Temporal Denoise output format + * @sdn: Spatial Denoise configuration + * @blc: Black Level Correction configuration + * @stitch_compress: Stitch compress configuration + * @stitch_output_format: Stitch output format + * @stitch_input_format: Stitch input format + * @stitch_decompress: Stitch decompress configuration + * @stitch: Stitch configuration + * @lsc: Lens Shading Correction configuration + * @wbg: White Balance Gain configuration + * @cdn: Colour Denoise configuration + * @cac: Colour Aberration Correction configuration + * @debin: Debinning configuration + * @tonemap: Tonemapping configuration + * @demosaic: Demosaicing configuration + * @ccm: Colour Correction Matrix configuration + * @sat_control: Saturation Control configuration + * @ycbcr: YCbCr colour correction configuration + * @sharpen: Sharpening configuration + * @false_colour: False colour correction + * @sh_fc_combine: Sharpening and False Colour correction + * @ycbcr_inverse: Inverse YCbCr colour correction + * @gamma: Gamma curve configuration + * @csc: Color Space Conversion configuration + * @downscale: Downscale configuration + * @resample: Resampling configuration + * @output_format: Output format configuration + * @hog: HOG configuration + */ +struct pisp_be_config { + struct pisp_be_global_config global; + struct pisp_image_format_config input_format; + struct pisp_decompress_config decompress; + struct pisp_be_dpc_config dpc; + struct pisp_be_geq_config geq; + struct pisp_image_format_config tdn_input_format; + struct pisp_decompress_config tdn_decompress; + struct pisp_be_tdn_config tdn; + struct pisp_compress_config tdn_compress; + struct pisp_image_format_config tdn_output_format; + struct pisp_be_sdn_config sdn; + struct pisp_bla_config blc; + struct pisp_compress_config stitch_compress; + struct pisp_image_format_config stitch_output_format; + struct pisp_image_format_config stitch_input_format; + struct pisp_decompress_config stitch_decompress; + struct pisp_be_stitch_config stitch; + struct pisp_be_lsc_config lsc; + struct pisp_wbg_config wbg; + struct pisp_be_cdn_config cdn; + struct pisp_be_cac_config cac; + struct pisp_be_debin_config debin; + struct pisp_be_tonemap_config tonemap; + struct pisp_be_demosaic_config demosaic; + struct pisp_be_ccm_config ccm; + struct pisp_be_sat_control_config sat_control; + struct pisp_be_ccm_config ycbcr; + struct pisp_be_sharpen_config sharpen; + struct pisp_be_false_colour_config false_colour; + struct pisp_be_sh_fc_combine_config sh_fc_combine; + struct pisp_be_ccm_config ycbcr_inverse; + struct pisp_be_gamma_config gamma; + struct pisp_be_ccm_config csc[PISP_BACK_END_NUM_OUTPUTS]; + struct pisp_be_downscale_config downscale[PISP_BACK_END_NUM_OUTPUTS]; + struct pisp_be_resample_config resample[PISP_BACK_END_NUM_OUTPUTS]; + struct pisp_be_output_format_config + output_format[PISP_BACK_END_NUM_OUTPUTS]; + struct pisp_be_hog_config hog; +} __attribute__((packed)); + +/** + * enum pisp_tile_edge - PiSP Back End Tile position + * @PISP_LEFT_EDGE: Left edge tile + * @PISP_RIGHT_EDGE: Right edge tile + * @PISP_TOP_EDGE: Top edge tile + * @PISP_BOTTOM_EDGE: Bottom edge tile + */ +enum pisp_tile_edge { + PISP_LEFT_EDGE = (1 << 0), + PISP_RIGHT_EDGE = (1 << 1), + PISP_TOP_EDGE = (1 << 2), + PISP_BOTTOM_EDGE = (1 << 3) +}; + +/** + * struct pisp_tile - Raspberry Pi PiSP Back End tile configuration + * + * Tile parameters: each set of tile parameters is a 160-bytes block of data + * which contains the tile processing parameters. + * + * @edge: Edge tile flag + * @pad0: Padding bytes + * @input_addr_offset: Top-left pixel offset, in bytes + * @input_addr_offset2: Top-left pixel offset, in bytes for the second/ + * third image planes + * @input_offset_x: Horizontal offset in pixels of this tile in the + * input image + * @input_offset_y: Vertical offset in pixels of this tile in the + * input image + * @input_width: Width in pixels of this tile + * @input_height: Height in pixels of the this tile + * @tdn_input_addr_offset: TDN input image offset, in bytes + * @tdn_output_addr_offset: TDN output image offset, in bytes + * @stitch_input_addr_offset: Stitch input image offset, in bytes + * @stitch_output_addr_offset: Stitch output image offset, in bytes + * @lsc_grid_offset_x: Horizontal offset in the LSC table for this tile + * @lsc_grid_offset_y: Vertical offset in the LSC table for this tile + * @cac_grid_offset_x: Horizontal offset in the CAC table for this tile + * @cac_grid_offset_y: Horizontal offset in the CAC table for this tile + * @crop_x_start: Number of pixels cropped from the left of the + * tile + * @crop_x_end: Number of pixels cropped from the right of the + * tile + * @crop_y_start: Number of pixels cropped from the top of the + * tile + * @crop_y_end: Number of pixels cropped from the bottom of the + * tile + * @downscale_phase_x: Initial horizontal phase in pixels + * @downscale_phase_y: Initial vertical phase in pixels + * @resample_in_width: Width in pixels of the tile entering the + * Resample block + * @resample_in_height: Height in pixels of the tile entering the + * Resample block + * @resample_phase_x: Initial horizontal phase for the Resample block + * @resample_phase_y: Initial vertical phase for the Resample block + * @output_offset_x: Horizontal offset in pixels where the tile will + * be written into the output image + * @output_offset_y: Vertical offset in pixels where the tile will be + * written into the output image + * @output_width: Width in pixels in the output image of this tile + * @output_height: Height in pixels in the output image of this tile + * @output_addr_offset: Offset in bytes into the output buffer + * @output_addr_offset2: Offset in bytes into the output buffer for the + * second and third plane + * @output_hog_addr_offset: Offset in bytes into the HOG buffer where + * results of this tile are to be written + */ +struct pisp_tile { + __u8 edge; /* enum pisp_tile_edge */ + __u8 pad0[3]; + /* 4 bytes */ + __u32 input_addr_offset; + __u32 input_addr_offset2; + __u16 input_offset_x; + __u16 input_offset_y; + __u16 input_width; + __u16 input_height; + /* 20 bytes */ + __u32 tdn_input_addr_offset; + __u32 tdn_output_addr_offset; + __u32 stitch_input_addr_offset; + __u32 stitch_output_addr_offset; + /* 36 bytes */ + __u32 lsc_grid_offset_x; + __u32 lsc_grid_offset_y; + /* 44 bytes */ + __u32 cac_grid_offset_x; + __u32 cac_grid_offset_y; + /* 52 bytes */ + __u16 crop_x_start[PISP_BACK_END_NUM_OUTPUTS]; + __u16 crop_x_end[PISP_BACK_END_NUM_OUTPUTS]; + __u16 crop_y_start[PISP_BACK_END_NUM_OUTPUTS]; + __u16 crop_y_end[PISP_BACK_END_NUM_OUTPUTS]; + /* 68 bytes */ + /* Ordering is planes then branches */ + __u16 downscale_phase_x[3 * PISP_BACK_END_NUM_OUTPUTS]; + __u16 downscale_phase_y[3 * PISP_BACK_END_NUM_OUTPUTS]; + /* 92 bytes */ + __u16 resample_in_width[PISP_BACK_END_NUM_OUTPUTS]; + __u16 resample_in_height[PISP_BACK_END_NUM_OUTPUTS]; + /* 100 bytes */ + /* Ordering is planes then branches */ + __u16 resample_phase_x[3 * PISP_BACK_END_NUM_OUTPUTS]; + __u16 resample_phase_y[3 * PISP_BACK_END_NUM_OUTPUTS]; + /* 124 bytes */ + __u16 output_offset_x[PISP_BACK_END_NUM_OUTPUTS]; + __u16 output_offset_y[PISP_BACK_END_NUM_OUTPUTS]; + __u16 output_width[PISP_BACK_END_NUM_OUTPUTS]; + __u16 output_height[PISP_BACK_END_NUM_OUTPUTS]; + /* 140 bytes */ + __u32 output_addr_offset[PISP_BACK_END_NUM_OUTPUTS]; + __u32 output_addr_offset2[PISP_BACK_END_NUM_OUTPUTS]; + /* 156 bytes */ + __u32 output_hog_addr_offset; + /* 160 bytes */ +} __attribute__((packed)); + +/** + * struct pisp_be_tiles_config - Raspberry Pi PiSP Back End configuration + * @tiles: Tile descriptors + * @num_tiles: Number of tiles + * @config: PiSP Back End configuration + */ +struct pisp_be_tiles_config { + struct pisp_tile tiles[PISP_BACK_END_NUM_TILES]; + __u32 num_tiles; + struct pisp_be_config config; +} __attribute__((packed)); + +#endif /* _UAPI_PISP_BE_CONFIG_H_ */ diff --git a/include/uapi/linux/media/raspberrypi/pisp_common.h b/include/uapi/linux/media/raspberrypi/pisp_common.h new file mode 100644 index 000000000000..b2522e29c976 --- /dev/null +++ b/include/uapi/linux/media/raspberrypi/pisp_common.h @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * RP1 PiSP common definitions. + * + * Copyright (C) 2021 - Raspberry Pi Ltd. + * + */ +#ifndef _UAPI_PISP_COMMON_H_ +#define _UAPI_PISP_COMMON_H_ + +#include + +struct pisp_image_format_config { + /* size in pixels */ + __u16 width; + __u16 height; + /* must match struct pisp_image_format below */ + __u32 format; + __s32 stride; + /* some planar image formats will need a second stride */ + __s32 stride2; +} __attribute__((packed)); + +enum pisp_bayer_order { + /* + * Note how bayer_order&1 tells you if G is on the even pixels of the + * checkerboard or not, and bayer_order&2 tells you if R is on the even + * rows or is swapped with B. Note that if the top (of the 8) bits is + * set, this denotes a monochrome or greyscale image, and the lower bits + * should all be ignored. + */ + PISP_BAYER_ORDER_RGGB = 0, + PISP_BAYER_ORDER_GBRG = 1, + PISP_BAYER_ORDER_BGGR = 2, + PISP_BAYER_ORDER_GRBG = 3, + PISP_BAYER_ORDER_GREYSCALE = 128 +}; + +enum pisp_image_format { + /* + * Precise values are mostly tbd. Generally these will be portmanteau + * values comprising bit fields and flags. This format must be shared + * throughout the PiSP. + */ + PISP_IMAGE_FORMAT_BPS_8 = 0x00000000, + PISP_IMAGE_FORMAT_BPS_10 = 0x00000001, + PISP_IMAGE_FORMAT_BPS_12 = 0x00000002, + PISP_IMAGE_FORMAT_BPS_16 = 0x00000003, + PISP_IMAGE_FORMAT_BPS_MASK = 0x00000003, + + PISP_IMAGE_FORMAT_PLANARITY_INTERLEAVED = 0x00000000, + PISP_IMAGE_FORMAT_PLANARITY_SEMI_PLANAR = 0x00000010, + PISP_IMAGE_FORMAT_PLANARITY_PLANAR = 0x00000020, + PISP_IMAGE_FORMAT_PLANARITY_MASK = 0x00000030, + + PISP_IMAGE_FORMAT_SAMPLING_444 = 0x00000000, + PISP_IMAGE_FORMAT_SAMPLING_422 = 0x00000100, + PISP_IMAGE_FORMAT_SAMPLING_420 = 0x00000200, + PISP_IMAGE_FORMAT_SAMPLING_MASK = 0x00000300, + + PISP_IMAGE_FORMAT_ORDER_NORMAL = 0x00000000, + PISP_IMAGE_FORMAT_ORDER_SWAPPED = 0x00001000, + + PISP_IMAGE_FORMAT_SHIFT_0 = 0x00000000, + PISP_IMAGE_FORMAT_SHIFT_1 = 0x00010000, + PISP_IMAGE_FORMAT_SHIFT_2 = 0x00020000, + PISP_IMAGE_FORMAT_SHIFT_3 = 0x00030000, + PISP_IMAGE_FORMAT_SHIFT_4 = 0x00040000, + PISP_IMAGE_FORMAT_SHIFT_5 = 0x00050000, + PISP_IMAGE_FORMAT_SHIFT_6 = 0x00060000, + PISP_IMAGE_FORMAT_SHIFT_7 = 0x00070000, + PISP_IMAGE_FORMAT_SHIFT_8 = 0x00080000, + PISP_IMAGE_FORMAT_SHIFT_MASK = 0x000f0000, + + PISP_IMAGE_FORMAT_UNCOMPRESSED = 0x00000000, + PISP_IMAGE_FORMAT_COMPRESSION_MODE_1 = 0x01000000, + PISP_IMAGE_FORMAT_COMPRESSION_MODE_2 = 0x02000000, + PISP_IMAGE_FORMAT_COMPRESSION_MODE_3 = 0x03000000, + PISP_IMAGE_FORMAT_COMPRESSION_MASK = 0x03000000, + + PISP_IMAGE_FORMAT_HOG_SIGNED = 0x04000000, + PISP_IMAGE_FORMAT_HOG_UNSIGNED = 0x08000000, + PISP_IMAGE_FORMAT_INTEGRAL_IMAGE = 0x10000000, + PISP_IMAGE_FORMAT_WALLPAPER_ROLL = 0x20000000, + PISP_IMAGE_FORMAT_THREE_CHANNEL = 0x40000000, + + /* Lastly a few specific instantiations of the above. */ + PISP_IMAGE_FORMAT_SINGLE_16 = PISP_IMAGE_FORMAT_BPS_16, + PISP_IMAGE_FORMAT_THREE_16 = PISP_IMAGE_FORMAT_BPS_16 | + PISP_IMAGE_FORMAT_THREE_CHANNEL +}; + +#define PISP_IMAGE_FORMAT_bps_8(fmt) \ + (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_8) +#define PISP_IMAGE_FORMAT_bps_10(fmt) \ + (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_10) +#define PISP_IMAGE_FORMAT_bps_12(fmt) \ + (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_12) +#define PISP_IMAGE_FORMAT_bps_16(fmt) \ + (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_16) +#define PISP_IMAGE_FORMAT_bps(fmt) \ + (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) ? \ + 8 + (2 << (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) - 1)) : 8) +#define PISP_IMAGE_FORMAT_shift(fmt) \ + (((fmt) & PISP_IMAGE_FORMAT_SHIFT_MASK) / PISP_IMAGE_FORMAT_SHIFT_1) +#define PISP_IMAGE_FORMAT_three_channel(fmt) \ + ((fmt) & PISP_IMAGE_FORMAT_THREE_CHANNEL) +#define PISP_IMAGE_FORMAT_single_channel(fmt) \ + (!((fmt) & PISP_IMAGE_FORMAT_THREE_CHANNEL)) +#define PISP_IMAGE_FORMAT_compressed(fmt) \ + (((fmt) & PISP_IMAGE_FORMAT_COMPRESSION_MASK) != \ + PISP_IMAGE_FORMAT_UNCOMPRESSED) +#define PISP_IMAGE_FORMAT_sampling_444(fmt) \ + (((fmt) & PISP_IMAGE_FORMAT_SAMPLING_MASK) == \ + PISP_IMAGE_FORMAT_SAMPLING_444) +#define PISP_IMAGE_FORMAT_sampling_422(fmt) \ + (((fmt) & PISP_IMAGE_FORMAT_SAMPLING_MASK) == \ + PISP_IMAGE_FORMAT_SAMPLING_422) +#define PISP_IMAGE_FORMAT_sampling_420(fmt) \ + (((fmt) & PISP_IMAGE_FORMAT_SAMPLING_MASK) == \ + PISP_IMAGE_FORMAT_SAMPLING_420) +#define PISP_IMAGE_FORMAT_order_normal(fmt) \ + (!((fmt) & PISP_IMAGE_FORMAT_ORDER_SWAPPED)) +#define PISP_IMAGE_FORMAT_order_swapped(fmt) \ + ((fmt) & PISP_IMAGE_FORMAT_ORDER_SWAPPED) +#define PISP_IMAGE_FORMAT_interleaved(fmt) \ + (((fmt) & PISP_IMAGE_FORMAT_PLANARITY_MASK) == \ + PISP_IMAGE_FORMAT_PLANARITY_INTERLEAVED) +#define PISP_IMAGE_FORMAT_semiplanar(fmt) \ + (((fmt) & PISP_IMAGE_FORMAT_PLANARITY_MASK) == \ + PISP_IMAGE_FORMAT_PLANARITY_SEMI_PLANAR) +#define PISP_IMAGE_FORMAT_planar(fmt) \ + (((fmt) & PISP_IMAGE_FORMAT_PLANARITY_MASK) == \ + PISP_IMAGE_FORMAT_PLANARITY_PLANAR) +#define PISP_IMAGE_FORMAT_wallpaper(fmt) \ + ((fmt) & PISP_IMAGE_FORMAT_WALLPAPER_ROLL) +#define PISP_IMAGE_FORMAT_HOG(fmt) \ + ((fmt) & \ + (PISP_IMAGE_FORMAT_HOG_SIGNED | PISP_IMAGE_FORMAT_HOG_UNSIGNED)) + +#define PISP_WALLPAPER_WIDTH 128 /* in bytes */ + +struct pisp_bla_config { + __u16 black_level_r; + __u16 black_level_gr; + __u16 black_level_gb; + __u16 black_level_b; + __u16 output_black_level; + __u8 pad[2]; +} __attribute__((packed)); + +struct pisp_wbg_config { + __u16 gain_r; + __u16 gain_g; + __u16 gain_b; + __u8 pad[2]; +} __attribute__((packed)); + +struct pisp_compress_config { + /* value subtracted from incoming data */ + __u16 offset; + __u8 pad; + /* 1 => Companding; 2 => Delta (recommended); 3 => Combined (for HDR) */ + __u8 mode; +} __attribute__((packed)); + +struct pisp_decompress_config { + /* value added to reconstructed data */ + __u16 offset; + __u8 pad; + /* 1 => Companding; 2 => Delta (recommended); 3 => Combined (for HDR) */ + __u8 mode; +} __attribute__((packed)); + +enum pisp_axi_flags { + /* + * round down bursts to end at a 32-byte boundary, to align following + * bursts + */ + PISP_AXI_FLAG_ALIGN = 128, + /* for FE writer: force WSTRB high, to pad output to 16-byte boundary */ + PISP_AXI_FLAG_PAD = 64, + /* for FE writer: Use Output FIFO level to trigger "panic" */ + PISP_AXI_FLAG_PANIC = 32, +}; + +struct pisp_axi_config { + /* + * burst length minus one, which must be in the range 0:15; OR'd with + * flags + */ + __u8 maxlen_flags; + /* { prot[2:0], cache[3:0] } fields, echoed on AXI bus */ + __u8 cache_prot; + /* QoS field(s) (4x4 bits for FE writer; 4 bits for other masters) */ + __u16 qos; +} __attribute__((packed)); + +#endif /* _UAPI_PISP_COMMON_H_ */ -- cgit v1.2.3 From 8f6c2202222faffa0959929d8cd9090254a60831 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 26 Jun 2024 20:14:34 +0200 Subject: media: uapi: Add meta pixel format for PiSP BE config Add format description for the PiSP Back End configuration parameter buffer. Signed-off-by: Jacopo Mondi Reviewed-by: Naushir Patuck Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .../userspace-api/media/v4l/meta-formats.rst | 1 + .../userspace-api/media/v4l/metafmt-pisp-be.rst | 56 ++++++++++++++++++++++ drivers/media/v4l2-core/v4l2-ioctl.c | 1 + include/uapi/linux/videodev2.h | 3 ++ 4 files changed, 61 insertions(+) create mode 100644 Documentation/userspace-api/media/v4l/metafmt-pisp-be.rst diff --git a/Documentation/userspace-api/media/v4l/meta-formats.rst b/Documentation/userspace-api/media/v4l/meta-formats.rst index c23aac823d2c..c6e56b5888bc 100644 --- a/Documentation/userspace-api/media/v4l/meta-formats.rst +++ b/Documentation/userspace-api/media/v4l/meta-formats.rst @@ -15,6 +15,7 @@ These formats are used for the :ref:`metadata` interface only. metafmt-d4xx metafmt-generic metafmt-intel-ipu3 + metafmt-pisp-be metafmt-rkisp1 metafmt-uvc metafmt-vivid diff --git a/Documentation/userspace-api/media/v4l/metafmt-pisp-be.rst b/Documentation/userspace-api/media/v4l/metafmt-pisp-be.rst new file mode 100644 index 000000000000..3281fe366c86 --- /dev/null +++ b/Documentation/userspace-api/media/v4l/metafmt-pisp-be.rst @@ -0,0 +1,56 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. _v4l2-meta-fmt-rpi-be-cfg: + +************************ +V4L2_META_FMT_RPI_BE_CFG +************************ + +Raspberry Pi PiSP Back End configuration format +=============================================== + +The Raspberry Pi PiSP Back End memory-to-memory image signal processor is +configured by userspace by providing a buffer of configuration parameters +to the `pispbe-config` output video device node using the +:c:type:`v4l2_meta_format` interface. + +The PiSP Back End processes images in tiles, and its configuration requires +specifying two different sets of parameters by populating the members of +:c:type:`pisp_be_tiles_config` defined in the ``pisp_be_config.h`` header file. + +The `Raspberry Pi PiSP technical specification +`_ +provide detailed description of the ISP back end configuration and programming +model. + +Global configuration data +------------------------- + +The global configuration data describe how the pixels in a particular image are +to be processed and is therefore shared across all the tiles of the image. So +for example, LSC (Lens Shading Correction) or Denoise parameters would be common +across all tiles from the same frame. + +Global configuration data are passed to the ISP by populating the member of +:c:type:`pisp_be_config`. + +Tile parameters +--------------- + +As the ISP processes images in tiles, each set of tiles parameters describe how +a single tile in an image is going to be processed. A single set of tile +parameters consist of 160 bytes of data and to process a batch of tiles several +sets of tiles parameters are required. + +Tiles parameters are passed to the ISP by populating the member of +``pisp_tile`` and the ``num_tiles`` fields of :c:type:`pisp_be_tiles_config`. + +Raspberry Pi PiSP Back End uAPI data types +========================================== + +This section describes the data types exposed to userspace by the Raspberry Pi +PiSP Back End. The section is informative only, for a detailed description of +each field refer to the `Raspberry Pi PiSP technical specification +`_. + +.. kernel-doc:: include/uapi/linux/media/raspberrypi/pisp_be_config.h diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index ee97643ff3a7..7c684788997c 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1465,6 +1465,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_Y210: descr = "10-bit YUYV Packed"; break; case V4L2_PIX_FMT_Y212: descr = "12-bit YUYV Packed"; break; case V4L2_PIX_FMT_Y216: descr = "16-bit YUYV Packed"; break; + case V4L2_META_FMT_RPI_BE_CFG: descr = "RPi PiSP BE Config format"; break; case V4L2_META_FMT_GENERIC_8: descr = "8-bit Generic Metadata"; break; case V4L2_META_FMT_GENERIC_CSI2_10: descr = "8-bit Generic Meta, 10b CSI-2"; break; case V4L2_META_FMT_GENERIC_CSI2_12: descr = "8-bit Generic Meta, 12b CSI-2"; break; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index dd6876380fe3..96fc0456081e 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -843,6 +843,9 @@ struct v4l2_pix_format { #define V4L2_META_FMT_RK_ISP1_PARAMS v4l2_fourcc('R', 'K', '1', 'P') /* Rockchip ISP1 3A Parameters */ #define V4L2_META_FMT_RK_ISP1_STAT_3A v4l2_fourcc('R', 'K', '1', 'S') /* Rockchip ISP1 3A Statistics */ +/* Vendor specific - used for RaspberryPi PiSP */ +#define V4L2_META_FMT_RPI_BE_CFG v4l2_fourcc('R', 'P', 'B', 'C') /* PiSP BE configuration */ + #ifdef __KERNEL__ /* * Line-based metadata formats. Remember to update v4l_fill_fmtdesc() when -- cgit v1.2.3 From d260c1224786c870edbae09ef6ecf5f35361821e Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 26 Jun 2024 20:14:35 +0200 Subject: media: uapi: Add PiSP Compressed RAW Bayer formats Add Raspberry Pi compressed RAW Bayer formats. The compression algorithm description is provided by Nick Hollinghurst from Raspberry Pi. Signed-off-by: Jacopo Mondi Reviewed-by: Naushir Patuck Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .../userspace-api/media/v4l/pixfmt-bayer.rst | 1 + .../media/v4l/pixfmt-srggb8-pisp-comp.rst | 74 ++++++++++++++++++++++ drivers/media/v4l2-core/v4l2-ioctl.c | 10 +++ include/uapi/linux/videodev2.h | 12 ++++ 4 files changed, 97 insertions(+) create mode 100644 Documentation/userspace-api/media/v4l/pixfmt-srggb8-pisp-comp.rst diff --git a/Documentation/userspace-api/media/v4l/pixfmt-bayer.rst b/Documentation/userspace-api/media/v4l/pixfmt-bayer.rst index 2500413e5f43..ed3eb432967d 100644 --- a/Documentation/userspace-api/media/v4l/pixfmt-bayer.rst +++ b/Documentation/userspace-api/media/v4l/pixfmt-bayer.rst @@ -20,6 +20,7 @@ orders. See also `the Wikipedia article on Bayer filter :maxdepth: 1 pixfmt-srggb8 + pixfmt-srggb8-pisp-comp pixfmt-srggb10 pixfmt-srggb10p pixfmt-srggb10alaw8 diff --git a/Documentation/userspace-api/media/v4l/pixfmt-srggb8-pisp-comp.rst b/Documentation/userspace-api/media/v4l/pixfmt-srggb8-pisp-comp.rst new file mode 100644 index 000000000000..5a82a15559d6 --- /dev/null +++ b/Documentation/userspace-api/media/v4l/pixfmt-srggb8-pisp-comp.rst @@ -0,0 +1,74 @@ +.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later + +.. _v4l2-pix-fmt-pisp-comp1-rggb: +.. _v4l2-pix-fmt-pisp-comp1-grbg: +.. _v4l2-pix-fmt-pisp-comp1-gbrg: +.. _v4l2-pix-fmt-pisp-comp1-bggr: +.. _v4l2-pix-fmt-pisp-comp1-mono: +.. _v4l2-pix-fmt-pisp-comp2-rggb: +.. _v4l2-pix-fmt-pisp-comp2-grbg: +.. _v4l2-pix-fmt-pisp-comp2-gbrg: +.. _v4l2-pix-fmt-pisp-comp2-bggr: +.. _v4l2-pix-fmt-pisp-comp2-mono: + +************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** +V4L2_PIX_FMT_PISP_COMP1_RGGB ('PC1R'), V4L2_PIX_FMT_PISP_COMP1_GRBG ('PC1G'), V4L2_PIX_FMT_PISP_COMP1_GBRG ('PC1g'), V4L2_PIX_FMT_PISP_COMP1_BGGR ('PC1B), V4L2_PIX_FMT_PISP_COMP1_MONO ('PC1M'), V4L2_PIX_FMT_PISP_COMP2_RGGB ('PC2R'), V4L2_PIX_FMT_PISP_COMP2_GRBG ('PC2G'), V4L2_PIX_FMT_PISP_COMP2_GBRG ('PC2g'), V4L2_PIX_FMT_PISP_COMP2_BGGR ('PC2B), V4L2_PIX_FMT_PISP_COMP2_MONO ('PC2M') +************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** + +================================================ +Raspberry Pi PiSP compressed 8-bit Bayer formats +================================================ + +Description +=========== + +The Raspberry Pi ISP (PiSP) uses a family of three fixed-rate compressed Bayer +formats. A black-level offset may be subtracted to improve compression +efficiency; the nominal black level and amount of offset must be signalled out +of band. Each scanline is padded to a multiple of 8 pixels wide, and each block +of 8 horizontally-contiguous pixels is coded using 8 bytes. + +Mode 1 uses a quantization and delta-based coding scheme which preserves up to +12 significant bits. Mode 2 is a simple sqrt-like companding scheme with 6 PWL +chords, preserving up to 12 significant bits. Mode 3 combines both companding +(with 4 chords) and the delta scheme, preserving up to 14 significant bits. + +The remainder of this description applies to Modes 1 and 3. + +Each block of 8 pixels is separated into even and odd phases of 4 pixels, +coded independently by 32-bit words at successive locations in memory. +The two LS bits of each 32-bit word give its "quantization mode". + +In quantization mode 0, the lowest 321 quantization levels are multiples of +FSD/4096 and the remaining levels are successive multiples of FSD/2048. +Quantization modes 1 and 2 use linear quantization with step sizes of +FSD/1024 and FSD/512 respectively. Each of the four pixels is quantized +independently, with rounding to the nearest level. +In quantization mode 2 where the middle two samples have quantized values +(q1,q2) both in the range [384..511], they are coded using 9 bits for q1 +followed by 7 bits for (q2 & 127). Otherwise, for quantization modes +0, 1 and 2: a 9-bit field encodes MIN(q1,q2) which must be in the range +[0..511] and a 7-bit field encodes (q2-q1+64) which must be in [0..127]. + +Each of the outer samples (q0,q3) is encoded using a 7-bit field based +on its inner neighbour q1 or q2. In quantization mode 2 where the inner +sample has a quantized value in the range [448..511], the field value is +(q0-384). Otherwise for quantization modes 0, 1 and 2: The outer sample +is encoded as (q0-MAX(0,q1-64)). q3 is likewise coded based on q2. +Each of these values must be in the range [0..127]. All these fields +of 2, 9, 7, 7, 7 bits respectively are packed in little-endian order +to give a 32-bit word with LE byte order. + +Quantization mode 3 has a "7.5-bit" escape, used when none of the above +encodings will fit. Each pixel value is quantized to the nearest of 176 +levels, where the lowest 95 levels are multiples of FSD/256 and the +remaining levels are multiples of FSD/128 (level 175 represents values +very close to FSD and may require saturating arithmetic to decode). + +Each pair of quantized pixels (q0,q1) or (q2,q3) is jointly coded +by a 15-bit field: 2816*(q0>>4) + 16*q1 + (q0&15). +Three fields of 2, 15, 15 bits are packed in LE order {15,15,2}. + +An implementation of a software decoder of compressed formats is available +in `Raspberry Pi camera applications code base +`_. diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 7c684788997c..5eb4d797d259 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1532,6 +1532,16 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_MT2110T: descr = "Mediatek 10bit Tile Mode"; break; case V4L2_PIX_FMT_MT2110R: descr = "Mediatek 10bit Raster Mode"; break; case V4L2_PIX_FMT_HEXTILE: descr = "Hextile Compressed Format"; break; + case V4L2_PIX_FMT_PISP_COMP1_RGGB: descr = "PiSP 8b RGRG/GBGB mode1 compr"; break; + case V4L2_PIX_FMT_PISP_COMP1_GRBG: descr = "PiSP 8b GRGR/BGBG mode1 compr"; break; + case V4L2_PIX_FMT_PISP_COMP1_GBRG: descr = "PiSP 8b GBGB/RGRG mode1 compr"; break; + case V4L2_PIX_FMT_PISP_COMP1_BGGR: descr = "PiSP 8b BGBG/GRGR mode1 compr"; break; + case V4L2_PIX_FMT_PISP_COMP1_MONO: descr = "PiSP 8b monochrome mode1 compr"; break; + case V4L2_PIX_FMT_PISP_COMP2_RGGB: descr = "PiSP 8b RGRG/GBGB mode2 compr"; break; + case V4L2_PIX_FMT_PISP_COMP2_GRBG: descr = "PiSP 8b GRGR/BGBG mode2 compr"; break; + case V4L2_PIX_FMT_PISP_COMP2_GBRG: descr = "PiSP 8b GBGB/RGRG mode2 compr"; break; + case V4L2_PIX_FMT_PISP_COMP2_BGGR: descr = "PiSP 8b BGBG/GRGR mode2 compr"; break; + case V4L2_PIX_FMT_PISP_COMP2_MONO: descr = "PiSP 8b monochrome mode2 compr"; break; default: if (fmt->description[0]) return; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 96fc0456081e..4e91362da6da 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -816,6 +816,18 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_IPU3_SGRBG10 v4l2_fourcc('i', 'p', '3', 'G') /* IPU3 packed 10-bit GRBG bayer */ #define V4L2_PIX_FMT_IPU3_SRGGB10 v4l2_fourcc('i', 'p', '3', 'r') /* IPU3 packed 10-bit RGGB bayer */ +/* Raspberry Pi PiSP compressed formats. */ +#define V4L2_PIX_FMT_PISP_COMP1_RGGB v4l2_fourcc('P', 'C', '1', 'R') /* PiSP 8-bit mode 1 compressed RGGB bayer */ +#define V4L2_PIX_FMT_PISP_COMP1_GRBG v4l2_fourcc('P', 'C', '1', 'G') /* PiSP 8-bit mode 1 compressed GRBG bayer */ +#define V4L2_PIX_FMT_PISP_COMP1_GBRG v4l2_fourcc('P', 'C', '1', 'g') /* PiSP 8-bit mode 1 compressed GBRG bayer */ +#define V4L2_PIX_FMT_PISP_COMP1_BGGR v4l2_fourcc('P', 'C', '1', 'B') /* PiSP 8-bit mode 1 compressed BGGR bayer */ +#define V4L2_PIX_FMT_PISP_COMP1_MONO v4l2_fourcc('P', 'C', '1', 'M') /* PiSP 8-bit mode 1 compressed monochrome */ +#define V4L2_PIX_FMT_PISP_COMP2_RGGB v4l2_fourcc('P', 'C', '2', 'R') /* PiSP 8-bit mode 2 compressed RGGB bayer */ +#define V4L2_PIX_FMT_PISP_COMP2_GRBG v4l2_fourcc('P', 'C', '2', 'G') /* PiSP 8-bit mode 2 compressed GRBG bayer */ +#define V4L2_PIX_FMT_PISP_COMP2_GBRG v4l2_fourcc('P', 'C', '2', 'g') /* PiSP 8-bit mode 2 compressed GBRG bayer */ +#define V4L2_PIX_FMT_PISP_COMP2_BGGR v4l2_fourcc('P', 'C', '2', 'B') /* PiSP 8-bit mode 2 compressed BGGR bayer */ +#define V4L2_PIX_FMT_PISP_COMP2_MONO v4l2_fourcc('P', 'C', '2', 'M') /* PiSP 8-bit mode 2 compressed monochrome */ + /* SDR formats - used only for Software Defined Radio devices */ #define V4L2_SDR_FMT_CU8 v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */ #define V4L2_SDR_FMT_CU16LE v4l2_fourcc('C', 'U', '1', '6') /* IQ u16le */ -- cgit v1.2.3 From cbc775e060cecbdb7210f46d8e7a65df2a865ee7 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 26 Jun 2024 20:14:36 +0200 Subject: media: dt-bindings: Add bindings for Raspberry Pi PiSP Back End Add bindings for the Raspberry Pi PiSP Back End memory-to-memory image signal processor. Datasheet: https://datasheets.raspberrypi.com/camera/raspberry-pi-image-signal-processor-specification.pdf Signed-off-by: Jacopo Mondi Reviewed-by: Rob Herring Reviewed-by: Naushir Patuck Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .../bindings/media/raspberrypi,pispbe.yaml | 63 ++++++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 64 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/raspberrypi,pispbe.yaml diff --git a/Documentation/devicetree/bindings/media/raspberrypi,pispbe.yaml b/Documentation/devicetree/bindings/media/raspberrypi,pispbe.yaml new file mode 100644 index 000000000000..1fc62a1d8eda --- /dev/null +++ b/Documentation/devicetree/bindings/media/raspberrypi,pispbe.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/raspberrypi,pispbe.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Raspberry Pi PiSP Image Signal Processor (ISP) Back End + +maintainers: + - Raspberry Pi Kernel Maintenance + - Jacopo Mondi + +description: | + The Raspberry Pi PiSP Image Signal Processor (ISP) Back End is an image + processor that fetches images in Bayer or Grayscale format from DRAM memory + in tiles and produces images consumable by applications. + + The full ISP documentation is available at + https://datasheets.raspberrypi.com/camera/raspberry-pi-image-signal-processor-specification.pdf + +properties: + compatible: + items: + - enum: + - brcm,bcm2712-pispbe + - const: raspberrypi,pispbe + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + iommus: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + +additionalProperties: false + +examples: + - | + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + isp@880000 { + compatible = "brcm,bcm2712-pispbe", "raspberrypi,pispbe"; + reg = <0x10 0x00880000 0x0 0x4000>; + interrupts = ; + clocks = <&firmware_clocks 7>; + iommus = <&iommu2>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 861ee24cc1f7..4f8126990da2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18810,6 +18810,7 @@ M: Jacopo Mondi L: Raspberry Pi Kernel Maintenance L: linux-media@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/media/raspberrypi,pispbe.yaml F: include/uapi/linux/media/raspberrypi/ RC-CORE / LIRC FRAMEWORK -- cgit v1.2.3 From 12187bd5d4f8c128c9deca1b7f95fde78eecf99b Mon Sep 17 00:00:00 2001 From: Naushir Patuck Date: Wed, 26 Jun 2024 20:14:37 +0200 Subject: media: raspberrypi: Add support for PiSP BE Add support for the Raspberry Pi PiSP Back End. The driver has been upported from the Raspberry Pi kernel at revision f74893f8a0c2 ("drivers: media: pisp_be: Update seqeuence numbers of the buffers"). The ISP documentation is available at: https://datasheets.raspberrypi.com/camera/raspberry-pi-image-signal-processor-specification.pdf Signed-off-by: David Plowman Signed-off-by: Naushir Patuck Signed-off-by: Nick Hollinghurst Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil [hverkuil: drop dev_err after platform_get_irq to fix a coccinelle check] --- MAINTAINERS | 1 + drivers/media/platform/Kconfig | 1 + drivers/media/platform/Makefile | 1 + drivers/media/platform/raspberrypi/Kconfig | 5 + drivers/media/platform/raspberrypi/Makefile | 3 + drivers/media/platform/raspberrypi/pisp_be/Kconfig | 12 + .../media/platform/raspberrypi/pisp_be/Makefile | 6 + .../media/platform/raspberrypi/pisp_be/pisp_be.c | 1799 ++++++++++++++++++++ .../platform/raspberrypi/pisp_be/pisp_be_formats.h | 519 ++++++ 9 files changed, 2347 insertions(+) create mode 100644 drivers/media/platform/raspberrypi/Kconfig create mode 100644 drivers/media/platform/raspberrypi/Makefile create mode 100644 drivers/media/platform/raspberrypi/pisp_be/Kconfig create mode 100644 drivers/media/platform/raspberrypi/pisp_be/Makefile create mode 100644 drivers/media/platform/raspberrypi/pisp_be/pisp_be.c create mode 100644 drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h diff --git a/MAINTAINERS b/MAINTAINERS index 4f8126990da2..19717843e342 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18811,6 +18811,7 @@ L: Raspberry Pi Kernel Maintenance L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/raspberrypi,pispbe.yaml +F: drivers/media/platform/raspberrypi/pisp_be/ F: include/uapi/linux/media/raspberrypi/ RC-CORE / LIRC FRAMEWORK diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 6feeebab4b06..85d2627776b6 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -79,6 +79,7 @@ source "drivers/media/platform/nuvoton/Kconfig" source "drivers/media/platform/nvidia/Kconfig" source "drivers/media/platform/nxp/Kconfig" source "drivers/media/platform/qcom/Kconfig" +source "drivers/media/platform/raspberrypi/Kconfig" source "drivers/media/platform/renesas/Kconfig" source "drivers/media/platform/rockchip/Kconfig" source "drivers/media/platform/samsung/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 11a2f2d0c1a1..ace4e34483dd 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -22,6 +22,7 @@ obj-y += nuvoton/ obj-y += nvidia/ obj-y += nxp/ obj-y += qcom/ +obj-y += raspberrypi/ obj-y += renesas/ obj-y += rockchip/ obj-y += samsung/ diff --git a/drivers/media/platform/raspberrypi/Kconfig b/drivers/media/platform/raspberrypi/Kconfig new file mode 100644 index 000000000000..e928f979019e --- /dev/null +++ b/drivers/media/platform/raspberrypi/Kconfig @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "Raspberry Pi media platform drivers" + +source "drivers/media/platform/raspberrypi/pisp_be/Kconfig" diff --git a/drivers/media/platform/raspberrypi/Makefile b/drivers/media/platform/raspberrypi/Makefile new file mode 100644 index 000000000000..c0d1a2dab486 --- /dev/null +++ b/drivers/media/platform/raspberrypi/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-y += pisp_be/ diff --git a/drivers/media/platform/raspberrypi/pisp_be/Kconfig b/drivers/media/platform/raspberrypi/pisp_be/Kconfig new file mode 100644 index 000000000000..38c0f8305d62 --- /dev/null +++ b/drivers/media/platform/raspberrypi/pisp_be/Kconfig @@ -0,0 +1,12 @@ +config VIDEO_RASPBERRYPI_PISP_BE + tristate "Raspberry Pi PiSP Backend (BE) ISP driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV + select VIDEO_V4L2_SUBDEV_API + select MEDIA_CONTROLLER + select VIDEOBUF2_DMA_CONTIG + help + Say Y here to enable support for the PiSP Backend (BE) ISP driver. + + To compile this driver as a module, choose M here. The module will be + called pisp-be. diff --git a/drivers/media/platform/raspberrypi/pisp_be/Makefile b/drivers/media/platform/raspberrypi/pisp_be/Makefile new file mode 100644 index 000000000000..a70bf5716824 --- /dev/null +++ b/drivers/media/platform/raspberrypi/pisp_be/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for Raspberry Pi PiSP Backend driver +# +pisp-be-objs := pisp_be.o +obj-$(CONFIG_VIDEO_RASPBERRYPI_PISP_BE) += pisp-be.o diff --git a/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c new file mode 100644 index 000000000000..e74df5b116dc --- /dev/null +++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c @@ -0,0 +1,1799 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PiSP Back End driver. + * Copyright (c) 2021-2024 Raspberry Pi Limited. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pisp_be_formats.h" + +/* Maximum number of config buffers possible */ +#define PISP_BE_NUM_CONFIG_BUFFERS VB2_MAX_FRAME + +#define PISPBE_NAME "pispbe" + +/* Some ISP-BE registers */ +#define PISP_BE_VERSION_REG 0x0 +#define PISP_BE_CONTROL_REG 0x4 +#define PISP_BE_CONTROL_COPY_CONFIG BIT(1) +#define PISP_BE_CONTROL_QUEUE_JOB BIT(0) +#define PISP_BE_CONTROL_NUM_TILES(n) ((n) << 16) +#define PISP_BE_TILE_ADDR_LO_REG 0x8 +#define PISP_BE_TILE_ADDR_HI_REG 0xc +#define PISP_BE_STATUS_REG 0x10 +#define PISP_BE_STATUS_QUEUED BIT(0) +#define PISP_BE_BATCH_STATUS_REG 0x14 +#define PISP_BE_INTERRUPT_EN_REG 0x18 +#define PISP_BE_INTERRUPT_STATUS_REG 0x1c +#define PISP_BE_AXI_REG 0x20 +#define PISP_BE_CONFIG_BASE_REG 0x40 +#define PISP_BE_IO_ADDR_LOW(n) (PISP_BE_CONFIG_BASE_REG + 8 * (n)) +#define PISP_BE_IO_ADDR_HIGH(n) (PISP_BE_IO_ADDR_LOW((n)) + 4) +#define PISP_BE_GLOBAL_BAYER_ENABLE 0xb0 +#define PISP_BE_GLOBAL_RGB_ENABLE 0xb4 +#define N_HW_ADDRESSES 13 +#define N_HW_ENABLES 2 + +#define PISP_BE_VERSION_2712 0x02252700 +#define PISP_BE_VERSION_MINOR_BITS 0xf + +/* + * This maps our nodes onto the inputs/outputs of the actual PiSP Back End. + * Be wary of the word "OUTPUT" which is used ambiguously here. In a V4L2 + * context it means an input to the hardware (source image or metadata). + * Elsewhere it means an output from the hardware. + */ +enum pispbe_node_ids { + MAIN_INPUT_NODE, + TDN_INPUT_NODE, + STITCH_INPUT_NODE, + OUTPUT0_NODE, + OUTPUT1_NODE, + TDN_OUTPUT_NODE, + STITCH_OUTPUT_NODE, + CONFIG_NODE, + PISPBE_NUM_NODES +}; + +struct pispbe_node_description { + const char *ent_name; + enum v4l2_buf_type buf_type; + unsigned int caps; +}; + +static const struct pispbe_node_description node_desc[PISPBE_NUM_NODES] = { + /* MAIN_INPUT_NODE */ + { + .ent_name = PISPBE_NAME "-input", + .buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE, + }, + /* TDN_INPUT_NODE */ + { + .ent_name = PISPBE_NAME "-tdn_input", + .buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE, + }, + /* STITCH_INPUT_NODE */ + { + .ent_name = PISPBE_NAME "-stitch_input", + .buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + .caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE, + }, + /* OUTPUT0_NODE */ + { + .ent_name = PISPBE_NAME "-output0", + .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE, + }, + /* OUTPUT1_NODE */ + { + .ent_name = PISPBE_NAME "-output1", + .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE, + }, + /* TDN_OUTPUT_NODE */ + { + .ent_name = PISPBE_NAME "-tdn_output", + .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE, + }, + /* STITCH_OUTPUT_NODE */ + { + .ent_name = PISPBE_NAME "-stitch_output", + .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + .caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE, + }, + /* CONFIG_NODE */ + { + .ent_name = PISPBE_NAME "-config", + .buf_type = V4L2_BUF_TYPE_META_OUTPUT, + .caps = V4L2_CAP_META_OUTPUT, + } +}; + +#define NODE_DESC_IS_OUTPUT(desc) ( \ + ((desc)->buf_type == V4L2_BUF_TYPE_META_OUTPUT) || \ + ((desc)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT) || \ + ((desc)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) + +#define NODE_IS_META(node) ( \ + ((node)->buf_type == V4L2_BUF_TYPE_META_OUTPUT)) +#define NODE_IS_OUTPUT(node) ( \ + ((node)->buf_type == V4L2_BUF_TYPE_META_OUTPUT) || \ + ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT) || \ + ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) +#define NODE_IS_CAPTURE(node) ( \ + ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) || \ + ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) +#define NODE_IS_MPLANE(node) ( \ + ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) || \ + ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) + +/* + * Structure to describe a single node /dev/video which represents a single + * input or output queue to the PiSP Back End device. + */ +struct pispbe_node { + unsigned int id; + int vfl_dir; + enum v4l2_buf_type buf_type; + struct video_device vfd; + struct media_pad pad; + struct media_intf_devnode *intf_devnode; + struct media_link *intf_link; + struct pispbe_dev *pispbe; + /* Video device lock */ + struct mutex node_lock; + /* vb2_queue lock */ + struct mutex queue_lock; + /* Protect pispbe_node->ready_queue and pispbe_buffer->ready_list */ + spinlock_t ready_lock; + struct list_head ready_queue; + struct vb2_queue queue; + struct v4l2_format format; + const struct pisp_be_format *pisp_format; +}; + +/* For logging only, use the entity name with "pispbe" and separator removed */ +#define NODE_NAME(node) \ + (node_desc[(node)->id].ent_name + sizeof(PISPBE_NAME)) + +/* Records details of the jobs currently running or queued on the h/w. */ +struct pispbe_job { + bool valid; + /* + * An array of buffer pointers - remember it's source buffers first, + * then captures, then metadata last. + */ + struct pispbe_buffer *buf[PISPBE_NUM_NODES]; +}; + +struct pispbe_hw_enables { + u32 bayer_enables; + u32 rgb_enables; +}; + +/* Records a job configuration and memory addresses. */ +struct pispbe_job_descriptor { + dma_addr_t hw_dma_addrs[N_HW_ADDRESSES]; + struct pisp_be_tiles_config *config; + struct pispbe_hw_enables hw_enables; + dma_addr_t tiles; +}; + +/* + * Structure representing the entire PiSP Back End device, comprising several + * nodes which share platform resources and a mutex for the actual HW. + */ +struct pispbe_dev { + struct device *dev; + struct pispbe_dev *pispbe; + struct pisp_be_tiles_config *config; + void __iomem *be_reg_base; + struct clk *clk; + struct v4l2_device v4l2_dev; + struct v4l2_subdev sd; + struct media_device mdev; + struct media_pad pad[PISPBE_NUM_NODES]; /* output pads first */ + struct pispbe_node node[PISPBE_NUM_NODES]; + dma_addr_t config_dma_addr; + unsigned int sequence; + u32 streaming_map; + struct pispbe_job queued_job, running_job; + spinlock_t hw_lock; /* protects "hw_busy" flag and streaming_map */ + bool hw_busy; /* non-zero if a job is queued or is being started */ + int irq; + u32 hw_version; + u8 done, started; +}; + +static u32 pispbe_rd(struct pispbe_dev *pispbe, unsigned int offset) +{ + return readl(pispbe->be_reg_base + offset); +} + +static void pispbe_wr(struct pispbe_dev *pispbe, unsigned int offset, u32 val) +{ + writel(val, pispbe->be_reg_base + offset); +} + +/* + * Queue a job to the h/w. If the h/w is idle it will begin immediately. + * Caller must ensure it is "safe to queue", i.e. we don't already have a + * queued, unstarted job. + */ +static void pispbe_queue_job(struct pispbe_dev *pispbe, + struct pispbe_job_descriptor *job) +{ + unsigned int begin, end; + + if (pispbe_rd(pispbe, PISP_BE_STATUS_REG) & PISP_BE_STATUS_QUEUED) + dev_err(pispbe->dev, "ERROR: not safe to queue new job!\n"); + + /* + * Write configuration to hardware. DMA addresses and enable flags + * are passed separately, because the driver needs to sanitize them, + * and we don't want to modify (or be vulnerable to modifications of) + * the mmap'd buffer. + */ + for (unsigned int u = 0; u < N_HW_ADDRESSES; ++u) { + pispbe_wr(pispbe, PISP_BE_IO_ADDR_LOW(u), + lower_32_bits(job->hw_dma_addrs[u])); + pispbe_wr(pispbe, PISP_BE_IO_ADDR_HIGH(u), + upper_32_bits(job->hw_dma_addrs[u])); + } + pispbe_wr(pispbe, PISP_BE_GLOBAL_BAYER_ENABLE, + job->hw_enables.bayer_enables); + pispbe_wr(pispbe, PISP_BE_GLOBAL_RGB_ENABLE, + job->hw_enables.rgb_enables); + + /* Everything else is as supplied by the user. */ + begin = offsetof(struct pisp_be_config, global.bayer_order) / + sizeof(u32); + end = sizeof(struct pisp_be_config) / sizeof(u32); + for (unsigned int u = begin; u < end; u++) + pispbe_wr(pispbe, PISP_BE_CONFIG_BASE_REG + sizeof(u32) * u, + ((u32 *)job->config)[u]); + + /* Read back the addresses -- an error here could be fatal */ + for (unsigned int u = 0; u < N_HW_ADDRESSES; ++u) { + unsigned int offset = PISP_BE_IO_ADDR_LOW(u); + u64 along = pispbe_rd(pispbe, offset); + + along += ((u64)pispbe_rd(pispbe, offset + 4)) << 32; + if (along != (u64)(job->hw_dma_addrs[u])) { + dev_dbg(pispbe->dev, + "ISP BE config error: check if ISP RAMs enabled?\n"); + return; + } + } + + /* + * Write tile pointer to hardware. The IOMMU should prevent + * out-of-bounds offsets reaching non-ISP buffers. + */ + pispbe_wr(pispbe, PISP_BE_TILE_ADDR_LO_REG, lower_32_bits(job->tiles)); + pispbe_wr(pispbe, PISP_BE_TILE_ADDR_HI_REG, upper_32_bits(job->tiles)); + + /* Enqueue the job */ + pispbe_wr(pispbe, PISP_BE_CONTROL_REG, + PISP_BE_CONTROL_COPY_CONFIG | PISP_BE_CONTROL_QUEUE_JOB | + PISP_BE_CONTROL_NUM_TILES(job->config->num_tiles)); +} + +struct pispbe_buffer { + struct vb2_v4l2_buffer vb; + struct list_head ready_list; + unsigned int config_index; +}; + +static int pispbe_get_planes_addr(dma_addr_t addr[3], struct pispbe_buffer *buf, + struct pispbe_node *node) +{ + unsigned int num_planes = node->format.fmt.pix_mp.num_planes; + unsigned int plane_factor = 0; + unsigned int size; + unsigned int p; + + if (!buf || !node->pisp_format) + return 0; + + /* + * Determine the base plane size. This will not be the same + * as node->format.fmt.pix_mp.plane_fmt[0].sizeimage for a single + * plane buffer in an mplane format. + */ + size = node->format.fmt.pix_mp.plane_fmt[0].bytesperline * + node->format.fmt.pix_mp.height; + + for (p = 0; p < num_planes && p < PISPBE_MAX_PLANES; p++) { + addr[p] = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, p); + plane_factor += node->pisp_format->plane_factor[p]; + } + + for (; p < PISPBE_MAX_PLANES && node->pisp_format->plane_factor[p]; p++) { + /* + * Calculate the address offset of this plane as needed + * by the hardware. This is specifically for non-mplane + * buffer formats, where there are 3 image planes, e.g. + * for the V4L2_PIX_FMT_YUV420 format. + */ + addr[p] = addr[0] + ((size * plane_factor) >> 3); + plane_factor += node->pisp_format->plane_factor[p]; + } + + return num_planes; +} + +static dma_addr_t pispbe_get_addr(struct pispbe_buffer *buf) +{ + if (buf) + return vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + + return 0; +} + +static void pispbe_xlate_addrs(struct pispbe_dev *pispbe, + struct pispbe_job_descriptor *job, + struct pispbe_buffer *buf[PISPBE_NUM_NODES]) +{ + struct pispbe_hw_enables *hw_en = &job->hw_enables; + struct pisp_be_tiles_config *config = job->config; + dma_addr_t *addrs = job->hw_dma_addrs; + int ret; + + /* Take a copy of the "enable" bitmaps so we can modify them. */ + hw_en->bayer_enables = config->config.global.bayer_enables; + hw_en->rgb_enables = config->config.global.rgb_enables; + + /* + * Main input first. There are 3 address pointers, corresponding to up + * to 3 planes. + */ + ret = pispbe_get_planes_addr(addrs, buf[MAIN_INPUT_NODE], + &pispbe->node[MAIN_INPUT_NODE]); + if (ret <= 0) { + /* + * This shouldn't happen; pispbe_schedule_internal should insist + * on an input. + */ + dev_warn(pispbe->dev, "ISP-BE missing input\n"); + hw_en->bayer_enables = 0; + hw_en->rgb_enables = 0; + return; + } + + /* + * Now TDN/Stitch inputs and outputs. These are single-plane and only + * used with Bayer input. Input enables must match the requirements + * of the processing stages, otherwise the hardware can lock up! + */ + if (hw_en->bayer_enables & PISP_BE_BAYER_ENABLE_INPUT) { + addrs[3] = pispbe_get_addr(buf[TDN_INPUT_NODE]); + if (addrs[3] == 0 || + !(hw_en->bayer_enables & PISP_BE_BAYER_ENABLE_TDN_INPUT) || + !(hw_en->bayer_enables & PISP_BE_BAYER_ENABLE_TDN) || + (config->config.tdn.reset & 1)) { + hw_en->bayer_enables &= + ~(PISP_BE_BAYER_ENABLE_TDN_INPUT | + PISP_BE_BAYER_ENABLE_TDN_DECOMPRESS); + if (!(config->config.tdn.reset & 1)) + hw_en->bayer_enables &= + ~PISP_BE_BAYER_ENABLE_TDN; + } + + addrs[4] = pispbe_get_addr(buf[STITCH_INPUT_NODE]); + if (addrs[4] == 0 || + !(hw_en->bayer_enables & PISP_BE_BAYER_ENABLE_STITCH_INPUT) || + !(hw_en->bayer_enables & PISP_BE_BAYER_ENABLE_STITCH)) { + hw_en->bayer_enables &= + ~(PISP_BE_BAYER_ENABLE_STITCH_INPUT | + PISP_BE_BAYER_ENABLE_STITCH_DECOMPRESS | + PISP_BE_BAYER_ENABLE_STITCH); + } + + addrs[5] = pispbe_get_addr(buf[TDN_OUTPUT_NODE]); + if (addrs[5] == 0) + hw_en->bayer_enables &= + ~(PISP_BE_BAYER_ENABLE_TDN_COMPRESS | + PISP_BE_BAYER_ENABLE_TDN_OUTPUT); + + addrs[6] = pispbe_get_addr(buf[STITCH_OUTPUT_NODE]); + if (addrs[6] == 0) + hw_en->bayer_enables &= + ~(PISP_BE_BAYER_ENABLE_STITCH_COMPRESS | + PISP_BE_BAYER_ENABLE_STITCH_OUTPUT); + } else { + /* No Bayer input? Disable entire Bayer pipe (else lockup) */ + hw_en->bayer_enables = 0; + } + + /* Main image output channels. */ + for (unsigned int i = 0; i < PISP_BACK_END_NUM_OUTPUTS; i++) { + ret = pispbe_get_planes_addr(addrs + 7 + 3 * i, + buf[OUTPUT0_NODE + i], + &pispbe->node[OUTPUT0_NODE + i]); + if (ret <= 0) + hw_en->rgb_enables &= ~(PISP_BE_RGB_ENABLE_OUTPUT0 << i); + } +} + +/* + * Prepare a job description to be submitted to the HW. + * + * To schedule a job, we need all streaming nodes (apart from Output0, + * Output1, Tdn and Stitch) to have a buffer ready, which must + * include at least a config buffer and a main input image. + * + * For Output0, Output1, Tdn and Stitch, a buffer only needs to be + * available if the blocks are enabled in the config. + * + * Needs to be called with hw_lock held. + * + * Returns 0 if a job has been successfully prepared, < 0 otherwise. + */ +static int pispbe_prepare_job(struct pispbe_dev *pispbe, + struct pispbe_job_descriptor *job) +{ + struct pispbe_buffer *buf[PISPBE_NUM_NODES] = {}; + unsigned int config_index; + struct pispbe_node *node; + unsigned long flags; + + lockdep_assert_held(&pispbe->hw_lock); + + memset(job, 0, sizeof(struct pispbe_job_descriptor)); + + if (((BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE)) & + pispbe->streaming_map) != + (BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE))) + return -ENODEV; + + node = &pispbe->node[CONFIG_NODE]; + spin_lock_irqsave(&node->ready_lock, flags); + buf[CONFIG_NODE] = list_first_entry_or_null(&node->ready_queue, + struct pispbe_buffer, + ready_list); + if (buf[CONFIG_NODE]) { + list_del(&buf[CONFIG_NODE]->ready_list); + pispbe->queued_job.buf[CONFIG_NODE] = buf[CONFIG_NODE]; + } + spin_unlock_irqrestore(&node->ready_lock, flags); + + /* Exit early if no config buffer has been queued. */ + if (!buf[CONFIG_NODE]) + return -ENODEV; + + config_index = buf[CONFIG_NODE]->vb.vb2_buf.index; + job->config = &pispbe->config[config_index]; + job->tiles = pispbe->config_dma_addr + + config_index * sizeof(struct pisp_be_tiles_config) + + offsetof(struct pisp_be_tiles_config, tiles); + + /* remember: srcimages, captures then metadata */ + for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++) { + unsigned int bayer_en = + job->config->config.global.bayer_enables; + unsigned int rgb_en = + job->config->config.global.rgb_enables; + bool ignore_buffers = false; + + /* Config node is handled outside the loop above. */ + if (i == CONFIG_NODE) + continue; + + buf[i] = NULL; + if (!(pispbe->streaming_map & BIT(i))) + continue; + + if ((!(rgb_en & PISP_BE_RGB_ENABLE_OUTPUT0) && + i == OUTPUT0_NODE) || + (!(rgb_en & PISP_BE_RGB_ENABLE_OUTPUT1) && + i == OUTPUT1_NODE) || + (!(bayer_en & PISP_BE_BAYER_ENABLE_TDN_INPUT) && + i == TDN_INPUT_NODE) || + (!(bayer_en & PISP_BE_BAYER_ENABLE_TDN_OUTPUT) && + i == TDN_OUTPUT_NODE) || + (!(bayer_en & PISP_BE_BAYER_ENABLE_STITCH_INPUT) && + i == STITCH_INPUT_NODE) || + (!(bayer_en & PISP_BE_BAYER_ENABLE_STITCH_OUTPUT) && + i == STITCH_OUTPUT_NODE)) { + /* + * Ignore Output0/Output1/Tdn/Stitch buffer check if the + * global enables aren't set for these blocks. If a + * buffer has been provided, we dequeue it back to the + * user with the other in-use buffers. + */ + ignore_buffers = true; + } + + node = &pispbe->node[i]; + + /* Pull a buffer from each V4L2 queue to form the queued job */ + spin_lock_irqsave(&node->ready_lock, flags); + buf[i] = list_first_entry_or_null(&node->ready_queue, + struct pispbe_buffer, + ready_list); + if (buf[i]) { + list_del(&buf[i]->ready_list); + pispbe->queued_job.buf[i] = buf[i]; + } + spin_unlock_irqrestore(&node->ready_lock, flags); + + if (!buf[i] && !ignore_buffers) + goto err_return_buffers; + } + + pispbe->queued_job.valid = true; + + /* Convert buffers to DMA addresses for the hardware */ + pispbe_xlate_addrs(pispbe, job, buf); + + return 0; + +err_return_buffers: + for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++) { + struct pispbe_node *n = &pispbe->node[i]; + + if (!buf[i]) + continue; + + /* Return the buffer to the ready_list queue */ + spin_lock_irqsave(&n->ready_lock, flags); + list_add(&buf[i]->ready_list, &n->ready_queue); + spin_unlock_irqrestore(&n->ready_lock, flags); + } + + memset(&pispbe->queued_job, 0, sizeof(pispbe->queued_job)); + + return -ENODEV; +} + +static void pispbe_schedule(struct pispbe_dev *pispbe, bool clear_hw_busy) +{ + struct pispbe_job_descriptor job; + unsigned long flags; + int ret; + + spin_lock_irqsave(&pispbe->hw_lock, flags); + + if (clear_hw_busy) + pispbe->hw_busy = false; + + if (pispbe->hw_busy) + goto unlock_and_return; + + ret = pispbe_prepare_job(pispbe, &job); + if (ret) + goto unlock_and_return; + + /* + * We can kick the job off without the hw_lock, as this can + * never run again until hw_busy is cleared, which will happen + * only when the following job has been queued and an interrupt + * is rised. + */ + pispbe->hw_busy = true; + spin_unlock_irqrestore(&pispbe->hw_lock, flags); + + if (job.config->num_tiles <= 0 || + job.config->num_tiles > PISP_BACK_END_NUM_TILES || + !((job.hw_enables.bayer_enables | job.hw_enables.rgb_enables) & + PISP_BE_BAYER_ENABLE_INPUT)) { + /* + * Bad job. We can't let it proceed as it could lock up + * the hardware, or worse! + * + * For now, just force num_tiles to 0, which causes the + * H/W to do something bizarre but survivable. It + * increments (started,done) counters by more than 1, + * but we seem to survive... + */ + dev_dbg(pispbe->dev, "Bad job: invalid number of tiles: %u\n", + job.config->num_tiles); + job.config->num_tiles = 0; + } + + pispbe_queue_job(pispbe, &job); + + return; + +unlock_and_return: + /* No job has been queued, just release the lock and return. */ + spin_unlock_irqrestore(&pispbe->hw_lock, flags); +} + +static void pispbe_isr_jobdone(struct pispbe_dev *pispbe, + struct pispbe_job *job) +{ + struct pispbe_buffer **buf = job->buf; + u64 ts = ktime_get_ns(); + + for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++) { + if (buf[i]) { + buf[i]->vb.vb2_buf.timestamp = ts; + buf[i]->vb.sequence = pispbe->sequence; + vb2_buffer_done(&buf[i]->vb.vb2_buf, + VB2_BUF_STATE_DONE); + } + } + + pispbe->sequence++; +} + +static irqreturn_t pispbe_isr(int irq, void *dev) +{ + struct pispbe_dev *pispbe = (struct pispbe_dev *)dev; + bool can_queue_another = false; + u8 started, done; + u32 u; + + u = pispbe_rd(pispbe, PISP_BE_INTERRUPT_STATUS_REG); + if (u == 0) + return IRQ_NONE; + + pispbe_wr(pispbe, PISP_BE_INTERRUPT_STATUS_REG, u); + u = pispbe_rd(pispbe, PISP_BE_BATCH_STATUS_REG); + done = (uint8_t)u; + started = (uint8_t)(u >> 8); + + /* + * Be aware that done can go up by 2 and started by 1 when: a job that + * we previously saw "start" now finishes, and we then queued a new job + * which we see both start and finish "simultaneously". + */ + if (pispbe->running_job.valid && pispbe->done != done) { + pispbe_isr_jobdone(pispbe, &pispbe->running_job); + memset(&pispbe->running_job, 0, sizeof(pispbe->running_job)); + pispbe->done++; + } + + if (pispbe->started != started) { + pispbe->started++; + can_queue_another = 1; + + if (pispbe->done != done && pispbe->queued_job.valid) { + pispbe_isr_jobdone(pispbe, &pispbe->queued_job); + pispbe->done++; + } else { + pispbe->running_job = pispbe->queued_job; + } + + memset(&pispbe->queued_job, 0, sizeof(pispbe->queued_job)); + } + + if (pispbe->done != done || pispbe->started != started) { + dev_dbg(pispbe->dev, + "Job counters not matching: done = %u, expected %u - started = %u, expected %u\n", + pispbe->done, done, pispbe->started, started); + pispbe->started = started; + pispbe->done = done; + } + + /* check if there's more to do before going to sleep */ + pispbe_schedule(pispbe, can_queue_another); + + return IRQ_HANDLED; +} + +static int pisp_be_validate_config(struct pispbe_dev *pispbe, + struct pisp_be_tiles_config *config) +{ + u32 bayer_enables = config->config.global.bayer_enables; + u32 rgb_enables = config->config.global.rgb_enables; + struct device *dev = pispbe->dev; + struct v4l2_format *fmt; + unsigned int bpl, size; + + if (!(bayer_enables & PISP_BE_BAYER_ENABLE_INPUT) == + !(rgb_enables & PISP_BE_RGB_ENABLE_INPUT)) { + dev_dbg(dev, "%s: Not one input enabled\n", __func__); + return -EIO; + } + + /* Ensure output config strides and buffer sizes match the V4L2 formats. */ + fmt = &pispbe->node[TDN_OUTPUT_NODE].format; + if (bayer_enables & PISP_BE_BAYER_ENABLE_TDN_OUTPUT) { + bpl = config->config.tdn_output_format.stride; + size = bpl * config->config.tdn_output_format.height; + + if (fmt->fmt.pix_mp.plane_fmt[0].bytesperline < bpl) { + dev_dbg(dev, "%s: bpl mismatch on tdn_output\n", + __func__); + return -EINVAL; + } + + if (fmt->fmt.pix_mp.plane_fmt[0].sizeimage < size) { + dev_dbg(dev, "%s: size mismatch on tdn_output\n", + __func__); + return -EINVAL; + } + } + + fmt = &pispbe->node[STITCH_OUTPUT_NODE].format; + if (bayer_enables & PISP_BE_BAYER_ENABLE_STITCH_OUTPUT) { + bpl = config->config.stitch_output_format.stride; + size = bpl * config->config.stitch_output_format.height; + + if (fmt->fmt.pix_mp.plane_fmt[0].bytesperline < bpl) { + dev_dbg(dev, "%s: bpl mismatch on stitch_output\n", + __func__); + return -EINVAL; + } + + if (fmt->fmt.pix_mp.plane_fmt[0].sizeimage < size) { + dev_dbg(dev, "%s: size mismatch on stitch_output\n", + __func__); + return -EINVAL; + } + } + + for (unsigned int j = 0; j < PISP_BACK_END_NUM_OUTPUTS; j++) { + if (!(rgb_enables & PISP_BE_RGB_ENABLE_OUTPUT(j))) + continue; + + if (config->config.output_format[j].image.format & + PISP_IMAGE_FORMAT_WALLPAPER_ROLL) + continue; /* TODO: Size checks for wallpaper formats */ + + fmt = &pispbe->node[OUTPUT0_NODE + j].format; + for (unsigned int i = 0; i < fmt->fmt.pix_mp.num_planes; i++) { + bpl = !i ? config->config.output_format[j].image.stride + : config->config.output_format[j].image.stride2; + size = bpl * config->config.output_format[j].image.height; + + if (config->config.output_format[j].image.format & + PISP_IMAGE_FORMAT_SAMPLING_420) + size >>= 1; + + if (fmt->fmt.pix_mp.plane_fmt[i].bytesperline < bpl) { + dev_dbg(dev, "%s: bpl mismatch on output %d\n", + __func__, j); + return -EINVAL; + } + + if (fmt->fmt.pix_mp.plane_fmt[i].sizeimage < size) { + dev_dbg(dev, "%s: size mismatch on output\n", + __func__); + return -EINVAL; + } + } + } + + return 0; +} + +static int pispbe_node_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct pispbe_node *node = vb2_get_drv_priv(q); + struct pispbe_dev *pispbe = node->pispbe; + unsigned int num_planes = NODE_IS_MPLANE(node) ? + node->format.fmt.pix_mp.num_planes : 1; + + if (*nplanes) { + if (*nplanes != num_planes) + return -EINVAL; + + for (unsigned int i = 0; i < *nplanes; i++) { + unsigned int size = NODE_IS_MPLANE(node) ? + node->format.fmt.pix_mp.plane_fmt[i].sizeimage : + node->format.fmt.meta.buffersize; + + if (sizes[i] < size) + return -EINVAL; + } + + return 0; + } + + *nplanes = num_planes; + for (unsigned int i = 0; i < *nplanes; i++) { + unsigned int size = NODE_IS_MPLANE(node) ? + node->format.fmt.pix_mp.plane_fmt[i].sizeimage : + node->format.fmt.meta.buffersize; + sizes[i] = size; + } + + dev_dbg(pispbe->dev, + "Image (or metadata) size %u, nbuffers %u for node %s\n", + sizes[0], *nbuffers, NODE_NAME(node)); + + return 0; +} + +static int pispbe_node_buffer_prepare(struct vb2_buffer *vb) +{ + struct pispbe_node *node = vb2_get_drv_priv(vb->vb2_queue); + struct pispbe_dev *pispbe = node->pispbe; + unsigned int num_planes = NODE_IS_MPLANE(node) ? + node->format.fmt.pix_mp.num_planes : 1; + + for (unsigned int i = 0; i < num_planes; i++) { + unsigned long size = NODE_IS_MPLANE(node) ? + node->format.fmt.pix_mp.plane_fmt[i].sizeimage : + node->format.fmt.meta.buffersize; + + if (vb2_plane_size(vb, i) < size) { + dev_dbg(pispbe->dev, + "data will not fit into plane %d (%lu < %lu)\n", + i, vb2_plane_size(vb, i), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, i, size); + } + + if (node->id == CONFIG_NODE) { + void *dst = &node->pispbe->config[vb->index]; + void *src = vb2_plane_vaddr(vb, 0); + + memcpy(dst, src, sizeof(struct pisp_be_tiles_config)); + + return pisp_be_validate_config(pispbe, dst); + } + + return 0; +} + +static void pispbe_node_buffer_queue(struct vb2_buffer *buf) +{ + struct vb2_v4l2_buffer *vbuf = + container_of(buf, struct vb2_v4l2_buffer, vb2_buf); + struct pispbe_buffer *buffer = + container_of(vbuf, struct pispbe_buffer, vb); + struct pispbe_node *node = vb2_get_drv_priv(buf->vb2_queue); + struct pispbe_dev *pispbe = node->pispbe; + unsigned long flags; + + dev_dbg(pispbe->dev, "%s: for node %s\n", __func__, NODE_NAME(node)); + spin_lock_irqsave(&node->ready_lock, flags); + list_add_tail(&buffer->ready_list, &node->ready_queue); + spin_unlock_irqrestore(&node->ready_lock, flags); + + /* + * Every time we add a buffer, check if there's now some work for the hw + * to do. + */ + pispbe_schedule(pispbe, false); +} + +static int pispbe_node_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct pispbe_node *node = vb2_get_drv_priv(q); + struct pispbe_dev *pispbe = node->pispbe; + struct pispbe_buffer *buf, *tmp; + unsigned long flags; + int ret; + + ret = pm_runtime_resume_and_get(pispbe->dev); + if (ret < 0) + goto err_return_buffers; + + spin_lock_irqsave(&pispbe->hw_lock, flags); + node->pispbe->streaming_map |= BIT(node->id); + node->pispbe->sequence = 0; + spin_unlock_irqrestore(&pispbe->hw_lock, flags); + + dev_dbg(pispbe->dev, "%s: for node %s (count %u)\n", + __func__, NODE_NAME(node), count); + dev_dbg(pispbe->dev, "Nodes streaming now 0x%x\n", + node->pispbe->streaming_map); + + /* Maybe we're ready to run. */ + pispbe_schedule(pispbe, false); + + return 0; + +err_return_buffers: + spin_lock_irqsave(&pispbe->hw_lock, flags); + list_for_each_entry_safe(buf, tmp, &node->ready_queue, ready_list) { + list_del(&buf->ready_list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + } + spin_unlock_irqrestore(&pispbe->hw_lock, flags); + + return ret; +} + +static void pispbe_node_stop_streaming(struct vb2_queue *q) +{ + struct pispbe_node *node = vb2_get_drv_priv(q); + struct pispbe_dev *pispbe = node->pispbe; + struct pispbe_buffer *buf; + unsigned long flags; + + /* + * Now this is a bit awkward. In a simple M2M device we could just wait + * for all queued jobs to complete, but here there's a risk that a + * partial set of buffers was queued and cannot be run. For now, just + * cancel all buffers stuck in the "ready queue", then wait for any + * running job. + * + * This may return buffers out of order. + */ + dev_dbg(pispbe->dev, "%s: for node %s\n", __func__, NODE_NAME(node)); + spin_lock_irqsave(&pispbe->hw_lock, flags); + do { + unsigned long flags1; + + spin_lock_irqsave(&node->ready_lock, flags1); + buf = list_first_entry_or_null(&node->ready_queue, + struct pispbe_buffer, + ready_list); + if (buf) { + list_del(&buf->ready_list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&node->ready_lock, flags1); + } while (buf); + spin_unlock_irqrestore(&pispbe->hw_lock, flags); + + vb2_wait_for_all_buffers(&node->queue); + + spin_lock_irqsave(&pispbe->hw_lock, flags); + pispbe->streaming_map &= ~BIT(node->id); + spin_unlock_irqrestore(&pispbe->hw_lock, flags); + + pm_runtime_mark_last_busy(pispbe->dev); + pm_runtime_put_autosuspend(pispbe->dev); + + dev_dbg(pispbe->dev, "Nodes streaming now 0x%x\n", + pispbe->streaming_map); +} + +static const struct vb2_ops pispbe_node_queue_ops = { + .queue_setup = pispbe_node_queue_setup, + .buf_prepare = pispbe_node_buffer_prepare, + .buf_queue = pispbe_node_buffer_queue, + .start_streaming = pispbe_node_start_streaming, + .stop_streaming = pispbe_node_stop_streaming, +}; + +static const struct v4l2_file_operations pispbe_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap +}; + +static int pispbe_node_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + strscpy(cap->driver, PISPBE_NAME, sizeof(cap->driver)); + strscpy(cap->card, PISPBE_NAME, sizeof(cap->card)); + + dev_dbg(pispbe->dev, "Caps for node %s: %x and %x (dev %x)\n", + NODE_NAME(node), cap->capabilities, cap->device_caps, + node->vfd.device_caps); + + return 0; +} + +static int pispbe_node_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (!NODE_IS_CAPTURE(node) || NODE_IS_META(node)) { + dev_dbg(pispbe->dev, + "Cannot get capture fmt for output node %s\n", + NODE_NAME(node)); + return -EINVAL; + } + + *f = node->format; + dev_dbg(pispbe->dev, "Get capture format for node %s\n", + NODE_NAME(node)); + + return 0; +} + +static int pispbe_node_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (NODE_IS_CAPTURE(node) || NODE_IS_META(node)) { + dev_dbg(pispbe->dev, + "Cannot get capture fmt for output node %s\n", + NODE_NAME(node)); + return -EINVAL; + } + + *f = node->format; + dev_dbg(pispbe->dev, "Get output format for node %s\n", + NODE_NAME(node)); + + return 0; +} + +static int pispbe_node_g_fmt_meta_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (!NODE_IS_META(node) || NODE_IS_CAPTURE(node)) { + dev_dbg(pispbe->dev, + "Cannot get capture fmt for meta output node %s\n", + NODE_NAME(node)); + return -EINVAL; + } + + *f = node->format; + dev_dbg(pispbe->dev, "Get output format for meta node %s\n", + NODE_NAME(node)); + + return 0; +} + +static const struct pisp_be_format *pispbe_find_fmt(unsigned int fourcc) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(supported_formats); i++) { + if (supported_formats[i].fourcc == fourcc) + return &supported_formats[i]; + } + + return NULL; +} + +static void pispbe_set_plane_params(struct v4l2_format *f, + const struct pisp_be_format *fmt) +{ + unsigned int nplanes = f->fmt.pix_mp.num_planes; + unsigned int total_plane_factor = 0; + + for (unsigned int i = 0; i < PISPBE_MAX_PLANES; i++) + total_plane_factor += fmt->plane_factor[i]; + + for (unsigned int i = 0; i < nplanes; i++) { + struct v4l2_plane_pix_format *p = &f->fmt.pix_mp.plane_fmt[i]; + unsigned int bpl, plane_size; + + bpl = (f->fmt.pix_mp.width * fmt->bit_depth) >> 3; + bpl = ALIGN(max(p->bytesperline, bpl), fmt->align); + + plane_size = bpl * f->fmt.pix_mp.height * + (nplanes > 1 ? fmt->plane_factor[i] : total_plane_factor); + /* + * The shift is to divide out the plane_factor fixed point + * scaling of 8. + */ + plane_size = max(p->sizeimage, plane_size >> 3); + + p->bytesperline = bpl; + p->sizeimage = plane_size; + } +} + +static void pispbe_try_format(struct v4l2_format *f, struct pispbe_node *node) +{ + struct pispbe_dev *pispbe = node->pispbe; + u32 pixfmt = f->fmt.pix_mp.pixelformat; + const struct pisp_be_format *fmt; + bool is_rgb; + + dev_dbg(pispbe->dev, + "%s: [%s] req %ux%u %p4cc, planes %d\n", + __func__, NODE_NAME(node), f->fmt.pix_mp.width, + f->fmt.pix_mp.height, &pixfmt, + f->fmt.pix_mp.num_planes); + + fmt = pispbe_find_fmt(pixfmt); + if (!fmt) { + dev_dbg(pispbe->dev, + "%s: [%s] Format not found, defaulting to YUV420\n", + __func__, NODE_NAME(node)); + fmt = pispbe_find_fmt(V4L2_PIX_FMT_YUV420); + } + + f->fmt.pix_mp.pixelformat = fmt->fourcc; + f->fmt.pix_mp.num_planes = fmt->num_planes; + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + f->fmt.pix_mp.width = max(min(f->fmt.pix_mp.width, 65536u), + PISP_BACK_END_MIN_TILE_WIDTH); + f->fmt.pix_mp.height = max(min(f->fmt.pix_mp.height, 65536u), + PISP_BACK_END_MIN_TILE_HEIGHT); + + /* + * Fill in the actual colour space when the requested one was + * not supported. This also catches the case when the "default" + * colour space was requested (as that's never in the mask). + */ + if (!(V4L2_COLORSPACE_MASK(f->fmt.pix_mp.colorspace) & + fmt->colorspace_mask)) + f->fmt.pix_mp.colorspace = fmt->colorspace_default; + + /* In all cases, we only support the defaults for these: */ + f->fmt.pix_mp.ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(f->fmt.pix_mp.colorspace); + f->fmt.pix_mp.xfer_func = + V4L2_MAP_XFER_FUNC_DEFAULT(f->fmt.pix_mp.colorspace); + + is_rgb = f->fmt.pix_mp.colorspace == V4L2_COLORSPACE_SRGB; + f->fmt.pix_mp.quantization = + V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb, f->fmt.pix_mp.colorspace, + f->fmt.pix_mp.ycbcr_enc); + + /* Set plane size and bytes/line for each plane. */ + pispbe_set_plane_params(f, fmt); + + for (unsigned int i = 0; i < f->fmt.pix_mp.num_planes; i++) { + dev_dbg(pispbe->dev, + "%s: [%s] calc plane %d, %ux%u, depth %u, bpl %u size %u\n", + __func__, NODE_NAME(node), i, f->fmt.pix_mp.width, + f->fmt.pix_mp.height, fmt->bit_depth, + f->fmt.pix_mp.plane_fmt[i].bytesperline, + f->fmt.pix_mp.plane_fmt[i].sizeimage); + } +} + +static int pispbe_node_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (!NODE_IS_CAPTURE(node) || NODE_IS_META(node)) { + dev_dbg(pispbe->dev, + "Cannot set capture fmt for output node %s\n", + NODE_NAME(node)); + return -EINVAL; + } + + pispbe_try_format(f, node); + + return 0; +} + +static int pispbe_node_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (!NODE_IS_OUTPUT(node) || NODE_IS_META(node)) { + dev_dbg(pispbe->dev, + "Cannot set capture fmt for output node %s\n", + NODE_NAME(node)); + return -EINVAL; + } + + pispbe_try_format(f, node); + + return 0; +} + +static int pispbe_node_try_fmt_meta_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (!NODE_IS_META(node) || NODE_IS_CAPTURE(node)) { + dev_dbg(pispbe->dev, + "Cannot set capture fmt for meta output node %s\n", + NODE_NAME(node)); + return -EINVAL; + } + + f->fmt.meta.dataformat = V4L2_META_FMT_RPI_BE_CFG; + f->fmt.meta.buffersize = sizeof(struct pisp_be_tiles_config); + + return 0; +} + +static int pispbe_node_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + int ret; + + ret = pispbe_node_try_fmt_vid_cap(file, priv, f); + if (ret < 0) + return ret; + + if (vb2_is_busy(&node->queue)) + return -EBUSY; + + node->format = *f; + node->pisp_format = pispbe_find_fmt(f->fmt.pix_mp.pixelformat); + + dev_dbg(pispbe->dev, "Set capture format for node %s to %p4cc\n", + NODE_NAME(node), &f->fmt.pix_mp.pixelformat); + + return 0; +} + +static int pispbe_node_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + int ret; + + ret = pispbe_node_try_fmt_vid_out(file, priv, f); + if (ret < 0) + return ret; + + if (vb2_is_busy(&node->queue)) + return -EBUSY; + + node->format = *f; + node->pisp_format = pispbe_find_fmt(f->fmt.pix_mp.pixelformat); + + dev_dbg(pispbe->dev, "Set output format for node %s to %p4cc\n", + NODE_NAME(node), &f->fmt.pix_mp.pixelformat); + + return 0; +} + +static int pispbe_node_s_fmt_meta_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + int ret; + + ret = pispbe_node_try_fmt_meta_out(file, priv, f); + if (ret < 0) + return ret; + + if (vb2_is_busy(&node->queue)) + return -EBUSY; + + node->format = *f; + node->pisp_format = &meta_out_supported_formats[0]; + + dev_dbg(pispbe->dev, "Set output format for meta node %s to %p4cc\n", + NODE_NAME(node), &f->fmt.meta.dataformat); + + return 0; +} + +static int pispbe_node_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct pispbe_node *node = video_drvdata(file); + + if (f->type != node->queue.type) + return -EINVAL; + + if (NODE_IS_META(node)) { + if (f->index) + return -EINVAL; + + f->pixelformat = V4L2_META_FMT_RPI_BE_CFG; + f->flags = 0; + return 0; + } + + if (f->index >= ARRAY_SIZE(supported_formats)) + return -EINVAL; + + f->pixelformat = supported_formats[f->index].fourcc; + f->flags = 0; + + return 0; +} + +static int pispbe_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct pispbe_node *node = video_drvdata(file); + struct pispbe_dev *pispbe = node->pispbe; + + if (NODE_IS_META(node) || fsize->index) + return -EINVAL; + + if (!pispbe_find_fmt(fsize->pixel_format)) { + dev_dbg(pispbe->dev, "Invalid pixel code: %x\n", + fsize->pixel_format); + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = 32; + fsize->stepwise.max_width = 65535; + fsize->stepwise.step_width = 2; + + fsize->stepwise.min_height = 32; + fsize->stepwise.max_height = 65535; + fsize->stepwise.step_height = 2; + + return 0; +} + +static const struct v4l2_ioctl_ops pispbe_node_ioctl_ops = { + .vidioc_querycap = pispbe_node_querycap, + .vidioc_g_fmt_vid_cap_mplane = pispbe_node_g_fmt_vid_cap, + .vidioc_g_fmt_vid_out_mplane = pispbe_node_g_fmt_vid_out, + .vidioc_g_fmt_meta_out = pispbe_node_g_fmt_meta_out, + .vidioc_try_fmt_vid_cap_mplane = pispbe_node_try_fmt_vid_cap, + .vidioc_try_fmt_vid_out_mplane = pispbe_node_try_fmt_vid_out, + .vidioc_try_fmt_meta_out = pispbe_node_try_fmt_meta_out, + .vidioc_s_fmt_vid_cap_mplane = pispbe_node_s_fmt_vid_cap, + .vidioc_s_fmt_vid_out_mplane = pispbe_node_s_fmt_vid_out, + .vidioc_s_fmt_meta_out = pispbe_node_s_fmt_meta_out, + .vidioc_enum_fmt_vid_cap = pispbe_node_enum_fmt, + .vidioc_enum_fmt_vid_out = pispbe_node_enum_fmt, + .vidioc_enum_fmt_meta_out = pispbe_node_enum_fmt, + .vidioc_enum_framesizes = pispbe_enum_framesizes, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static const struct video_device pispbe_videodev = { + .name = PISPBE_NAME, + .vfl_dir = VFL_DIR_M2M, /* gets overwritten */ + .fops = &pispbe_fops, + .ioctl_ops = &pispbe_node_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, +}; + +static void pispbe_node_def_fmt(struct pispbe_node *node) +{ + if (NODE_IS_META(node) && NODE_IS_OUTPUT(node)) { + /* Config node */ + struct v4l2_format *f = &node->format; + + f->fmt.meta.dataformat = V4L2_META_FMT_RPI_BE_CFG; + f->fmt.meta.buffersize = sizeof(struct pisp_be_tiles_config); + f->type = node->buf_type; + } else { + struct v4l2_format f = { + .fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420, + .fmt.pix_mp.width = 1920, + .fmt.pix_mp.height = 1080, + .type = node->buf_type, + }; + pispbe_try_format(&f, node); + node->format = f; + } + + node->pisp_format = pispbe_find_fmt(node->format.fmt.pix_mp.pixelformat); +} + +/* + * Initialise a struct pispbe_node and register it as /dev/video + * to represent one of the PiSP Back End's input or output streams. + */ +static int pispbe_init_node(struct pispbe_dev *pispbe, unsigned int id) +{ + bool output = NODE_DESC_IS_OUTPUT(&node_desc[id]); + struct pispbe_node *node = &pispbe->node[id]; + struct media_entity *entity = &node->vfd.entity; + struct video_device *vdev = &node->vfd; + struct vb2_queue *q = &node->queue; + int ret; + + node->id = id; + node->pispbe = pispbe; + node->buf_type = node_desc[id].buf_type; + + mutex_init(&node->node_lock); + mutex_init(&node->queue_lock); + INIT_LIST_HEAD(&node->ready_queue); + spin_lock_init(&node->ready_lock); + + node->format.type = node->buf_type; + pispbe_node_def_fmt(node); + + q->type = node->buf_type; + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->mem_ops = &vb2_dma_contig_memops; + q->drv_priv = node; + q->ops = &pispbe_node_queue_ops; + q->buf_struct_size = sizeof(struct pispbe_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->dev = pispbe->dev; + /* get V4L2 to handle node->queue locking */ + q->lock = &node->queue_lock; + + ret = vb2_queue_init(q); + if (ret < 0) { + dev_err(pispbe->dev, "vb2_queue_init failed\n"); + goto err_mutex_destroy; + } + + *vdev = pispbe_videodev; /* default initialization */ + strscpy(vdev->name, node_desc[id].ent_name, sizeof(vdev->name)); + vdev->v4l2_dev = &pispbe->v4l2_dev; + vdev->vfl_dir = output ? VFL_DIR_TX : VFL_DIR_RX; + /* get V4L2 to serialise our ioctls */ + vdev->lock = &node->node_lock; + vdev->queue = &node->queue; + vdev->device_caps = V4L2_CAP_STREAMING | node_desc[id].caps; + + node->pad.flags = output ? MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(entity, 1, &node->pad); + if (ret) { + dev_err(pispbe->dev, + "Failed to register media pads for %s device node\n", + NODE_NAME(node)); + goto err_unregister_queue; + } + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(pispbe->dev, + "Failed to register video %s device node\n", + NODE_NAME(node)); + goto err_unregister_queue; + } + video_set_drvdata(vdev, node); + + if (output) + ret = media_create_pad_link(entity, 0, &pispbe->sd.entity, + id, MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + else + ret = media_create_pad_link(&pispbe->sd.entity, id, entity, + 0, MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) + goto err_unregister_video_dev; + + dev_dbg(pispbe->dev, "%s device node registered as /dev/video%d\n", + NODE_NAME(node), node->vfd.num); + + return 0; + +err_unregister_video_dev: + video_unregister_device(&node->vfd); +err_unregister_queue: + vb2_queue_release(&node->queue); +err_mutex_destroy: + mutex_destroy(&node->node_lock); + mutex_destroy(&node->queue_lock); + return ret; +} + +static const struct v4l2_subdev_pad_ops pispbe_pad_ops = { + .link_validate = v4l2_subdev_link_validate_default, +}; + +static const struct v4l2_subdev_ops pispbe_sd_ops = { + .pad = &pispbe_pad_ops, +}; + +static int pispbe_init_subdev(struct pispbe_dev *pispbe) +{ + struct v4l2_subdev *sd = &pispbe->sd; + int ret; + + v4l2_subdev_init(sd, &pispbe_sd_ops); + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; + sd->owner = THIS_MODULE; + sd->dev = pispbe->dev; + strscpy(sd->name, PISPBE_NAME, sizeof(sd->name)); + + for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++) + pispbe->pad[i].flags = + NODE_DESC_IS_OUTPUT(&node_desc[i]) ? + MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&sd->entity, PISPBE_NUM_NODES, + pispbe->pad); + if (ret) + goto error; + + ret = v4l2_device_register_subdev(&pispbe->v4l2_dev, sd); + if (ret) + goto error; + + return 0; + +error: + media_entity_cleanup(&sd->entity); + return ret; +} + +static int pispbe_init_devices(struct pispbe_dev *pispbe) +{ + struct v4l2_device *v4l2_dev; + struct media_device *mdev; + unsigned int num_regist; + int ret; + + /* Register v4l2_device and media_device */ + mdev = &pispbe->mdev; + mdev->hw_revision = pispbe->hw_version; + mdev->dev = pispbe->dev; + strscpy(mdev->model, PISPBE_NAME, sizeof(mdev->model)); + media_device_init(mdev); + + v4l2_dev = &pispbe->v4l2_dev; + v4l2_dev->mdev = &pispbe->mdev; + strscpy(v4l2_dev->name, PISPBE_NAME, sizeof(v4l2_dev->name)); + + ret = v4l2_device_register(pispbe->dev, v4l2_dev); + if (ret) + goto err_media_dev_cleanup; + + /* Register the PISPBE subdevice. */ + ret = pispbe_init_subdev(pispbe); + if (ret) + goto err_unregister_v4l2; + + /* Create device video nodes */ + for (num_regist = 0; num_regist < PISPBE_NUM_NODES; num_regist++) { + ret = pispbe_init_node(pispbe, num_regist); + if (ret) + goto err_unregister_nodes; + } + + ret = media_device_register(mdev); + if (ret) + goto err_unregister_nodes; + + pispbe->config = + dma_alloc_coherent(pispbe->dev, + sizeof(struct pisp_be_tiles_config) * + PISP_BE_NUM_CONFIG_BUFFERS, + &pispbe->config_dma_addr, GFP_KERNEL); + if (!pispbe->config) { + dev_err(pispbe->dev, "Unable to allocate cached config buffers.\n"); + ret = -ENOMEM; + goto err_unregister_mdev; + } + + return 0; + +err_unregister_mdev: + media_device_unregister(mdev); +err_unregister_nodes: + while (num_regist-- > 0) { + video_unregister_device(&pispbe->node[num_regist].vfd); + vb2_queue_release(&pispbe->node[num_regist].queue); + } + v4l2_device_unregister_subdev(&pispbe->sd); + media_entity_cleanup(&pispbe->sd.entity); +err_unregister_v4l2: + v4l2_device_unregister(v4l2_dev); +err_media_dev_cleanup: + media_device_cleanup(mdev); + return ret; +} + +static void pispbe_destroy_devices(struct pispbe_dev *pispbe) +{ + if (pispbe->config) { + dma_free_coherent(pispbe->dev, + sizeof(struct pisp_be_tiles_config) * + PISP_BE_NUM_CONFIG_BUFFERS, + pispbe->config, + pispbe->config_dma_addr); + } + + dev_dbg(pispbe->dev, "Unregister from media controller\n"); + + v4l2_device_unregister_subdev(&pispbe->sd); + media_entity_cleanup(&pispbe->sd.entity); + media_device_unregister(&pispbe->mdev); + + for (int i = PISPBE_NUM_NODES - 1; i >= 0; i--) { + video_unregister_device(&pispbe->node[i].vfd); + vb2_queue_release(&pispbe->node[i].queue); + mutex_destroy(&pispbe->node[i].node_lock); + mutex_destroy(&pispbe->node[i].queue_lock); + } + + media_device_cleanup(&pispbe->mdev); + v4l2_device_unregister(&pispbe->v4l2_dev); +} + +static int pispbe_runtime_suspend(struct device *dev) +{ + struct pispbe_dev *pispbe = dev_get_drvdata(dev); + + clk_disable_unprepare(pispbe->clk); + + return 0; +} + +static int pispbe_runtime_resume(struct device *dev) +{ + struct pispbe_dev *pispbe = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(pispbe->clk); + if (ret) { + dev_err(dev, "Unable to enable clock\n"); + return ret; + } + + dev_dbg(dev, "%s: Enabled clock, rate=%lu\n", + __func__, clk_get_rate(pispbe->clk)); + + return 0; +} + +static int pispbe_hw_init(struct pispbe_dev *pispbe) +{ + u32 u; + + /* Check the HW is present and has a known version */ + u = pispbe_rd(pispbe, PISP_BE_VERSION_REG); + dev_dbg(pispbe->dev, "pispbe_probe: HW version: 0x%08x", u); + pispbe->hw_version = u; + if ((u & ~PISP_BE_VERSION_MINOR_BITS) != PISP_BE_VERSION_2712) + return -ENODEV; + + /* Clear leftover interrupts */ + pispbe_wr(pispbe, PISP_BE_INTERRUPT_STATUS_REG, 0xFFFFFFFFu); + u = pispbe_rd(pispbe, PISP_BE_BATCH_STATUS_REG); + dev_dbg(pispbe->dev, "pispbe_probe: BatchStatus: 0x%08x", u); + + pispbe->done = (uint8_t)u; + pispbe->started = (uint8_t)(u >> 8); + u = pispbe_rd(pispbe, PISP_BE_STATUS_REG); + dev_dbg(pispbe->dev, "pispbe_probe: Status: 0x%08x", u); + + if (u != 0 || pispbe->done != pispbe->started) { + dev_err(pispbe->dev, "pispbe_probe: HW is stuck or busy\n"); + return -EBUSY; + } + + /* + * AXI QOS=0, CACHE=4'b0010, PROT=3'b011 + * Also set "chicken bits" 22:20 which enable sub-64-byte bursts + * and AXI AWID/BID variability (on versions which support this). + */ + pispbe_wr(pispbe, PISP_BE_AXI_REG, 0x32703200u); + + /* Enable both interrupt flags */ + pispbe_wr(pispbe, PISP_BE_INTERRUPT_EN_REG, 0x00000003u); + + return 0; +} + +/* Probe the ISP-BE hardware block, as a single platform device. */ +static int pispbe_probe(struct platform_device *pdev) +{ + struct pispbe_dev *pispbe; + int ret; + + pispbe = devm_kzalloc(&pdev->dev, sizeof(*pispbe), GFP_KERNEL); + if (!pispbe) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, pispbe); + pispbe->dev = &pdev->dev; + platform_set_drvdata(pdev, pispbe); + + pispbe->be_reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pispbe->be_reg_base)) { + dev_err(&pdev->dev, "Failed to get ISP-BE registers address\n"); + return PTR_ERR(pispbe->be_reg_base); + } + + pispbe->irq = platform_get_irq(pdev, 0); + if (pispbe->irq <= 0) + return -EINVAL; + + ret = devm_request_irq(&pdev->dev, pispbe->irq, pispbe_isr, 0, + PISPBE_NAME, pispbe); + if (ret) { + dev_err(&pdev->dev, "Unable to request interrupt\n"); + return ret; + } + + ret = dma_set_mask_and_coherent(pispbe->dev, DMA_BIT_MASK(36)); + if (ret) + return ret; + + pispbe->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pispbe->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(pispbe->clk), + "Failed to get clock"); + + /* Hardware initialisation */ + pm_runtime_set_autosuspend_delay(pispbe->dev, 200); + pm_runtime_use_autosuspend(pispbe->dev); + pm_runtime_enable(pispbe->dev); + + ret = pispbe_runtime_resume(pispbe->dev); + if (ret) + goto pm_runtime_disable_err; + + pispbe->hw_busy = false; + spin_lock_init(&pispbe->hw_lock); + ret = pispbe_hw_init(pispbe); + if (ret) + goto pm_runtime_suspend_err; + + ret = pispbe_init_devices(pispbe); + if (ret) + goto disable_devs_err; + + pm_runtime_mark_last_busy(pispbe->dev); + pm_runtime_put_autosuspend(pispbe->dev); + + return 0; + +disable_devs_err: + pispbe_destroy_devices(pispbe); +pm_runtime_suspend_err: + pispbe_runtime_suspend(pispbe->dev); +pm_runtime_disable_err: + pm_runtime_dont_use_autosuspend(pispbe->dev); + pm_runtime_disable(pispbe->dev); + + return ret; +} + +static int pispbe_remove(struct platform_device *pdev) +{ + struct pispbe_dev *pispbe = platform_get_drvdata(pdev); + + pispbe_destroy_devices(pispbe); + + pispbe_runtime_suspend(pispbe->dev); + pm_runtime_dont_use_autosuspend(pispbe->dev); + pm_runtime_disable(pispbe->dev); + + return 0; +} + +static const struct dev_pm_ops pispbe_pm_ops = { + SET_RUNTIME_PM_OPS(pispbe_runtime_suspend, pispbe_runtime_resume, NULL) +}; + +static const struct of_device_id pispbe_of_match[] = { + { + .compatible = "raspberrypi,pispbe", + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, pispbe_of_match); + +static struct platform_driver pispbe_pdrv = { + .probe = pispbe_probe, + .remove = pispbe_remove, + .driver = { + .name = PISPBE_NAME, + .of_match_table = pispbe_of_match, + .pm = &pispbe_pm_ops, + }, +}; + +module_platform_driver(pispbe_pdrv); + +MODULE_DESCRIPTION("PiSP Back End driver"); +MODULE_AUTHOR("David Plowman "); +MODULE_AUTHOR("Nick Hollinghurst "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h new file mode 100644 index 000000000000..b5cb7b8c7531 --- /dev/null +++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h @@ -0,0 +1,519 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * PiSP Back End driver image format definitions. + * + * Copyright (c) 2021-2024 Raspberry Pi Ltd + */ + +#ifndef _PISP_BE_FORMATS_ +#define _PISP_BE_FORMATS_ + +#include +#include + +#define PISPBE_MAX_PLANES 3 +#define P3(x) ((x) * 8) + +struct pisp_be_format { + unsigned int fourcc; + unsigned int align; + unsigned int bit_depth; + /* 0P3 factor for plane sizing */ + unsigned int plane_factor[PISPBE_MAX_PLANES]; + unsigned int num_planes; + unsigned int colorspace_mask; + enum v4l2_colorspace colorspace_default; +}; + +#define V4L2_COLORSPACE_MASK(colorspace) BIT(colorspace) + +#define V4L2_COLORSPACE_MASK_JPEG \ + V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_JPEG) +#define V4L2_COLORSPACE_MASK_SMPTE170M \ + V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_SMPTE170M) +#define V4L2_COLORSPACE_MASK_REC709 \ + V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_REC709) +#define V4L2_COLORSPACE_MASK_SRGB \ + V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_SRGB) +#define V4L2_COLORSPACE_MASK_RAW \ + V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_RAW) + +/* + * All three colour spaces SRGB, SMPTE170M and REC709 are fundamentally sRGB + * underneath (as near as makes no difference to us), just with different YCbCr + * encodings. Therefore the ISP can generate sRGB on its main output and any of + * the others on its low resolution output. Applications should, when using both + * outputs, program the colour spaces on them to be the same, matching whatever + * is requested for the low resolution output, even if the main output is + * producing an RGB format. In turn this requires us to allow all these colour + * spaces for every YUV/RGB output format. + */ +#define V4L2_COLORSPACE_MASK_ALL_SRGB (V4L2_COLORSPACE_MASK_JPEG | \ + V4L2_COLORSPACE_MASK_SRGB | \ + V4L2_COLORSPACE_MASK_SMPTE170M | \ + V4L2_COLORSPACE_MASK_REC709) + +static const struct pisp_be_format supported_formats[] = { + /* Single plane YUV formats */ + { + .fourcc = V4L2_PIX_FMT_YUV420, + /* 128 alignment to ensure U/V planes are 64 byte aligned. */ + .align = 128, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.25), P3(0.25) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YVU420, + /* 128 alignment to ensure U/V planes are 64 byte aligned. */ + .align = 128, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.25), P3(0.25) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_NV12, + .align = 32, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.5) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_NV21, + .align = 32, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.5) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .align = 64, + .bit_depth = 16, + .plane_factor = { P3(1) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .align = 64, + .bit_depth = 16, + .plane_factor = { P3(1) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YVYU, + .align = 64, + .bit_depth = 16, + .plane_factor = { P3(1) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_VYUY, + .align = 64, + .bit_depth = 16, + .plane_factor = { P3(1) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + /* Multiplane YUV formats */ + { + .fourcc = V4L2_PIX_FMT_YUV420M, + .align = 64, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.25), P3(0.25) }, + .num_planes = 3, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_NV12M, + .align = 32, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.5) }, + .num_planes = 2, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_NV21M, + .align = 32, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.5) }, + .num_planes = 2, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YVU420M, + .align = 64, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.25), P3(0.25) }, + .num_planes = 3, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YUV422M, + .align = 64, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.5), P3(0.5) }, + .num_planes = 3, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YVU422M, + .align = 64, + .bit_depth = 8, + .plane_factor = { P3(1), P3(0.5), P3(0.5) }, + .num_planes = 3, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YUV444M, + .align = 64, + .bit_depth = 8, + .plane_factor = { P3(1), P3(1), P3(1) }, + .num_planes = 3, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + { + .fourcc = V4L2_PIX_FMT_YVU444M, + .align = 64, + .bit_depth = 8, + .plane_factor = { P3(1), P3(1), P3(1) }, + .num_planes = 3, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SMPTE170M, + }, + /* RGB formats */ + { + .fourcc = V4L2_PIX_FMT_RGB24, + .align = 32, + .bit_depth = 24, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SRGB, + }, + { + .fourcc = V4L2_PIX_FMT_BGR24, + .align = 32, + .bit_depth = 24, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SRGB, + }, + { + .fourcc = V4L2_PIX_FMT_XBGR32, + .align = 64, + .bit_depth = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SRGB, + }, + { + .fourcc = V4L2_PIX_FMT_RGBX32, + .align = 64, + .bit_depth = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SRGB, + }, + { + .fourcc = V4L2_PIX_FMT_RGB48, + .align = 64, + .bit_depth = 48, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SRGB, + }, + { + .fourcc = V4L2_PIX_FMT_BGR48, + .align = 64, + .bit_depth = 48, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB, + .colorspace_default = V4L2_COLORSPACE_SRGB, + }, + /* Bayer formats - 8-bit */ + { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + /* Bayer formats - 16-bit */ + { + .fourcc = V4L2_PIX_FMT_SRGGB16, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR16, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG16, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG16, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + /* Bayer formats unpacked to 16bpp */ + /* 10 bit */ + .fourcc = V4L2_PIX_FMT_SRGGB10, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + /* 12 bit */ + .fourcc = V4L2_PIX_FMT_SRGGB12, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + /* 14 bit */ + .fourcc = V4L2_PIX_FMT_SRGGB14, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR14, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG14, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG14, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + /* Bayer formats - 16-bit PiSP Compressed */ + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_BGGR, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_RGGB, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_GRBG, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_GBRG, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + /* Greyscale Formats */ + { + .fourcc = V4L2_PIX_FMT_GREY, + .bit_depth = 8, + .align = 32, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_Y16, + .bit_depth = 16, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, + { + .fourcc = V4L2_PIX_FMT_PISP_COMP1_MONO, + .bit_depth = 8, + .align = 32, + .plane_factor = { P3(1.0) }, + .num_planes = 1, + .colorspace_mask = V4L2_COLORSPACE_MASK_RAW, + .colorspace_default = V4L2_COLORSPACE_RAW, + }, +}; + +static const struct pisp_be_format meta_out_supported_formats[] = { + /* Configuration buffer format. */ + { + .fourcc = V4L2_META_FMT_RPI_BE_CFG, + }, +}; + +#endif /* _PISP_BE_FORMATS_ */ -- cgit v1.2.3 From 5b683b20309c0f45f2894ac0d01af25a61feffc5 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 26 Jun 2024 20:14:38 +0200 Subject: media: admin-guide: Document the Raspberry Pi PiSP BE Add documentation for the PiSP Back End memory-to-memory ISP. Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .../admin-guide/media/raspberrypi-pisp-be.dot | 20 ++++ .../admin-guide/media/raspberrypi-pisp-be.rst | 109 +++++++++++++++++++++ Documentation/admin-guide/media/v4l-drivers.rst | 1 + 3 files changed, 130 insertions(+) create mode 100644 Documentation/admin-guide/media/raspberrypi-pisp-be.dot create mode 100644 Documentation/admin-guide/media/raspberrypi-pisp-be.rst diff --git a/Documentation/admin-guide/media/raspberrypi-pisp-be.dot b/Documentation/admin-guide/media/raspberrypi-pisp-be.dot new file mode 100644 index 000000000000..55671dc1d443 --- /dev/null +++ b/Documentation/admin-guide/media/raspberrypi-pisp-be.dot @@ -0,0 +1,20 @@ +digraph board { + rankdir=TB + n00000001 [label="{{ 0 | 1 | 2 | 7} | pispbe\n | { 3 | 4 | 5 | 6}}", shape=Mrecord, style=filled, fillcolor=green] + n00000001:port3 -> n0000001c [style=bold] + n00000001:port4 -> n00000022 [style=bold] + n00000001:port5 -> n00000028 [style=bold] + n00000001:port6 -> n0000002e [style=bold] + n0000000a [label="pispbe-input\n/dev/video0", shape=box, style=filled, fillcolor=yellow] + n0000000a -> n00000001:port0 [style=bold] + n00000010 [label="pispbe-tdn_input\n/dev/video1", shape=box, style=filled, fillcolor=yellow] + n00000010 -> n00000001:port1 [style=bold] + n00000016 [label="pispbe-stitch_input\n/dev/video2", shape=box, style=filled, fillcolor=yellow] + n00000016 -> n00000001:port2 [style=bold] + n0000001c [label="pispbe-output0\n/dev/video3", shape=box, style=filled, fillcolor=yellow] + n00000022 [label="pispbe-output1\n/dev/video4", shape=box, style=filled, fillcolor=yellow] + n00000028 [label="pispbe-tdn_output\n/dev/video5", shape=box, style=filled, fillcolor=yellow] + n0000002e [label="pispbe-stitch_output\n/dev/video6", shape=box, style=filled, fillcolor=yellow] + n00000034 [label="pispbe-config\n/dev/video7", shape=box, style=filled, fillcolor=yellow] + n00000034 -> n00000001:port7 [style=bold] +} diff --git a/Documentation/admin-guide/media/raspberrypi-pisp-be.rst b/Documentation/admin-guide/media/raspberrypi-pisp-be.rst new file mode 100644 index 000000000000..0fcf46f26276 --- /dev/null +++ b/Documentation/admin-guide/media/raspberrypi-pisp-be.rst @@ -0,0 +1,109 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================================================= +Raspberry Pi PiSP Back End Memory-to-Memory ISP (pisp-be) +========================================================= + +The PiSP Back End +================= + +The PiSP Back End is a memory-to-memory Image Signal Processor (ISP) which reads +image data from DRAM memory and performs image processing as specified by the +application through the parameters in a configuration buffer, before writing +pixel data back to memory through two distinct output channels. + +The ISP registers and programming model are documented in the `Raspberry Pi +Image Signal Processor (PiSP) Specification document`_ + +The PiSP Back End ISP processes images in tiles. The handling of image +tessellation and the computation of low-level configuration parameters is +realized by a free software library called `libpisp +`_. + +The full image processing pipeline, which involves capturing RAW Bayer data from +an image sensor through a MIPI CSI-2 compatible capture interface, storing them +in DRAM memory and processing them in the PiSP Back End to obtain images usable +by an application is implemented in `libcamera `_ as +part of the Raspberry Pi platform support. + +The pisp-be driver +================== + +The Raspberry Pi PiSP Back End (pisp-be) driver is located under +drivers/media/platform/raspberrypi/pisp-be. It uses the `V4L2 API` to register +a number of video capture and output devices, the `V4L2 subdev API` to register +a subdevice for the ISP that connects the video devices in a single media graph +realized using the `Media Controller (MC) API`. + +The media topology registered by the `pisp-be` driver is represented below: + +.. _pips-be-topology: + +.. kernel-figure:: raspberrypi-pisp-be.dot + :alt: Diagram of the default media pipeline topology + :align: center + + +The media graph registers the following video device nodes: + +- pispbe-input: output device for images to be submitted to the ISP for + processing. +- pispbe-tdn_input: output device for temporal denoise. +- pispbe-stitch_input: output device for image stitching (HDR). +- pispbe-output0: first capture device for processed images. +- pispbe-output1: second capture device for processed images. +- pispbe-tdn_output: capture device for temporal denoise. +- pispbe-stitch_output: capture device for image stitching (HDR). +- pispbe-config: output device for ISP configuration parameters. + +pispbe-input +------------ + +Images to be processed by the ISP are queued to the `pispbe-input` output device +node. For a list of image formats supported as input to the ISP refer to the +`Raspberry Pi Image Signal Processor (PiSP) Specification document`_. + +pispbe-tdn_input, pispbe-tdn_output +----------------------------------- + +The `pispbe-tdn_input` output video device receives images to be processed by +the temporal denoise block which are captured from the `pispbe-tdn_output` +capture video device. Userspace is responsible for maintaining queues on both +devices, and ensuring that buffers completed on the output are queued to the +input. + +pispbe-stitch_input, pispbe-stitch_output +----------------------------------------- + +To realize HDR (high dynamic range) image processing the image stitching and +tonemapping blocks are used. The `pispbe-stitch_output` writes images to memory +and the `pispbe-stitch_input` receives the previously written frame to process +it along with the current input image. Userspace is responsible for maintaining +queues on both devices, and ensuring that buffers completed on the output are +queued to the input. + +pispbe-output0, pispbe-output1 +------------------------------ + +The two capture devices write to memory the pixel data as processed by the ISP. + +pispbe-config +------------- + +The `pispbe-config` output video devices receives a buffer of configuration +parameters that define the desired image processing to be performed by the ISP. + +The format of the ISP configuration parameter is defined by +:c:type:`pisp_be_tiles_config` C structure and the meaning of each parameter is +described in the `Raspberry Pi Image Signal Processor (PiSP) Specification +document`_. + +ISP configuration +================= + +The ISP configuration is described solely by the content of the parameters +buffer. The only parameter that userspace needs to configure using the V4L2 API +is the image format on the output and capture video devices for validation of +the content of the parameters buffer. + +.. _Raspberry Pi Image Signal Processor (PiSP) Specification document: https://datasheets.raspberrypi.com/camera/raspberry-pi-image-signal-processor-specification.pdf diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst index 4120eded9a13..b6af448b9fe9 100644 --- a/Documentation/admin-guide/media/v4l-drivers.rst +++ b/Documentation/admin-guide/media/v4l-drivers.rst @@ -23,6 +23,7 @@ Video4Linux (V4L) driver-specific documentation omap4_camera philips qcom_camss + raspberrypi-pisp-be rcar-fdp1 rkisp1 saa7134 -- cgit v1.2.3 From 77d32b7e2a7b2e5389b67363d25371b4b8cad140 Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Mon, 24 Jun 2024 10:41:22 +0200 Subject: media: stm32: dcmipp: correct error handling in dcmipp_create_subdevs Correct error handling within the dcmipp_create_subdevs by properly decrementing the i counter when releasing the subdeves. Fixes: 28e0f3772296 ("media: stm32-dcmipp: STM32 DCMIPP camera interface driver") Cc: stable@vger.kernel.org Signed-off-by: Alain Volmat Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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..4924ee36cfda 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c @@ -202,7 +202,7 @@ static int dcmipp_create_subdevs(struct dcmipp_device *dcmipp) return 0; err_init_entity: - while (i > 0) + while (i-- > 0) dcmipp->pipe_cfg->ents[i - 1].release(dcmipp->entity[i - 1]); return ret; } -- cgit v1.2.3 From 24d76ba94ca811e5d6c9731f468c420cfcfb272a Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Tue, 25 Jun 2024 10:29:54 +0200 Subject: media: i2c: gc2145: addition of RAW8 formats support Adds support for RAW8 formats (BGGR/RGGB/RBRG/BRBG). Signed-off-by: Alain Volmat Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil [hverkuil: fix typo: colospace -> colorspace] --- drivers/media/i2c/gc2145.c | 75 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 67 insertions(+), 8 deletions(-) diff --git a/drivers/media/i2c/gc2145.c b/drivers/media/i2c/gc2145.c index bef7b0e056a8..ef631c86080c 100644 --- a/drivers/media/i2c/gc2145.c +++ b/drivers/media/i2c/gc2145.c @@ -542,45 +542,82 @@ static const struct gc2145_mode supported_modes[] = { /** * struct gc2145_format - GC2145 pixel format description * @code: media bus (MBUS) associated code + * @colorspace: V4L2 colorspace * @datatype: MIPI CSI2 data type * @output_fmt: GC2145 output format * @switch_bit: GC2145 first/second switch + * @row_col_switch: GC2145 switch row and/or column */ struct gc2145_format { unsigned int code; + unsigned int colorspace; unsigned char datatype; unsigned char output_fmt; bool switch_bit; + unsigned char row_col_switch; }; /* All supported formats */ static const struct gc2145_format supported_formats[] = { { .code = MEDIA_BUS_FMT_UYVY8_1X16, + .colorspace = V4L2_COLORSPACE_SRGB, .datatype = MIPI_CSI2_DT_YUV422_8B, .output_fmt = 0x00, }, { .code = MEDIA_BUS_FMT_VYUY8_1X16, + .colorspace = V4L2_COLORSPACE_SRGB, .datatype = MIPI_CSI2_DT_YUV422_8B, .output_fmt = 0x01, }, { .code = MEDIA_BUS_FMT_YUYV8_1X16, + .colorspace = V4L2_COLORSPACE_SRGB, .datatype = MIPI_CSI2_DT_YUV422_8B, .output_fmt = 0x02, }, { .code = MEDIA_BUS_FMT_YVYU8_1X16, + .colorspace = V4L2_COLORSPACE_SRGB, .datatype = MIPI_CSI2_DT_YUV422_8B, .output_fmt = 0x03, }, { .code = MEDIA_BUS_FMT_RGB565_1X16, + .colorspace = V4L2_COLORSPACE_SRGB, .datatype = MIPI_CSI2_DT_RGB565, .output_fmt = 0x06, .switch_bit = true, }, + { + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .colorspace = V4L2_COLORSPACE_RAW, + .datatype = MIPI_CSI2_DT_RAW8, + .output_fmt = 0x17, + .row_col_switch = GC2145_SYNC_MODE_COL_SWITCH, + }, + { + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .colorspace = V4L2_COLORSPACE_RAW, + .datatype = MIPI_CSI2_DT_RAW8, + .output_fmt = 0x17, + .row_col_switch = GC2145_SYNC_MODE_COL_SWITCH | GC2145_SYNC_MODE_ROW_SWITCH, + }, + { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .colorspace = V4L2_COLORSPACE_RAW, + .datatype = MIPI_CSI2_DT_RAW8, + .output_fmt = 0x17, + .row_col_switch = 0, + }, + { + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .colorspace = V4L2_COLORSPACE_RAW, + .datatype = MIPI_CSI2_DT_RAW8, + .output_fmt = 0x17, + .row_col_switch = GC2145_SYNC_MODE_ROW_SWITCH, + }, }; struct gc2145_ctrls { @@ -641,7 +678,8 @@ gc2145_get_format_code(struct gc2145 *gc2145, u32 code) static void gc2145_update_pad_format(struct gc2145 *gc2145, const struct gc2145_mode *mode, - struct v4l2_mbus_framefmt *fmt, u32 code) + struct v4l2_mbus_framefmt *fmt, u32 code, + u32 colorspace) { fmt->code = code; fmt->width = mode->width; @@ -663,7 +701,8 @@ static int gc2145_init_state(struct v4l2_subdev *sd, /* Initialize pad format */ format = v4l2_subdev_state_get_format(state, 0); gc2145_update_pad_format(gc2145, &supported_modes[0], format, - MEDIA_BUS_FMT_RGB565_1X16); + MEDIA_BUS_FMT_RGB565_1X16, + V4L2_COLORSPACE_SRGB); /* Initialize crop rectangle. */ crop = v4l2_subdev_state_get_crop(state, 0); @@ -754,7 +793,13 @@ static int gc2145_set_pad_format(struct v4l2_subdev *sd, width, height, fmt->format.width, fmt->format.height); - gc2145_update_pad_format(gc2145, mode, &fmt->format, gc2145_fmt->code); + /* In RAW mode, VGA is not possible so use 720p instead */ + if (gc2145_fmt->colorspace == V4L2_COLORSPACE_RAW && + mode == &supported_modes[GC2145_MODE_640X480]) + mode = &supported_modes[GC2145_MODE_1280X720]; + + gc2145_update_pad_format(gc2145, mode, &fmt->format, gc2145_fmt->code, + gc2145_fmt->colorspace); framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { gc2145->mode = mode; @@ -811,7 +856,11 @@ static int gc2145_config_mipi_mode(struct gc2145 *gc2145, * For RAW8, LWC = image width * For RAW10, LWC = image width * 1.25 */ - lwc = gc2145->mode->width * 2; + if (gc2145_format->colorspace != V4L2_COLORSPACE_RAW) + lwc = gc2145->mode->width * 2; + else + lwc = gc2145->mode->width; + cci_write(gc2145->regmap, GC2145_REG_LWC_HIGH, lwc >> 8, &ret); cci_write(gc2145->regmap, GC2145_REG_LWC_LOW, lwc & 0xff, &ret); @@ -821,10 +870,14 @@ static int gc2145_config_mipi_mode(struct gc2145 *gc2145, * 1280x720 / 1600x1200 (aka no scaler) non RAW: 0x0001 * 1600x1200 RAW: 0x0190 */ - if (gc2145->mode->width == 1280 || gc2145->mode->width == 1600) - fifo_full_lvl = 0x0001; - else + if (gc2145_format->colorspace != V4L2_COLORSPACE_RAW) { + if (gc2145->mode->width == 1280 || gc2145->mode->width == 1600) + fifo_full_lvl = 0x0001; + else + fifo_full_lvl = 0x0190; + } else { fifo_full_lvl = 0x0190; + } cci_write(gc2145->regmap, GC2145_REG_FIFO_FULL_LVL_HIGH, fifo_full_lvl >> 8, &ret); @@ -835,7 +888,9 @@ static int gc2145_config_mipi_mode(struct gc2145 *gc2145, * Set the FIFO gate mode / MIPI wdiv set: * 0xf1 in case of RAW mode and 0xf0 otherwise */ - cci_write(gc2145->regmap, GC2145_REG_FIFO_GATE_MODE, 0xf0, &ret); + cci_write(gc2145->regmap, GC2145_REG_FIFO_GATE_MODE, + gc2145_format->colorspace == V4L2_COLORSPACE_RAW ? + 0xf1 : 0xf0, &ret); /* Set the MIPI data type */ cci_write(gc2145->regmap, GC2145_REG_MIPI_DT, @@ -883,6 +938,10 @@ static int gc2145_start_streaming(struct gc2145 *gc2145, GC2145_BYPASS_MODE_SWITCH, gc2145_format->switch_bit ? GC2145_BYPASS_MODE_SWITCH : 0, &ret); + cci_update_bits(gc2145->regmap, GC2145_REG_SYNC_MODE, + GC2145_SYNC_MODE_COL_SWITCH | + GC2145_SYNC_MODE_ROW_SWITCH, + gc2145_format->row_col_switch, &ret); if (ret) { dev_err(&client->dev, "%s failed to write regs\n", __func__); goto err_rpm_put; -- cgit v1.2.3 From 40f8c2bfa616ed486af5b9e9d14248e4172a5bdc Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Tue, 25 Jun 2024 10:29:55 +0200 Subject: media: i2c: gc2145: use CCI_REG16_LE for little-endian registers Use CCI_REG16_LE macro in order to access little-endian encoded registers of the P3 (CSI) section. Signed-off-by: Alain Volmat Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/i2c/gc2145.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/media/i2c/gc2145.c b/drivers/media/i2c/gc2145.c index ef631c86080c..667bb756d056 100644 --- a/drivers/media/i2c/gc2145.c +++ b/drivers/media/i2c/gc2145.c @@ -68,8 +68,7 @@ #define GC2145_DPHY_CLK_DELAY BIT(4) #define GC2145_DPHY_LANE0_DELAY BIT(5) #define GC2145_DPHY_LANE1_DELAY BIT(6) -#define GC2145_REG_FIFO_FULL_LVL_LOW CCI_REG8(0x04) -#define GC2145_REG_FIFO_FULL_LVL_HIGH CCI_REG8(0x05) +#define GC2145_REG_FIFO_FULL_LVL CCI_REG16_LE(0x04) #define GC2145_REG_FIFO_MODE CCI_REG8(0x06) #define GC2145_FIFO_MODE_READ_GATE BIT(3) #define GC2145_FIFO_MODE_MIPI_CLK_MODULE BIT(7) @@ -79,8 +78,7 @@ #define GC2145_CSI2_MODE_MIPI_EN BIT(4) #define GC2145_CSI2_MODE_EN BIT(7) #define GC2145_REG_MIPI_DT CCI_REG8(0x11) -#define GC2145_REG_LWC_LOW CCI_REG8(0x12) -#define GC2145_REG_LWC_HIGH CCI_REG8(0x13) +#define GC2145_REG_LWC CCI_REG16_LE(0x12) #define GC2145_REG_DPHY_MODE CCI_REG8(0x15) #define GC2145_DPHY_MODE_TRIGGER_PROG BIT(4) #define GC2145_REG_FIFO_GATE_MODE CCI_REG8(0x17) @@ -861,8 +859,7 @@ static int gc2145_config_mipi_mode(struct gc2145 *gc2145, else lwc = gc2145->mode->width; - cci_write(gc2145->regmap, GC2145_REG_LWC_HIGH, lwc >> 8, &ret); - cci_write(gc2145->regmap, GC2145_REG_LWC_LOW, lwc & 0xff, &ret); + cci_write(gc2145->regmap, GC2145_REG_LWC, lwc, &ret); /* * Adjust the MIPI FIFO Full Level @@ -879,10 +876,8 @@ static int gc2145_config_mipi_mode(struct gc2145 *gc2145, fifo_full_lvl = 0x0190; } - cci_write(gc2145->regmap, GC2145_REG_FIFO_FULL_LVL_HIGH, - fifo_full_lvl >> 8, &ret); - cci_write(gc2145->regmap, GC2145_REG_FIFO_FULL_LVL_LOW, - fifo_full_lvl & 0xff, &ret); + cci_write(gc2145->regmap, GC2145_REG_FIFO_FULL_LVL, + fifo_full_lvl, &ret); /* * Set the FIFO gate mode / MIPI wdiv set: -- cgit v1.2.3 From 7b9b9306cba0d60c6e8551366fba3bbbffb929e1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 20 Jun 2024 02:24:31 +0300 Subject: media: imx-mipi-csis: Use v4l2_subdev_enable_streams() To support sources that implement the .enable_streams() and .disable_streams() operations, replace the manual calls to the subdev .s_stream() operation with the v4l2_subdev_enable_streams() and v4l2_subdev_disable_streams() helpers. The helpers fall back to .s_stream() if the source doesn't implement the new operations, so backward compatibility is preserved. Signed-off-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Reviewed-by: Rui Miguel Silva Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx-mipi-csis.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c index f49b06978f14..b9729a8883d6 100644 --- a/drivers/media/platform/nxp/imx-mipi-csis.c +++ b/drivers/media/platform/nxp/imx-mipi-csis.c @@ -320,7 +320,11 @@ struct mipi_csis_device { struct v4l2_subdev sd; struct media_pad pads[CSIS_PADS_NUM]; struct v4l2_async_notifier notifier; - struct v4l2_subdev *src_sd; + + struct { + struct v4l2_subdev *sd; + const struct media_pad *pad; + } source; struct v4l2_mbus_config_mipi_csi2 bus; u32 clk_frequency; @@ -597,7 +601,7 @@ static int mipi_csis_calculate_params(struct mipi_csis_device *csis, u32 lane_rate; /* Calculate the line rate from the pixel rate. */ - link_freq = v4l2_get_link_freq(csis->src_sd->ctrl_handler, + link_freq = v4l2_get_link_freq(csis->source.sd->ctrl_handler, csis_fmt->width, csis->bus.num_data_lanes * 2); if (link_freq < 0) { @@ -958,7 +962,8 @@ static int mipi_csis_s_stream(struct v4l2_subdev *sd, int enable) int ret; if (!enable) { - v4l2_subdev_call(csis->src_sd, video, s_stream, 0); + v4l2_subdev_disable_streams(csis->source.sd, + csis->source.pad->index, BIT(0)); mipi_csis_stop_stream(csis); if (csis->debug.enable) @@ -986,7 +991,8 @@ static int mipi_csis_s_stream(struct v4l2_subdev *sd, int enable) mipi_csis_start_stream(csis, format, csis_fmt); - ret = v4l2_subdev_call(csis->src_sd, video, s_stream, 1); + ret = v4l2_subdev_enable_streams(csis->source.sd, + csis->source.pad->index, BIT(0)); if (ret < 0) goto err_stop; @@ -1233,12 +1239,14 @@ static int mipi_csis_link_setup(struct media_entity *entity, remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); if (flags & MEDIA_LNK_FL_ENABLED) { - if (csis->src_sd) + if (csis->source.sd) return -EBUSY; - csis->src_sd = remote_sd; + csis->source.sd = remote_sd; + csis->source.pad = remote_pad; } else { - csis->src_sd = NULL; + csis->source.sd = NULL; + csis->source.pad = NULL; } return 0; -- cgit v1.2.3 From 57dd8f2f77bc85374fc06962110cad0259a1a495 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 27 Jun 2024 13:15:31 +0200 Subject: Revert "media: stm32: dcmipp: correct error handling in dcmipp_create_subdevs" This reverts commit 77d32b7e2a7b2e5389b67363d25371b4b8cad140. This patch is obviously wrong (causes array accesses at index -1), and I caught that just too late. --- drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4924ee36cfda..4acc3b90d03a 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c @@ -202,7 +202,7 @@ static int dcmipp_create_subdevs(struct dcmipp_device *dcmipp) return 0; err_init_entity: - while (i-- > 0) + while (i > 0) dcmipp->pipe_cfg->ents[i - 1].release(dcmipp->entity[i - 1]); return ret; } -- cgit v1.2.3 From 2513996024de58855d452d96b52484e489524f91 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 24 Jun 2024 12:52:53 +0300 Subject: media: Documentation: vivid.rst: fix confusing section refs The documentation contained several instances of "section X" references, which no longer map to whatever X was. Replace these by the section titles. Also fix a single confusing typo in the "Radio & RDS Looping" section: "are regular frequency intervals" -> "at regular frequency intervals" Signed-off-by: Hans Verkuil --- Documentation/admin-guide/media/vivid.rst | 41 ++++++++++++++++--------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/Documentation/admin-guide/media/vivid.rst b/Documentation/admin-guide/media/vivid.rst index b6f658c0997e..04de7b69c817 100644 --- a/Documentation/admin-guide/media/vivid.rst +++ b/Documentation/admin-guide/media/vivid.rst @@ -313,10 +313,10 @@ Video Capture This is probably the most frequently used feature. The video capture device can be configured by using the module options num_inputs, input_types and -ccs_cap_mode (see section 1 for more detailed information), but by default -four inputs are configured: a webcam, a TV tuner, an S-Video and an HDMI -input, one input for each input type. Those are described in more detail -below. +ccs_cap_mode (see "Configuring the driver" for more detailed information), +but by default four inputs are configured: a webcam, a TV tuner, an S-Video +and an HDMI input, one input for each input type. Those are described in more +detail below. Special attention has been given to the rate at which new frames become available. The jitter will be around 1 jiffie (that depends on the HZ @@ -434,10 +434,10 @@ Video Output ------------ The video output device can be configured by using the module options -num_outputs, output_types and ccs_out_mode (see section 1 for more detailed -information), but by default two outputs are configured: an S-Video and an -HDMI input, one output for each output type. Those are described in more detail -below. +num_outputs, output_types and ccs_out_mode (see "Configuring the driver" +for more detailed information), but by default two outputs are configured: +an S-Video and an HDMI input, one output for each output type. Those are +described in more detail below. Like with video capture the framerate is also exact in the long term. @@ -1200,15 +1200,15 @@ and WSS (50 Hz formats) VBI data is looped. Teletext VBI data is not looped. Radio & RDS Looping ~~~~~~~~~~~~~~~~~~~ -As mentioned in section 6 the radio receiver emulates stations are regular -frequency intervals. Depending on the frequency of the radio receiver a -signal strength value is calculated (this is returned by VIDIOC_G_TUNER). -However, it will also look at the frequency set by the radio transmitter and -if that results in a higher signal strength than the settings of the radio -transmitter will be used as if it was a valid station. This also includes -the RDS data (if any) that the transmitter 'transmits'. This is received -faithfully on the receiver side. Note that when the driver is loaded the -frequencies of the radio receiver and transmitter are not identical, so +As mentioned in the "Radio Receiver" section, the radio receiver emulates +stations at regular frequency intervals. Depending on the frequency of the +radio receiver a signal strength value is calculated (this is returned by +VIDIOC_G_TUNER). However, it will also look at the frequency set by the radio +transmitter and if that results in a higher signal strength than the settings +of the radio transmitter will be used as if it was a valid station. This also +includes the RDS data (if any) that the transmitter 'transmits'. This is +received faithfully on the receiver side. Note that when the driver is loaded +the frequencies of the radio receiver and transmitter are not identical, so initially no looping takes place. @@ -1218,8 +1218,8 @@ Cropping, Composing, Scaling This driver supports cropping, composing and scaling in any combination. Normally which features are supported can be selected through the Vivid controls, but it is also possible to hardcode it when the module is loaded through the -ccs_cap_mode and ccs_out_mode module options. See section 1 on the details of -these module options. +ccs_cap_mode and ccs_out_mode module options. See "Configuring the driver" on +the details of these module options. This allows you to test your application for all these variations. @@ -1260,7 +1260,8 @@ is set, then the alpha component is only used for the color red and set to The driver has to be configured to support the multiplanar formats. By default the driver instances are single-planar. This can be changed by setting the -multiplanar module option, see section 1 for more details on that option. +multiplanar module option, see "Configuring the driver" for more details on that +option. If the driver instance is using the multiplanar formats/API, then the first single planar format (YUYV) and the multiplanar NV16M and NV61M formats the -- cgit v1.2.3 From 50e2eba54d0d865cbb7a4e03fd721d5b396083d1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 24 Jun 2024 12:52:54 +0300 Subject: media: Documentation: vivid.rst: drop "Video, VBI and RDS Looping" Drop the "Video, VBI and RDS Looping" section, instead moving the Video/VBI info to section "Video and Sliced VBI looping" and the RDS info to section "Radio & RDS Looping". Signed-off-by: Hans Verkuil --- Documentation/admin-guide/media/vivid.rst | 32 ++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/Documentation/admin-guide/media/vivid.rst b/Documentation/admin-guide/media/vivid.rst index 04de7b69c817..d7d5a01f4e59 100644 --- a/Documentation/admin-guide/media/vivid.rst +++ b/Documentation/admin-guide/media/vivid.rst @@ -1130,26 +1130,19 @@ Metadata Capture Controls if set, then the generated metadata stream contains Source Clock information. -Video, VBI and RDS Looping --------------------------- -The vivid driver supports looping of video output to video input, VBI output -to VBI input and RDS output to RDS input. For video/VBI looping this emulates -as if a cable was hooked up between the output and input connector. So video -and VBI looping is only supported between S-Video and HDMI inputs and outputs. -VBI is only valid for S-Video as it makes no sense for HDMI. +Video and Sliced VBI Looping +---------------------------- -Since radio is wireless this looping always happens if the radio receiver -frequency is close to the radio transmitter frequency. In that case the radio -transmitter will 'override' the emulated radio stations. +The vivid driver supports looping of video output to video input, and VBI +output to VBI input. For video/VBI looping this emulates as if a cable was +hooked up between the output and input connector. So video and VBI looping +is only supported between S-Video and HDMI inputs and outputs. +VBI is only valid for S-Video as it makes no sense for HDMI. Looping is currently supported only between devices created by the same vivid driver instance. - -Video and Sliced VBI looping -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - The way to enable video/VBI looping is currently fairly crude. A 'Loop Video' control is available in the "Vivid" control class of the video capture and VBI capture devices. When checked the video looping will be enabled. @@ -1198,7 +1191,16 @@ and WSS (50 Hz formats) VBI data is looped. Teletext VBI data is not looped. Radio & RDS Looping -~~~~~~~~~~~~~~~~~~~ +------------------- + +The vivid driver supports looping of RDS output to RDS input. + +Since radio is wireless this looping always happens if the radio receiver +frequency is close to the radio transmitter frequency. In that case the radio +transmitter will 'override' the emulated radio stations. + +RDS looping is currently supported only between devices created by the same +vivid driver instance. As mentioned in the "Radio Receiver" section, the radio receiver emulates stations at regular frequency intervals. Depending on the frequency of the -- cgit v1.2.3 From 3883822e17f7237d07da4970e642a50de991a76b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 24 Jun 2024 12:52:55 +0300 Subject: media: Documentation: vivid.rst: add supports_requests The module option supports_requests was not documented, add it. Signed-off-by: Hans Verkuil --- Documentation/admin-guide/media/vivid.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/admin-guide/media/vivid.rst b/Documentation/admin-guide/media/vivid.rst index d7d5a01f4e59..917b57225939 100644 --- a/Documentation/admin-guide/media/vivid.rst +++ b/Documentation/admin-guide/media/vivid.rst @@ -302,6 +302,15 @@ all configurable using the following module options: - 0: forbid hints - 1: allow hints +- supports_requests: + + specifies if the device should support the Request API. There are + three possible values, default is 1: + + - 0: no request + - 1: supports requests + - 2: requires requests + Taken together, all these module options allow you to precisely customize the driver behavior and test your application with all sorts of permutations. It is also very suitable to emulate hardware that is not yet available, e.g. -- cgit v1.2.3 From 0bc9574a7a2cac8cf05d1821081cc701e48e75fc Mon Sep 17 00:00:00 2001 From: Dorcas Anono Litunya Date: Mon, 24 Jun 2024 12:52:57 +0300 Subject: media: Documentation: vivid.rst: Remove documentation for Capture Overlay Modifying documentation to remove 'Capture Overlay section' as destructive capture overlay support was removed. See commit ccaa9d50ca73 ("media: vivid: drop overlay support") Signed-off-by: Dorcas Anono Litunya Signed-off-by: Hans Verkuil --- Documentation/admin-guide/media/vivid.rst | 68 ------------------------------- 1 file changed, 68 deletions(-) diff --git a/Documentation/admin-guide/media/vivid.rst b/Documentation/admin-guide/media/vivid.rst index 917b57225939..31fb86030249 100644 --- a/Documentation/admin-guide/media/vivid.rst +++ b/Documentation/admin-guide/media/vivid.rst @@ -1282,74 +1282,6 @@ data_offset to be non-zero, so this is a useful feature for testing applications Video output will also honor any data_offset that the application set. -Capture Overlay ---------------- - -Note: capture overlay support is implemented primarily to test the existing -V4L2 capture overlay API. In practice few if any GPUs support such overlays -anymore, and neither are they generally needed anymore since modern hardware -is so much more capable. By setting flag 0x10000 in the node_types module -option the vivid driver will create a simple framebuffer device that can be -used for testing this API. Whether this API should be used for new drivers is -questionable. - -This driver has support for a destructive capture overlay with bitmap clipping -and list clipping (up to 16 rectangles) capabilities. Overlays are not -supported for multiplanar formats. It also honors the struct v4l2_window field -setting: if it is set to FIELD_TOP or FIELD_BOTTOM and the capture setting is -FIELD_ALTERNATE, then only the top or bottom fields will be copied to the overlay. - -The overlay only works if you are also capturing at that same time. This is a -vivid limitation since it copies from a buffer to the overlay instead of -filling the overlay directly. And if you are not capturing, then no buffers -are available to fill. - -In addition, the pixelformat of the capture format and that of the framebuffer -must be the same for the overlay to work. Otherwise VIDIOC_OVERLAY will return -an error. - -In order to really see what it going on you will need to create two vivid -instances: the first with a framebuffer enabled. You configure the capture -overlay of the second instance to use the framebuffer of the first, then -you start capturing in the second instance. For the first instance you setup -the output overlay for the video output, turn on video looping and capture -to see the blended framebuffer overlay that's being written to by the second -instance. This setup would require the following commands: - -.. code-block:: none - - $ sudo modprobe vivid n_devs=2 node_types=0x10101,0x1 - $ v4l2-ctl -d1 --find-fb - /dev/fb1 is the framebuffer associated with base address 0x12800000 - $ sudo v4l2-ctl -d2 --set-fbuf fb=1 - $ v4l2-ctl -d1 --set-fbuf fb=1 - $ v4l2-ctl -d0 --set-fmt-video=pixelformat='AR15' - $ v4l2-ctl -d1 --set-fmt-video-out=pixelformat='AR15' - $ v4l2-ctl -d2 --set-fmt-video=pixelformat='AR15' - $ v4l2-ctl -d0 -i2 - $ v4l2-ctl -d2 -i2 - $ v4l2-ctl -d2 -c horizontal_movement=4 - $ v4l2-ctl -d1 --overlay=1 - $ v4l2-ctl -d0 -c loop_video=1 - $ v4l2-ctl -d2 --stream-mmap --overlay=1 - -And from another console: - -.. code-block:: none - - $ v4l2-ctl -d1 --stream-out-mmap - -And yet another console: - -.. code-block:: none - - $ qv4l2 - -and start streaming. - -As you can see, this is not for the faint of heart... - - Output Overlay -------------- -- cgit v1.2.3 From e03549dd025392bffc47264d53894e48a387b25d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 24 Jun 2024 12:52:58 +0300 Subject: media: vivid: vidioc_g_edid: do not change the original input EDID Returning an EDID for a connected output would modify the original input EDID with the physical address of the output. That causes problems, and it should just update the physical address of the output EDID. Update vivid_hdmi_edid to set the physical address to 0.0.0.0. Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vivid/vivid-core.c | 4 ++-- .../media/test-drivers/vivid/vivid-vid-common.c | 22 ++++++++++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c index 0273bc9863b0..4a9d9b30aa42 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.c +++ b/drivers/media/test-drivers/vivid/vivid-core.c @@ -218,7 +218,7 @@ static const u8 vivid_hdmi_edid[256] = { 0x5e, 0x5d, 0x10, 0x1f, 0x04, 0x13, 0x22, 0x21, 0x20, 0x05, 0x14, 0x02, 0x11, 0x01, 0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x6d, 0x03, - 0x0c, 0x00, 0x10, 0x00, 0x00, 0x3c, 0x21, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x21, 0x00, 0x60, 0x01, 0x02, 0x03, 0x67, 0xd8, 0x5d, 0xc4, 0x01, 0x78, 0x00, 0x00, 0xe2, 0x00, 0xca, 0xe3, 0x05, 0x00, 0x00, 0xe3, 0x06, 0x01, 0x00, 0x4d, @@ -229,7 +229,7 @@ static const u8 vivid_hdmi_edid[256] = { 0x00, 0x00, 0x1a, 0x1a, 0x1d, 0x00, 0x80, 0x51, 0xd0, 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, }; static int vidioc_querycap(struct file *file, void *priv, diff --git a/drivers/media/test-drivers/vivid/vivid-vid-common.c b/drivers/media/test-drivers/vivid/vivid-vid-common.c index 38d788b5cf19..a3e8eb90f11b 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-common.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-common.c @@ -1038,6 +1038,7 @@ int vidioc_g_edid(struct file *file, void *_fh, struct vivid_dev *dev = video_drvdata(file); struct video_device *vdev = video_devdata(file); struct cec_adapter *adap; + unsigned int loc; memset(edid->reserved, 0, sizeof(edid->reserved)); if (vdev->vfl_dir == VFL_DIR_RX) { @@ -1068,8 +1069,25 @@ int vidioc_g_edid(struct file *file, void *_fh, return -EINVAL; if (edid->blocks > dev->edid_blocks - edid->start_block) edid->blocks = dev->edid_blocks - edid->start_block; - if (adap) - v4l2_set_edid_phys_addr(dev->edid, dev->edid_blocks * 128, adap->phys_addr); + memcpy(edid->edid, dev->edid + edid->start_block * 128, edid->blocks * 128); + + loc = cec_get_edid_spa_location(dev->edid, dev->edid_blocks * 128); + if (vdev->vfl_dir == VFL_DIR_TX && adap && loc && + loc >= edid->start_block * 128 && + loc < (edid->start_block + edid->blocks) * 128) { + unsigned int i; + u8 sum = 0; + + loc -= edid->start_block * 128; + edid->edid[loc] = adap->phys_addr >> 8; + edid->edid[loc + 1] = adap->phys_addr & 0xff; + loc &= ~0x7f; + + /* update the checksum */ + for (i = loc; i < loc + 127; i++) + sum += edid->edid[i]; + edid->edid[i] = 256 - sum; + } return 0; } -- cgit v1.2.3 From 17763960b1784578e8fe915304b330922f646209 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 24 Jun 2024 12:52:59 +0300 Subject: media: vivid: don't set HDMI TX controls if there are no HDMI outputs When setting the EDID it would attempt to update two controls that are only present if there is an HDMI output configured. If there isn't any (e.g. when the vivid module is loaded with node_types=1), then calling VIDIOC_S_EDID would crash. Fix this by first checking if outputs are present. Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vivid/vivid-vid-cap.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index 3a3041a0378f..afa0dc5bcdae 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -1554,8 +1554,10 @@ int vidioc_s_edid(struct file *file, void *_fh, return -EINVAL; if (edid->blocks == 0) { dev->edid_blocks = 0; - v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, 0); - v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, 0); + if (dev->num_outputs) { + v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, 0); + v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, 0); + } phys_addr = CEC_PHYS_ADDR_INVALID; goto set_phys_addr; } @@ -1579,8 +1581,10 @@ int vidioc_s_edid(struct file *file, void *_fh, display_present |= dev->display_present[i] << j++; - v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, display_present); - v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, display_present); + if (dev->num_outputs) { + v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, display_present); + v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, display_present); + } set_phys_addr: /* TODO: a proper hotplug detect cycle should be emulated here */ -- cgit v1.2.3 From 3d023ff2ef03960a4072585c0a59e7c45d608487 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 24 Jun 2024 12:53:00 +0300 Subject: media: vivid: add instance number to input/output names Add the instance number before the input or output number. So "HDMI 1" becomes "HDMI 000-1". This is helps identifying which input or output belongs to which vivid instance. Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vivid/vivid-vid-cap.c | 16 ++++++++-------- drivers/media/test-drivers/vivid/vivid-vid-out.c | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index afa0dc5bcdae..b41a7c87aaa7 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -1073,13 +1073,13 @@ int vidioc_enum_input(struct file *file, void *priv, inp->type = V4L2_INPUT_TYPE_CAMERA; switch (dev->input_type[inp->index]) { case WEBCAM: - snprintf(inp->name, sizeof(inp->name), "Webcam %u", - dev->input_name_counter[inp->index]); + snprintf(inp->name, sizeof(inp->name), "Webcam %03u-%u", + dev->inst, dev->input_name_counter[inp->index]); inp->capabilities = 0; break; case TV: - snprintf(inp->name, sizeof(inp->name), "TV %u", - dev->input_name_counter[inp->index]); + snprintf(inp->name, sizeof(inp->name), "TV %03u-%u", + dev->inst, dev->input_name_counter[inp->index]); inp->type = V4L2_INPUT_TYPE_TUNER; inp->std = V4L2_STD_ALL; if (dev->has_audio_inputs) @@ -1087,16 +1087,16 @@ int vidioc_enum_input(struct file *file, void *priv, inp->capabilities = V4L2_IN_CAP_STD; break; case SVID: - snprintf(inp->name, sizeof(inp->name), "S-Video %u", - dev->input_name_counter[inp->index]); + snprintf(inp->name, sizeof(inp->name), "S-Video %03u-%u", + dev->inst, dev->input_name_counter[inp->index]); inp->std = V4L2_STD_ALL; if (dev->has_audio_inputs) inp->audioset = (1 << ARRAY_SIZE(vivid_audio_inputs)) - 1; inp->capabilities = V4L2_IN_CAP_STD; break; case HDMI: - snprintf(inp->name, sizeof(inp->name), "HDMI %u", - dev->input_name_counter[inp->index]); + snprintf(inp->name, sizeof(inp->name), "HDMI %03u-%u", + dev->inst, dev->input_name_counter[inp->index]); inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; if (dev->edid_blocks == 0 || dev->dv_timings_signal_mode[dev->input] == NO_SIGNAL) diff --git a/drivers/media/test-drivers/vivid/vivid-vid-out.c b/drivers/media/test-drivers/vivid/vivid-vid-out.c index 7a0f4c61ac80..5e47bdcb9923 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-out.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-out.c @@ -966,16 +966,16 @@ int vidioc_enum_output(struct file *file, void *priv, out->type = V4L2_OUTPUT_TYPE_ANALOG; switch (dev->output_type[out->index]) { case SVID: - snprintf(out->name, sizeof(out->name), "S-Video %u", - dev->output_name_counter[out->index]); + snprintf(out->name, sizeof(out->name), "S-Video %03u-%u", + dev->inst, dev->output_name_counter[out->index]); out->std = V4L2_STD_ALL; if (dev->has_audio_outputs) out->audioset = (1 << ARRAY_SIZE(vivid_audio_outputs)) - 1; out->capabilities = V4L2_OUT_CAP_STD; break; case HDMI: - snprintf(out->name, sizeof(out->name), "HDMI %u", - dev->output_name_counter[out->index]); + snprintf(out->name, sizeof(out->name), "HDMI %03u-%u", + dev->inst, dev->output_name_counter[out->index]); out->capabilities = V4L2_OUT_CAP_DV_TIMINGS; break; } -- cgit v1.2.3 From d7c969f37515d78dc1a026d12b94b90e319a5a61 Mon Sep 17 00:00:00 2001 From: Dorcas Anono Litunya Date: Mon, 24 Jun 2024 12:53:02 +0300 Subject: media: vivid: Add 'Is Connected To' menu controls The video loopback functionality in vivid is very crude. What we really want is to be able to emulate connecting an HDMI or S-Video input to an HDMI or S-Video output, and the input and output can be in different vivid instances. Effectively this emulates what happens when you physically connect an HDMI or S-Video cable between two devices. In particular, this makes prototyping with vivid much more realistic. This patch creates a menu control for each HDMI or S-Video input. The menu control starts with "Test Pattern Generator" and "None" (i.e. disconnected). After that up to 62 HDMI or S-Video outputs are listed that you can connect the input to. If there are more than 62 HDMI or S-Video outputs, then those will not be included in the menu (currently menucontrols have max 64 entries). If an input is connected to an output, then all other 'Connected To' controls are updated to exclude that output to avoid having multiple inputs connected to the same output. Signed-off-by: Dorcas Anono Litunya Signed-off-by: Hans Verkuil [hverkuil: use u8 in loops in vivid_create_controls() to avoid truncate warning] --- drivers/media/test-drivers/vivid/vivid-core.c | 207 +++++++++++++++++++++++-- drivers/media/test-drivers/vivid/vivid-core.h | 109 ++++++++++++- drivers/media/test-drivers/vivid/vivid-ctrls.c | 102 ++++++++++++ 3 files changed, 401 insertions(+), 17 deletions(-) diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c index 4a9d9b30aa42..7eb8a0a5d4d3 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.c +++ b/drivers/media/test-drivers/vivid/vivid-core.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only + /* * vivid-core.c - A Virtual Video Test Driver, core initialization * @@ -42,15 +43,13 @@ #include "vivid-touch-cap.h" #define VIVID_MODULE_NAME "vivid" - -/* The maximum number of vivid devices */ -#define VIVID_MAX_DEVS CONFIG_VIDEO_VIVID_MAX_DEVS +#define MAX_STRING_LENGTH 23 MODULE_DESCRIPTION("Virtual Video Test Driver"); MODULE_AUTHOR("Hans Verkuil"); MODULE_LICENSE("GPL"); -static unsigned n_devs = 1; +unsigned int n_devs = 1; module_param(n_devs, uint, 0444); MODULE_PARM_DESC(n_devs, " number of driver instances to create"); @@ -186,7 +185,31 @@ MODULE_PARM_DESC(supports_requests, " support for requests, default is 1.\n" "\t\t 1 == supports requests\n" "\t\t 2 == requires requests"); -static struct vivid_dev *vivid_devs[VIVID_MAX_DEVS]; +struct vivid_dev *vivid_devs[VIVID_MAX_DEVS]; + +DEFINE_SPINLOCK(hdmi_output_skip_mask_lock); +struct workqueue_struct *update_hdmi_ctrls_workqueue; +u64 hdmi_to_output_menu_skip_mask; + +struct vivid_dev *vivid_ctrl_hdmi_to_output_instance[MAX_MENU_ITEMS]; +unsigned int vivid_ctrl_hdmi_to_output_index[MAX_MENU_ITEMS]; + +char *vivid_ctrl_hdmi_to_output_strings[MAX_MENU_ITEMS + 1] = { + "Test Pattern Generator", + "None" +}; + +DEFINE_SPINLOCK(svid_output_skip_mask_lock); +struct workqueue_struct *update_svid_ctrls_workqueue; +u64 svid_to_output_menu_skip_mask; + +struct vivid_dev *vivid_ctrl_svid_to_output_instance[MAX_MENU_ITEMS]; +unsigned int vivid_ctrl_svid_to_output_index[MAX_MENU_ITEMS]; + +char *vivid_ctrl_svid_to_output_strings[MAX_MENU_ITEMS + 1] = { + "Test Pattern Generator", + "None" +}; const struct v4l2_rect vivid_min_rect = { 0, 0, MIN_WIDTH, MIN_HEIGHT @@ -827,6 +850,7 @@ static void vivid_dev_release(struct v4l2_device *v4l2_dev) { struct vivid_dev *dev = container_of(v4l2_dev, struct vivid_dev, v4l2_dev); + cancel_work_sync(&dev->update_hdmi_ctrl_work); vivid_free_controls(dev); v4l2_device_unregister(&dev->v4l2_dev); #ifdef CONFIG_MEDIA_CONTROLLER @@ -946,6 +970,7 @@ static int vivid_detect_feature_set(struct vivid_dev *dev, int inst, dev->num_inputs--; } dev->num_hdmi_inputs = in_type_counter[HDMI]; + dev->num_svid_inputs = in_type_counter[SVID]; /* how many outputs do we have and of what type? */ dev->num_outputs = num_outputs[inst]; @@ -1734,6 +1759,42 @@ static int vivid_create_devnodes(struct platform_device *pdev, return 0; } +static void update_hdmi_ctrls_work_handler(struct work_struct *work) +{ + u64 skip_mask; + + spin_lock(&hdmi_output_skip_mask_lock); + skip_mask = hdmi_to_output_menu_skip_mask; + spin_unlock(&hdmi_output_skip_mask_lock); + for (int i = 0; i < n_devs && vivid_devs[i]; i++) { + for (int j = 0; j < vivid_devs[i]->num_hdmi_inputs; j++) { + struct v4l2_ctrl *c = vivid_devs[i]->ctrl_hdmi_to_output[j]; + + v4l2_ctrl_modify_range(c, c->minimum, c->maximum, + skip_mask & ~(1ULL << c->cur.val), + c->default_value); + } + } +} + +static void update_svid_ctrls_work_handler(struct work_struct *work) +{ + u64 skip_mask; + + spin_lock(&svid_output_skip_mask_lock); + skip_mask = svid_to_output_menu_skip_mask; + spin_unlock(&svid_output_skip_mask_lock); + for (int i = 0; i < n_devs && vivid_devs[i]; i++) { + for (int j = 0; j < vivid_devs[i]->num_svid_inputs; j++) { + struct v4l2_ctrl *c = vivid_devs[i]->ctrl_svid_to_output[j]; + + v4l2_ctrl_modify_range(c, c->minimum, c->maximum, + skip_mask & ~(1ULL << c->cur.val), + c->default_value); + } + } +} + static int vivid_create_instance(struct platform_device *pdev, int inst) { static const struct v4l2_dv_timings def_dv_timings = @@ -1850,6 +1911,22 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) dev->edid_max_blocks = dev->edid_blocks = 2; memcpy(dev->edid, vivid_hdmi_edid, sizeof(vivid_hdmi_edid)); dev->radio_rds_init_time = ktime_get(); + INIT_WORK(&dev->update_hdmi_ctrl_work, update_hdmi_ctrls_work_handler); + INIT_WORK(&dev->update_svid_ctrl_work, update_svid_ctrls_work_handler); + for (int j = 0, k = 0; j < dev->num_inputs; ++j) + if (dev->input_type[j] == HDMI) + dev->hdmi_index_to_input_index[k++] = j; + for (int j = 0, k = 0; j < dev->num_outputs; ++j) + if (dev->output_type[j] == HDMI) { + dev->output_to_iface_index[j] = k; + dev->hdmi_index_to_output_index[k++] = j; + } + for (int j = 0, k = 0; j < dev->num_inputs; ++j) + if (dev->input_type[j] == SVID) + dev->svid_index_to_input_index[k++] = j; + for (int j = 0, k = 0; j < dev->num_outputs; ++j) + if (dev->output_type[j] == SVID) + dev->output_to_iface_index[j] = k++; /* create all controls */ ret = vivid_create_controls(dev, ccs_cap == -1, ccs_out == -1, no_error_inj, @@ -1986,7 +2063,7 @@ unreg_dev: vb2_video_unregister_device(&dev->vid_out_dev); vb2_video_unregister_device(&dev->vid_cap_dev); cec_unregister_adapter(dev->cec_rx_adap); - for (i = 0; i < MAX_OUTPUTS; i++) + for (i = 0; i < MAX_HDMI_OUTPUTS; i++) cec_unregister_adapter(dev->cec_tx_adap[i]); if (dev->kthread_cec) kthread_stop(dev->kthread_cec); @@ -2033,6 +2110,42 @@ static int vivid_probe(struct platform_device *pdev) /* n_devs will reflect the actual number of allocated devices */ n_devs = i; + /* Determine qmenu items actually in use */ + int hdmi_count = FIXED_MENU_ITEMS; + int svid_count = FIXED_MENU_ITEMS; + + for (int i = 0; i < n_devs; i++) { + struct vivid_dev *dev = vivid_devs[i]; + + if (!dev->has_vid_out) + continue; + for (int j = 0; j < dev->num_outputs && hdmi_count < MAX_MENU_ITEMS; ++j) { + if (dev->output_type[j] == HDMI) { + vivid_ctrl_hdmi_to_output_instance[hdmi_count] = vivid_devs[i]; + vivid_ctrl_hdmi_to_output_index[hdmi_count++] = j; + } + } + for (int j = 0; j < dev->num_outputs && svid_count < MAX_MENU_ITEMS; ++j) { + if (dev->output_type[j] == SVID) { + vivid_ctrl_svid_to_output_instance[svid_count] = vivid_devs[i]; + vivid_ctrl_svid_to_output_index[svid_count++] = j; + } + } + } + hdmi_count = min(hdmi_count, MAX_MENU_ITEMS); + svid_count = min(svid_count, MAX_MENU_ITEMS); + for (int i = 0; i < n_devs; i++) { + for (int j = 0; j < vivid_devs[i]->num_hdmi_inputs; j++) { + struct v4l2_ctrl *c = vivid_devs[i]->ctrl_hdmi_to_output[j]; + + v4l2_ctrl_modify_range(c, c->minimum, hdmi_count - 1, 0, c->default_value); + } + for (int j = 0; j < vivid_devs[i]->num_svid_inputs; j++) { + struct v4l2_ctrl *c = vivid_devs[i]->ctrl_svid_to_output[j]; + + v4l2_ctrl_modify_range(c, c->minimum, svid_count - 1, 0, c->default_value); + } + } return ret; } @@ -2109,7 +2222,7 @@ static void vivid_remove(struct platform_device *pdev) vb2_video_unregister_device(&dev->touch_cap_dev); } cec_unregister_adapter(dev->cec_rx_adap); - for (j = 0; j < MAX_OUTPUTS; j++) + for (j = 0; j < MAX_HDMI_OUTPUTS; j++) cec_unregister_adapter(dev->cec_tx_adap[j]); if (dev->kthread_cec) kthread_stop(dev->kthread_cec); @@ -2137,21 +2250,91 @@ static struct platform_driver vivid_pdrv = { static int __init vivid_init(void) { - int ret; - + int hdmi_count = FIXED_MENU_ITEMS; + int svid_count = FIXED_MENU_ITEMS; + int ret = -ENOMEM; + unsigned int ndevs; + + /* Sanity check, prevent insane number of vivid instances */ + if (n_devs > 64) + n_devs = 64; + ndevs = clamp_t(unsigned int, n_devs, 1, VIVID_MAX_DEVS); + + for (unsigned int i = 0; i < ndevs; i++) { + if (!(node_types[i] & (1 << 8))) + continue; + unsigned int n_outputs = min(num_outputs[i], MAX_OUTPUTS); + + for (u8 j = 0, k = 0; j < n_outputs && hdmi_count < MAX_MENU_ITEMS && + k < MAX_HDMI_OUTPUTS; ++j) { + if (output_types[i] & BIT(j)) { + vivid_ctrl_hdmi_to_output_strings[hdmi_count] = + kmalloc(MAX_STRING_LENGTH, GFP_KERNEL); + if (!vivid_ctrl_hdmi_to_output_strings[hdmi_count]) + goto free_output_strings; + snprintf(vivid_ctrl_hdmi_to_output_strings[hdmi_count], + MAX_STRING_LENGTH, "Output HDMI %03d-%d", + i & 0xff, k); + k++; + hdmi_count++; + } + } + for (u8 j = 0, k = 0; j < n_outputs && svid_count < MAX_MENU_ITEMS; ++j) { + if (!(output_types[i] & BIT(j))) { + vivid_ctrl_svid_to_output_strings[svid_count] = + kmalloc(MAX_STRING_LENGTH, GFP_KERNEL); + if (!vivid_ctrl_svid_to_output_strings[svid_count]) + goto free_output_strings; + snprintf(vivid_ctrl_svid_to_output_strings[svid_count], + MAX_STRING_LENGTH, "Output S-Video %03d-%d", + i & 0xff, k); + k++; + svid_count++; + } + } + } ret = platform_device_register(&vivid_pdev); if (ret) - return ret; - + goto free_output_strings; ret = platform_driver_register(&vivid_pdrv); if (ret) - platform_device_unregister(&vivid_pdev); + goto unreg_device; + + /* Initialize workqueue before module is loaded */ + update_hdmi_ctrls_workqueue = create_workqueue("update_hdmi_ctrls_wq"); + if (!update_hdmi_ctrls_workqueue) { + ret = -ENOMEM; + goto unreg_driver; + } + update_svid_ctrls_workqueue = create_workqueue("update_svid_ctrls_wq"); + if (!update_svid_ctrls_workqueue) { + ret = -ENOMEM; + goto destroy_hdmi_wq; + } + return ret; +destroy_hdmi_wq: + destroy_workqueue(update_hdmi_ctrls_workqueue); +unreg_driver: + platform_driver_register(&vivid_pdrv); +unreg_device: + platform_device_unregister(&vivid_pdev); +free_output_strings: + for (int i = FIXED_MENU_ITEMS; i < MAX_MENU_ITEMS; i++) { + kfree(vivid_ctrl_hdmi_to_output_strings[i]); + kfree(vivid_ctrl_svid_to_output_strings[i]); + } return ret; } static void __exit vivid_exit(void) { + for (int i = FIXED_MENU_ITEMS; i < MAX_MENU_ITEMS; i++) { + kfree(vivid_ctrl_hdmi_to_output_strings[i]); + kfree(vivid_ctrl_svid_to_output_strings[i]); + } + destroy_workqueue(update_svid_ctrls_workqueue); + destroy_workqueue(update_hdmi_ctrls_workqueue); platform_driver_unregister(&vivid_pdrv); platform_device_unregister(&vivid_pdev); } diff --git a/drivers/media/test-drivers/vivid/vivid-core.h b/drivers/media/test-drivers/vivid/vivid-core.h index cfb8e66083f6..b0c5b7369de5 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.h +++ b/drivers/media/test-drivers/vivid/vivid-core.h @@ -50,10 +50,87 @@ #define JIFFIES_PER_DAY (3600U * 24U * HZ) #define JIFFIES_RESYNC (JIFFIES_PER_DAY * (0xf0000000U / JIFFIES_PER_DAY)) +/* + * Maximum number of HDMI inputs allowed by vivid, due to limitations + * of the Physical Address in the EDID and used by CEC we stop at 15 + * inputs and outputs. + */ +#define MAX_HDMI_INPUTS 15 +#define MAX_HDMI_OUTPUTS 15 + +/* Maximum number of S-Video inputs allowed by vivid */ +#define MAX_SVID_INPUTS 16 + +/* The maximum number of items in a menu control */ +#define MAX_MENU_ITEMS BITS_PER_LONG_LONG + +/* Number of fixed menu items in the 'Connected To' menu controls */ +#define FIXED_MENU_ITEMS 2 + +/* The maximum number of vivid devices */ +#define VIVID_MAX_DEVS CONFIG_VIDEO_VIVID_MAX_DEVS + extern const struct v4l2_rect vivid_min_rect; extern const struct v4l2_rect vivid_max_rect; extern unsigned vivid_debug; +/* + * NULL-terminated string array for the HDMI 'Connected To' menu controls + * with the list of possible HDMI outputs. + * + * The first two items are fixed ("TPG" and "None"). + */ +extern char *vivid_ctrl_hdmi_to_output_strings[1 + MAX_MENU_ITEMS]; +/* Menu control skip mask of all HDMI outputs that are in use */ +extern u64 hdmi_to_output_menu_skip_mask; +/* Spinlock for access to hdmi_to_output_menu_skip_mask */ +extern spinlock_t hdmi_output_skip_mask_lock; +/* + * Workqueue that updates the menu controls whenever the HDMI menu skip mask + * changes. + */ +extern struct workqueue_struct *update_hdmi_ctrls_workqueue; + +/* + * The HDMI menu control value (index in the menu list) maps to an HDMI + * output that is part of the given vivid_dev instance and has the given + * output index (as returned by VIDIOC_G_OUTPUT). + * + * NULL/0 if not available. + */ +extern struct vivid_dev *vivid_ctrl_hdmi_to_output_instance[MAX_MENU_ITEMS]; +extern unsigned int vivid_ctrl_hdmi_to_output_index[MAX_MENU_ITEMS]; + +/* + * NULL-terminated string array for the S-Video 'Connected To' menu controls + * with the list of possible S-Video outputs. + * + * The first two items are fixed ("TPG" and "None"). + */ +extern char *vivid_ctrl_svid_to_output_strings[1 + MAX_MENU_ITEMS]; +/* Menu control skip mask of all S-Video outputs that are in use */ +extern u64 svid_to_output_menu_skip_mask; +/* Spinlock for access to svid_to_output_menu_skip_mask */ +extern spinlock_t svid_output_skip_mask_lock; +/* + * Workqueue that updates the menu controls whenever the S-Video menu skip mask + * changes. + */ +extern struct workqueue_struct *update_svid_ctrls_workqueue; + +/* + * The S-Video menu control value (index in the menu list) maps to an S-Video + * output that is part of the given vivid_dev instance and has the given + * output index (as returned by VIDIOC_G_OUTPUT). + * + * NULL/0 if not available. + */ +extern struct vivid_dev *vivid_ctrl_svid_to_output_instance[MAX_MENU_ITEMS]; +extern unsigned int vivid_ctrl_svid_to_output_index[MAX_MENU_ITEMS]; + +extern struct vivid_dev *vivid_devs[VIVID_MAX_DEVS]; +extern unsigned int n_devs; + struct vivid_fmt { u32 fourcc; /* v4l2 format id */ enum tgp_color_enc color_enc; @@ -118,7 +195,7 @@ struct vivid_cec_xfer { }; struct vivid_dev { - unsigned inst; + u8 inst; struct v4l2_device v4l2_dev; #ifdef CONFIG_MEDIA_CONTROLLER struct media_device mdev; @@ -161,6 +238,8 @@ struct vivid_dev { spinlock_t slock; struct mutex mutex; + struct work_struct update_hdmi_ctrl_work; + struct work_struct update_svid_ctrl_work; /* capabilities */ u32 vid_cap_caps; @@ -176,12 +255,13 @@ struct vivid_dev { /* supported features */ bool multiplanar; - unsigned num_inputs; - unsigned int num_hdmi_inputs; + u8 num_inputs; + u8 num_hdmi_inputs; + u8 num_svid_inputs; u8 input_type[MAX_INPUTS]; u8 input_name_counter[MAX_INPUTS]; - unsigned num_outputs; - unsigned int num_hdmi_outputs; + u8 num_outputs; + u8 num_hdmi_outputs; u8 output_type[MAX_OUTPUTS]; u8 output_name_counter[MAX_OUTPUTS]; bool has_audio_inputs; @@ -204,6 +284,20 @@ struct vivid_dev { bool has_touch_cap; bool can_loop_video; + /* Output index (0-MAX_OUTPUTS) to vivid instance of connected input */ + struct vivid_dev *output_to_input_instance[MAX_OUTPUTS]; + /* Output index (0-MAX_OUTPUTS) to input index (0-MAX_INPUTS) of connected input */ + u8 output_to_input_index[MAX_OUTPUTS]; + /* Output index (0-MAX_OUTPUTS) to HDMI or S-Video output index (0-MAX_HDMI/SVID_OUTPUTS) */ + u8 output_to_iface_index[MAX_OUTPUTS]; + /* ctrl_hdmi_to_output or ctrl_svid_to_output control value for each input */ + s32 input_is_connected_to_output[MAX_INPUTS]; + /* HDMI index (0-MAX_HDMI_OUTPUTS) to output index (0-MAX_OUTPUTS) */ + u8 hdmi_index_to_output_index[MAX_HDMI_OUTPUTS]; + /* HDMI index (0-MAX_HDMI_INPUTS) to input index (0-MAX_INPUTS) */ + u8 hdmi_index_to_input_index[MAX_HDMI_INPUTS]; + /* S-Video index (0-MAX_SVID_INPUTS) to input index (0-MAX_INPUTS) */ + u8 svid_index_to_input_index[MAX_SVID_INPUTS]; /* controls */ struct v4l2_ctrl *brightness; @@ -276,6 +370,11 @@ struct vivid_dev { struct v4l2_ctrl *radio_rx_rds_psname; struct v4l2_ctrl *radio_rx_rds_radiotext; + struct v4l2_ctrl *ctrl_hdmi_to_output[MAX_HDMI_INPUTS]; + char ctrl_hdmi_to_output_names[MAX_HDMI_INPUTS][32]; + struct v4l2_ctrl *ctrl_svid_to_output[MAX_SVID_INPUTS]; + char ctrl_svid_to_output_names[MAX_SVID_INPUTS][32]; + unsigned input_brightness[MAX_INPUTS]; unsigned osd_mode; unsigned button_pressed; diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c index f2b20e25a7a4..f713f10349b7 100644 --- a/drivers/media/test-drivers/vivid/vivid-ctrls.c +++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c @@ -104,6 +104,12 @@ #define VIVID_CID_META_CAP_GENERATE_PTS (VIVID_CID_VIVID_BASE + 111) #define VIVID_CID_META_CAP_GENERATE_SCR (VIVID_CID_VIVID_BASE + 112) +/* HDMI inputs are in the range 0-14. The next available CID is VIVID_CID_VIVID_BASE + 128 */ +#define VIVID_CID_HDMI_IS_CONNECTED_TO_OUTPUT(input) (VIVID_CID_VIVID_BASE + 113 + (input)) + +/* S-Video inputs are in the range 0-15. The next available CID is VIVID_CID_VIVID_BASE + 144 */ +#define VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(input) (VIVID_CID_VIVID_BASE + 128 + (input)) + /* General User Controls */ static void vivid_unregister_dev(bool valid, struct video_device *vdev) @@ -454,6 +460,10 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) }; struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_cap); unsigned int i, j; + struct vivid_dev *output_inst = NULL; + int index = 0; + int hdmi_index, svid_index; + s32 input_index = 0; switch (ctrl->id) { case VIVID_CID_TEST_PATTERN: @@ -604,6 +614,56 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) if (dev->edid_blocks > dev->edid_max_blocks) dev->edid_blocks = dev->edid_max_blocks; break; + case VIVID_CID_HDMI_IS_CONNECTED_TO_OUTPUT(0) ... VIVID_CID_HDMI_IS_CONNECTED_TO_OUTPUT(14): + hdmi_index = ctrl->id - VIVID_CID_HDMI_IS_CONNECTED_TO_OUTPUT(0); + output_inst = vivid_ctrl_hdmi_to_output_instance[ctrl->cur.val]; + index = vivid_ctrl_hdmi_to_output_index[ctrl->cur.val]; + input_index = dev->hdmi_index_to_input_index[hdmi_index]; + dev->input_is_connected_to_output[input_index] = ctrl->val; + + if (output_inst) + output_inst->output_to_input_instance[index] = NULL; + if (ctrl->val >= FIXED_MENU_ITEMS) { + output_inst = vivid_ctrl_hdmi_to_output_instance[ctrl->val]; + index = vivid_ctrl_hdmi_to_output_index[ctrl->val]; + output_inst->output_to_input_instance[index] = dev; + output_inst->output_to_input_index[index] = + dev->hdmi_index_to_input_index[hdmi_index]; + } + spin_lock(&hdmi_output_skip_mask_lock); + hdmi_to_output_menu_skip_mask &= ~(1ULL << ctrl->cur.val); + if (ctrl->val >= FIXED_MENU_ITEMS) + hdmi_to_output_menu_skip_mask |= 1ULL << ctrl->val; + spin_unlock(&hdmi_output_skip_mask_lock); + if (ctrl->val < FIXED_MENU_ITEMS && ctrl->cur.val < FIXED_MENU_ITEMS) + break; + queue_work(update_hdmi_ctrls_workqueue, &dev->update_hdmi_ctrl_work); + break; + case VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(0) ... VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(15): + svid_index = ctrl->id - VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(0); + output_inst = vivid_ctrl_svid_to_output_instance[ctrl->cur.val]; + index = vivid_ctrl_svid_to_output_index[ctrl->cur.val]; + input_index = dev->svid_index_to_input_index[svid_index]; + dev->input_is_connected_to_output[input_index] = ctrl->val; + + if (output_inst) + output_inst->output_to_input_instance[index] = NULL; + if (ctrl->val >= FIXED_MENU_ITEMS) { + output_inst = vivid_ctrl_svid_to_output_instance[ctrl->val]; + index = vivid_ctrl_svid_to_output_index[ctrl->val]; + output_inst->output_to_input_instance[index] = dev; + output_inst->output_to_input_index[index] = + dev->svid_index_to_input_index[svid_index]; + } + spin_lock(&svid_output_skip_mask_lock); + svid_to_output_menu_skip_mask &= ~(1ULL << ctrl->cur.val); + if (ctrl->val >= FIXED_MENU_ITEMS) + svid_to_output_menu_skip_mask |= 1ULL << ctrl->val; + spin_unlock(&svid_output_skip_mask_lock); + if (ctrl->val < FIXED_MENU_ITEMS && ctrl->cur.val < FIXED_MENU_ITEMS) + break; + queue_work(update_svid_ctrls_workqueue, &dev->update_svid_ctrl_work); + break; } return 0; } @@ -1710,6 +1770,48 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_insert_eav, NULL); v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_insert_hdmi_video_guard_band, NULL); v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_reduced_fps, NULL); + + WARN_ON(dev->num_hdmi_inputs > MAX_HDMI_INPUTS); + WARN_ON(dev->num_svid_inputs > MAX_SVID_INPUTS); + + for (u8 i = 0; i < dev->num_hdmi_inputs; i++) { + snprintf(dev->ctrl_hdmi_to_output_names[i], + sizeof(dev->ctrl_hdmi_to_output_names[i]), + "HDMI %03u-%u Is Connected To", dev->inst, i); + } + + for (u8 i = 0; i < dev->num_hdmi_inputs; i++) { + struct v4l2_ctrl_config ctrl_config = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_HDMI_IS_CONNECTED_TO_OUTPUT(i), + .name = dev->ctrl_hdmi_to_output_names[i], + .type = V4L2_CTRL_TYPE_MENU, + .max = 1, + .qmenu = (const char * const *)vivid_ctrl_hdmi_to_output_strings, + }; + dev->ctrl_hdmi_to_output[i] = v4l2_ctrl_new_custom(hdl_vid_cap, + &ctrl_config, NULL); + } + + for (u8 i = 0; i < dev->num_svid_inputs; i++) { + snprintf(dev->ctrl_svid_to_output_names[i], + sizeof(dev->ctrl_svid_to_output_names[i]), + "S-Video %03u-%u Is Connected To", dev->inst, i); + } + + for (u8 i = 0; i < dev->num_svid_inputs; i++) { + struct v4l2_ctrl_config ctrl_config = { + .ops = &vivid_vid_cap_ctrl_ops, + .id = VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(i), + .name = dev->ctrl_svid_to_output_names[i], + .type = V4L2_CTRL_TYPE_MENU, + .max = 1, + .qmenu = (const char * const *)vivid_ctrl_svid_to_output_strings, + }; + dev->ctrl_svid_to_output[i] = v4l2_ctrl_new_custom(hdl_vid_cap, + &ctrl_config, NULL); + } + if (show_ccs_cap) { dev->ctrl_has_crop_cap = v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_has_crop_cap, NULL); -- cgit v1.2.3 From 4c4dacb052d44b3af612e3e0c0e70a413b8dded0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 24 Jun 2024 12:53:04 +0300 Subject: media: vivid: loopback based on 'Connected To' controls Instead of using hardwired video loopback limited to a single vivid instance, use the new 'Connected To' controls to only loopback if an HDMI or S-Video input is connected to another output, which can be in another vivid instance. Effectively this emulates connecting and disconnecting an HDMI/S-Video cable. The Loop Video control is dropped since it has now been replaced by the new 'Connected To' controls. The Display Present has also been dropped since it no longer fits. Signed-off-by: Hans Verkuil Co-developed-by: Dorcas Anono Litunya Signed-off-by: Dorcas Anono Litunya --- Documentation/admin-guide/media/vivid.rst | 5 - drivers/media/test-drivers/vivid/vivid-cec.c | 88 ++++++++--- drivers/media/test-drivers/vivid/vivid-core.c | 39 +++-- drivers/media/test-drivers/vivid/vivid-core.h | 18 ++- drivers/media/test-drivers/vivid/vivid-ctrls.c | 162 +++++++-------------- .../media/test-drivers/vivid/vivid-kthread-cap.c | 86 +++++++---- drivers/media/test-drivers/vivid/vivid-vbi-cap.c | 5 +- drivers/media/test-drivers/vivid/vivid-vid-cap.c | 103 ++++++++----- drivers/media/test-drivers/vivid/vivid-vid-cap.h | 2 + .../media/test-drivers/vivid/vivid-vid-common.c | 114 ++++++++++----- .../media/test-drivers/vivid/vivid-vid-common.h | 5 +- drivers/media/test-drivers/vivid/vivid-vid-out.c | 17 +-- 12 files changed, 362 insertions(+), 282 deletions(-) diff --git a/Documentation/admin-guide/media/vivid.rst b/Documentation/admin-guide/media/vivid.rst index 31fb86030249..29481241d7cb 100644 --- a/Documentation/admin-guide/media/vivid.rst +++ b/Documentation/admin-guide/media/vivid.rst @@ -1020,11 +1020,6 @@ Digital Video Controls affects the reported colorspace since DVI_D outputs will always use sRGB. -- Display Present: - - sets the presence of a "display" on the HDMI output. This affects - the tx_edid_present, tx_hotplug and tx_rxsense controls. - FM Radio Receiver Controls ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/drivers/media/test-drivers/vivid/vivid-cec.c b/drivers/media/test-drivers/vivid/vivid-cec.c index 1f7469ff04d5..941ef4263214 100644 --- a/drivers/media/test-drivers/vivid/vivid-cec.c +++ b/drivers/media/test-drivers/vivid/vivid-cec.c @@ -23,7 +23,7 @@ struct xfer_on_bus { static bool find_dest_adap(struct vivid_dev *dev, struct cec_adapter *adap, u8 dest) { - unsigned int i; + unsigned int i, j; if (dest >= 0xf) return false; @@ -33,12 +33,29 @@ static bool find_dest_adap(struct vivid_dev *dev, cec_has_log_addr(dev->cec_rx_adap, dest)) return true; - for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) { - if (adap == dev->cec_tx_adap[i]) + for (i = 0, j = 0; i < dev->num_inputs; i++) { + unsigned int menu_idx = + dev->input_is_connected_to_output[i]; + + if (dev->input_type[i] != HDMI) + continue; + j++; + if (menu_idx < FIXED_MENU_ITEMS) + continue; + + struct vivid_dev *dev_tx = vivid_ctrl_hdmi_to_output_instance[menu_idx]; + unsigned int output = vivid_ctrl_hdmi_to_output_index[menu_idx]; + + if (!dev_tx) continue; - if (!dev->cec_tx_adap[i]->is_configured) + + unsigned int hdmi_output = dev_tx->output_to_iface_index[output]; + + if (adap == dev_tx->cec_tx_adap[hdmi_output]) + continue; + if (!dev_tx->cec_tx_adap[hdmi_output]->is_configured) continue; - if (cec_has_log_addr(dev->cec_tx_adap[i], dest)) + if (cec_has_log_addr(dev_tx->cec_tx_adap[hdmi_output], dest)) return true; } return false; @@ -96,7 +113,7 @@ static void adjust_sfts(struct vivid_dev *dev) int vivid_cec_bus_thread(void *_dev) { u32 last_sft; - unsigned int i; + unsigned int i, j; unsigned int dest; ktime_t start, end; s64 delta_us, retry_us; @@ -193,9 +210,27 @@ int vivid_cec_bus_thread(void *_dev) if (first_status == CEC_TX_STATUS_OK) { if (xfers_on_bus[first_idx].adap != dev->cec_rx_adap) cec_received_msg(dev->cec_rx_adap, &first_msg); - for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) - if (xfers_on_bus[first_idx].adap != dev->cec_tx_adap[i]) - cec_received_msg(dev->cec_tx_adap[i], &first_msg); + for (i = 0, j = 0; i < dev->num_inputs; i++) { + unsigned int menu_idx = + dev->input_is_connected_to_output[i]; + + if (dev->input_type[i] != HDMI) + continue; + j++; + if (menu_idx < FIXED_MENU_ITEMS) + continue; + + struct vivid_dev *dev_tx = vivid_ctrl_hdmi_to_output_instance[menu_idx]; + unsigned int output = vivid_ctrl_hdmi_to_output_index[menu_idx]; + + if (!dev_tx) + continue; + + unsigned int hdmi_output = dev_tx->output_to_iface_index[output]; + + if (xfers_on_bus[first_idx].adap != dev_tx->cec_tx_adap[hdmi_output]) + cec_received_msg(dev_tx->cec_tx_adap[hdmi_output], &first_msg); + } } end = ktime_get(); /* @@ -242,21 +277,36 @@ static int vivid_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, u32 signal_free_time, struct cec_msg *msg) { struct vivid_dev *dev = cec_get_drvdata(adap); + struct vivid_dev *dev_rx = dev; u8 idx = cec_msg_initiator(msg); + u8 output = 0; - spin_lock(&dev->cec_xfers_slock); - dev->xfers[idx].adap = adap; - memcpy(dev->xfers[idx].msg, msg->msg, CEC_MAX_MSG_SIZE); - dev->xfers[idx].len = msg->len; - dev->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_RETRY; + if (dev->cec_rx_adap != adap) { + int i; + + for (i = 0; i < dev->num_hdmi_outputs; i++) + if (dev->cec_tx_adap[i] == adap) + break; + if (i == dev->num_hdmi_outputs) + return -ENONET; + output = dev->hdmi_index_to_output_index[i]; + dev_rx = dev->output_to_input_instance[output]; + if (!dev_rx) + return -ENONET; + } + spin_lock(&dev_rx->cec_xfers_slock); + dev_rx->xfers[idx].adap = adap; + memcpy(dev_rx->xfers[idx].msg, msg->msg, CEC_MAX_MSG_SIZE); + dev_rx->xfers[idx].len = msg->len; + dev_rx->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_RETRY; if (signal_free_time > CEC_SIGNAL_FREE_TIME_RETRY) { - if (idx == dev->last_initiator) - dev->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEXT_XFER; + if (idx == dev_rx->last_initiator) + dev_rx->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEXT_XFER; else - dev->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR; + dev_rx->xfers[idx].sft = CEC_SIGNAL_FREE_TIME_NEW_INITIATOR; } - spin_unlock(&dev->cec_xfers_slock); - wake_up_interruptible(&dev->kthread_waitq_cec); + spin_unlock(&dev_rx->cec_xfers_slock); + wake_up_interruptible(&dev_rx->kthread_waitq_cec); return 0; } diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c index 7eb8a0a5d4d3..00e0d08af357 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.c +++ b/drivers/media/test-drivers/vivid/vivid-core.c @@ -190,6 +190,7 @@ struct vivid_dev *vivid_devs[VIVID_MAX_DEVS]; DEFINE_SPINLOCK(hdmi_output_skip_mask_lock); struct workqueue_struct *update_hdmi_ctrls_workqueue; u64 hdmi_to_output_menu_skip_mask; +u64 hdmi_input_update_outputs_mask; struct vivid_dev *vivid_ctrl_hdmi_to_output_instance[MAX_MENU_ITEMS]; unsigned int vivid_ctrl_hdmi_to_output_index[MAX_MENU_ITEMS]; @@ -985,7 +986,6 @@ static int vivid_detect_feature_set(struct vivid_dev *dev, int inst, for (i = 0; i < dev->num_outputs; i++) { dev->output_type[i] = ((output_types[inst] >> i) & 1) ? HDMI : SVID; dev->output_name_counter[i] = out_type_counter[dev->output_type[i]]++; - dev->display_present[i] = true; } dev->has_audio_outputs = out_type_counter[SVID]; if (out_type_counter[HDMI] == 16) { @@ -1418,7 +1418,6 @@ static int vivid_create_queues(struct vivid_dev *dev) static int vivid_create_devnodes(struct platform_device *pdev, struct vivid_dev *dev, int inst, - unsigned int cec_tx_bus_cnt, v4l2_std_id tvnorms_cap, v4l2_std_id tvnorms_out, unsigned in_type_counter[4], @@ -1462,7 +1461,7 @@ static int vivid_create_devnodes(struct platform_device *pdev, return ret; } cec_s_phys_addr(dev->cec_rx_adap, 0, false); - v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI input 0\n", + v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI input\n", dev_name(&dev->cec_rx_adap->devnode.dev)); } #endif @@ -1505,10 +1504,10 @@ static int vivid_create_devnodes(struct platform_device *pdev, #endif #ifdef CONFIG_VIDEO_VIVID_CEC - for (i = 0; i < cec_tx_bus_cnt; i++) { + for (i = 0; i < dev->num_hdmi_outputs; i++) { ret = cec_register_adapter(dev->cec_tx_adap[i], &pdev->dev); if (ret < 0) { - for (; i < cec_tx_bus_cnt; i++) { + for (; i >= 0; i--) { cec_delete_adapter(dev->cec_tx_adap[i]); dev->cec_tx_adap[i] = NULL; } @@ -1516,10 +1515,6 @@ static int vivid_create_devnodes(struct platform_device *pdev, } v4l2_info(&dev->v4l2_dev, "CEC adapter %s registered for HDMI output %d\n", dev_name(&dev->cec_tx_adap[i]->devnode.dev), i); - if (i < out_type_counter[HDMI]) - cec_s_phys_addr(dev->cec_tx_adap[i], (i + 1) << 12, false); - else - cec_s_phys_addr(dev->cec_tx_adap[i], 0x1000, false); } #endif @@ -1762,11 +1757,16 @@ static int vivid_create_devnodes(struct platform_device *pdev, static void update_hdmi_ctrls_work_handler(struct work_struct *work) { u64 skip_mask; + u64 update_mask; spin_lock(&hdmi_output_skip_mask_lock); skip_mask = hdmi_to_output_menu_skip_mask; + update_mask = hdmi_input_update_outputs_mask; + hdmi_input_update_outputs_mask = 0; spin_unlock(&hdmi_output_skip_mask_lock); for (int i = 0; i < n_devs && vivid_devs[i]; i++) { + if (update_mask & (1 << i)) + vivid_update_connected_outputs(vivid_devs[i]); for (int j = 0; j < vivid_devs[i]->num_hdmi_inputs; j++) { struct v4l2_ctrl *c = vivid_devs[i]->ctrl_hdmi_to_output[j]; @@ -1808,7 +1808,6 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) struct vivid_dev *dev; unsigned node_type = node_types[inst]; v4l2_std_id tvnorms_cap = 0, tvnorms_out = 0; - unsigned int cec_tx_bus_cnt = 0; int ret; int i; @@ -1937,8 +1936,6 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) goto unreg_dev; /* enable/disable interface specific controls */ - if (dev->num_outputs && dev->output_type[0] != HDMI) - v4l2_ctrl_activate(dev->ctrl_display_present, false); if (dev->num_inputs && dev->input_type[0] != HDMI) { v4l2_ctrl_activate(dev->ctrl_dv_timings_signal_mode, false); v4l2_ctrl_activate(dev->ctrl_dv_timings, false); @@ -1994,27 +1991,27 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) } if (dev->has_vid_out) { - for (i = 0; i < dev->num_outputs; i++) { + int j; + + for (i = j = 0; i < dev->num_outputs; i++) { struct cec_adapter *adap; if (dev->output_type[i] != HDMI) continue; - dev->cec_output2bus_map[i] = cec_tx_bus_cnt; - adap = vivid_cec_alloc_adap(dev, cec_tx_bus_cnt, true); + adap = vivid_cec_alloc_adap(dev, j, true); ret = PTR_ERR_OR_ZERO(adap); if (ret < 0) { - for (i = 0; i < dev->num_outputs; i++) - cec_delete_adapter(dev->cec_tx_adap[i]); + while (j--) + cec_delete_adapter(dev->cec_tx_adap[j]); goto unreg_dev; } - dev->cec_tx_adap[cec_tx_bus_cnt] = adap; - cec_tx_bus_cnt++; + dev->cec_tx_adap[j++] = adap; } } - if (dev->cec_rx_adap || cec_tx_bus_cnt) { + if (dev->cec_rx_adap || dev->num_hdmi_outputs) { init_waitqueue_head(&dev->kthread_waitq_cec); dev->kthread_cec = kthread_run(vivid_cec_bus_thread, dev, "vivid_cec-%s", dev->v4l2_dev.name); @@ -2040,7 +2037,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) v4l2_ctrl_handler_setup(&dev->ctrl_hdl_touch_cap); /* finally start creating the device nodes */ - ret = vivid_create_devnodes(pdev, dev, inst, cec_tx_bus_cnt, + ret = vivid_create_devnodes(pdev, dev, inst, tvnorms_cap, tvnorms_out, in_type_counter, out_type_counter); if (ret) diff --git a/drivers/media/test-drivers/vivid/vivid-core.h b/drivers/media/test-drivers/vivid/vivid-core.h index b0c5b7369de5..cc18a3bc6dc0 100644 --- a/drivers/media/test-drivers/vivid/vivid-core.h +++ b/drivers/media/test-drivers/vivid/vivid-core.h @@ -83,7 +83,15 @@ extern unsigned vivid_debug; extern char *vivid_ctrl_hdmi_to_output_strings[1 + MAX_MENU_ITEMS]; /* Menu control skip mask of all HDMI outputs that are in use */ extern u64 hdmi_to_output_menu_skip_mask; -/* Spinlock for access to hdmi_to_output_menu_skip_mask */ +/* + * Bitmask of which vivid instances need to update any connected + * HDMI outputs. + */ +extern u64 hdmi_input_update_outputs_mask; +/* + * Spinlock for access to hdmi_to_output_menu_skip_mask and + * hdmi_input_update_outputs_mask. + */ extern spinlock_t hdmi_output_skip_mask_lock; /* * Workqueue that updates the menu controls whenever the HDMI menu skip mask @@ -283,7 +291,6 @@ struct vivid_dev { bool has_tv_tuner; bool has_touch_cap; - bool can_loop_video; /* Output index (0-MAX_OUTPUTS) to vivid instance of connected input */ struct vivid_dev *output_to_input_instance[MAX_OUTPUTS]; /* Output index (0-MAX_OUTPUTS) to input index (0-MAX_INPUTS) of connected input */ @@ -336,7 +343,6 @@ struct vivid_dev { struct v4l2_ctrl *ctrl_dv_timings_signal_mode; struct v4l2_ctrl *ctrl_dv_timings; }; - struct v4l2_ctrl *ctrl_display_present; struct v4l2_ctrl *ctrl_has_crop_cap; struct v4l2_ctrl *ctrl_has_compose_cap; struct v4l2_ctrl *ctrl_has_scaler_cap; @@ -463,7 +469,6 @@ struct vivid_dev { u8 *scaled_line; u8 *blended_line; unsigned cur_scaled_line; - bool display_present[MAX_OUTPUTS]; /* Output Overlay */ void *fb_vbase_out; @@ -643,11 +648,10 @@ struct vivid_dev { /* CEC */ struct cec_adapter *cec_rx_adap; - struct cec_adapter *cec_tx_adap[MAX_OUTPUTS]; - u8 cec_output2bus_map[MAX_OUTPUTS]; + struct cec_adapter *cec_tx_adap[MAX_HDMI_OUTPUTS]; struct task_struct *kthread_cec; wait_queue_head_t kthread_waitq_cec; - struct vivid_cec_xfer xfers[MAX_OUTPUTS]; + struct vivid_cec_xfer xfers[MAX_OUTPUTS]; spinlock_t cec_xfers_slock; /* read and write cec messages */ u32 cec_sft; /* bus signal free time, in bit periods */ u8 last_initiator; diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c index f713f10349b7..8bb38bc7b8cc 100644 --- a/drivers/media/test-drivers/vivid/vivid-ctrls.c +++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c @@ -18,7 +18,6 @@ #include "vivid-radio-common.h" #include "vivid-osd.h" #include "vivid-ctrls.h" -#include "vivid-cec.h" #define VIVID_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000) #define VIVID_CID_BUTTON (VIVID_CID_CUSTOM_BASE + 0) @@ -69,14 +68,12 @@ #define VIVID_CID_HAS_CROP_OUT (VIVID_CID_VIVID_BASE + 34) #define VIVID_CID_HAS_COMPOSE_OUT (VIVID_CID_VIVID_BASE + 35) #define VIVID_CID_HAS_SCALER_OUT (VIVID_CID_VIVID_BASE + 36) -#define VIVID_CID_LOOP_VIDEO (VIVID_CID_VIVID_BASE + 37) #define VIVID_CID_SEQ_WRAP (VIVID_CID_VIVID_BASE + 38) #define VIVID_CID_TIME_WRAP (VIVID_CID_VIVID_BASE + 39) #define VIVID_CID_MAX_EDID_BLOCKS (VIVID_CID_VIVID_BASE + 40) #define VIVID_CID_PERCENTAGE_FILL (VIVID_CID_VIVID_BASE + 41) #define VIVID_CID_REDUCED_FPS (VIVID_CID_VIVID_BASE + 42) #define VIVID_CID_HSV_ENC (VIVID_CID_VIVID_BASE + 43) -#define VIVID_CID_DISPLAY_PRESENT (VIVID_CID_VIVID_BASE + 44) #define VIVID_CID_STD_SIGNAL_MODE (VIVID_CID_VIVID_BASE + 60) #define VIVID_CID_STANDARD (VIVID_CID_VIVID_BASE + 61) @@ -445,6 +442,33 @@ static const struct v4l2_ctrl_ops vivid_user_vid_ctrl_ops = { /* Video Capture Controls */ +static void vivid_update_power_present(struct vivid_dev *dev) +{ + unsigned int i, j; + + dev->power_present = 0; + for (i = 0, j = 0; + i < ARRAY_SIZE(dev->dv_timings_signal_mode); i++) { + if (dev->input_type[i] != HDMI) + continue; + /* + * If connected to TPG or HDMI output, and the signal + * mode is not NO_SIGNAL, then there is power present. + */ + if (dev->input_is_connected_to_output[i] != 1 && + dev->dv_timings_signal_mode[i] != NO_SIGNAL) + dev->power_present |= (1 << j); + j++; + } + + __v4l2_ctrl_s_ctrl(dev->ctrl_rx_power_present, + dev->power_present); + + v4l2_ctrl_activate(dev->ctrl_dv_timings, + dev->dv_timings_signal_mode[dev->input] == + SELECTED_DV_TIMINGS); +} + static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) { static const u32 colorspaces[] = { @@ -459,7 +483,7 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) V4L2_COLORSPACE_470_SYSTEM_BG, }; struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_cap); - unsigned int i, j; + unsigned int i; struct vivid_dev *output_inst = NULL; int index = 0; int hdmi_index, svid_index; @@ -579,25 +603,9 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) dev->dv_timings_signal_mode[dev->input] = dev->ctrl_dv_timings_signal_mode->val; dev->query_dv_timings[dev->input] = dev->ctrl_dv_timings->val; - - dev->power_present = 0; - for (i = 0, j = 0; - i < ARRAY_SIZE(dev->dv_timings_signal_mode); - i++) - if (dev->input_type[i] == HDMI) { - if (dev->dv_timings_signal_mode[i] != NO_SIGNAL) - dev->power_present |= (1 << j); - j++; - } - __v4l2_ctrl_s_ctrl(dev->ctrl_rx_power_present, - dev->power_present); - - v4l2_ctrl_activate(dev->ctrl_dv_timings, - dev->dv_timings_signal_mode[dev->input] == - SELECTED_DV_TIMINGS); - + vivid_update_power_present(dev); vivid_update_quality(dev); - vivid_send_source_change(dev, HDMI); + vivid_send_input_source_change(dev, dev->input); break; case VIVID_CID_DV_TIMINGS_ASPECT_RATIO: dev->dv_timings_aspect_ratio[dev->input] = ctrl->val; @@ -621,8 +629,11 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) input_index = dev->hdmi_index_to_input_index[hdmi_index]; dev->input_is_connected_to_output[input_index] = ctrl->val; - if (output_inst) + if (output_inst) { output_inst->output_to_input_instance[index] = NULL; + vivid_update_outputs(output_inst); + cec_phys_addr_invalidate(output_inst->cec_tx_adap[index]); + } if (ctrl->val >= FIXED_MENU_ITEMS) { output_inst = vivid_ctrl_hdmi_to_output_instance[ctrl->val]; index = vivid_ctrl_hdmi_to_output_index[ctrl->val]; @@ -635,8 +646,14 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) if (ctrl->val >= FIXED_MENU_ITEMS) hdmi_to_output_menu_skip_mask |= 1ULL << ctrl->val; spin_unlock(&hdmi_output_skip_mask_lock); + vivid_update_power_present(dev); + vivid_update_quality(dev); + vivid_send_input_source_change(dev, dev->hdmi_index_to_input_index[hdmi_index]); if (ctrl->val < FIXED_MENU_ITEMS && ctrl->cur.val < FIXED_MENU_ITEMS) break; + spin_lock(&hdmi_output_skip_mask_lock); + hdmi_input_update_outputs_mask |= 1 << dev->inst; + spin_unlock(&hdmi_output_skip_mask_lock); queue_work(update_hdmi_ctrls_workqueue, &dev->update_hdmi_ctrl_work); break; case VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(0) ... VIVID_CID_SVID_IS_CONNECTED_TO_OUTPUT(15): @@ -660,6 +677,8 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl) if (ctrl->val >= FIXED_MENU_ITEMS) svid_to_output_menu_skip_mask |= 1ULL << ctrl->val; spin_unlock(&svid_output_skip_mask_lock); + vivid_update_quality(dev); + vivid_send_input_source_change(dev, dev->svid_index_to_input_index[svid_index]); if (ctrl->val < FIXED_MENU_ITEMS && ctrl->cur.val < FIXED_MENU_ITEMS) break; queue_work(update_svid_ctrls_workqueue, &dev->update_svid_ctrl_work); @@ -1026,37 +1045,6 @@ static const struct v4l2_ctrl_config vivid_ctrl_limited_rgb_range = { }; -/* Video Loop Control */ - -static int vivid_loop_cap_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_loop_cap); - - switch (ctrl->id) { - case VIVID_CID_LOOP_VIDEO: - dev->loop_video = ctrl->val; - vivid_update_quality(dev); - vivid_send_source_change(dev, SVID); - vivid_send_source_change(dev, HDMI); - break; - } - return 0; -} - -static const struct v4l2_ctrl_ops vivid_loop_cap_ctrl_ops = { - .s_ctrl = vivid_loop_cap_s_ctrl, -}; - -static const struct v4l2_ctrl_config vivid_ctrl_loop_video = { - .ops = &vivid_loop_cap_ctrl_ops, - .id = VIVID_CID_LOOP_VIDEO, - .name = "Loop Video", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .max = 1, - .step = 1, -}; - - /* VBI Capture Control */ static int vivid_vbi_cap_s_ctrl(struct v4l2_ctrl *ctrl) @@ -1091,8 +1079,6 @@ static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl) { struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_out); struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt; - u32 display_present = 0; - unsigned int i, j, bus_idx; switch (ctrl->id) { case VIVID_CID_HAS_CROP_OUT: @@ -1123,39 +1109,11 @@ static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl) V4L2_QUANTIZATION_LIM_RANGE : V4L2_QUANTIZATION_DEFAULT; } - if (dev->loop_video) - vivid_send_source_change(dev, HDMI); - break; - case VIVID_CID_DISPLAY_PRESENT: - if (dev->output_type[dev->output] != HDMI) - break; + if (vivid_output_is_connected_to(dev)) { + struct vivid_dev *dev_rx = vivid_output_is_connected_to(dev); - dev->display_present[dev->output] = ctrl->val; - for (i = 0, j = 0; i < dev->num_outputs; i++) - if (dev->output_type[i] == HDMI) - display_present |= - dev->display_present[i] << j++; - - __v4l2_ctrl_s_ctrl(dev->ctrl_tx_rxsense, display_present); - - if (dev->edid_blocks) { - __v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, - display_present); - __v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, - display_present); + vivid_send_source_change(dev_rx, HDMI); } - - bus_idx = dev->cec_output2bus_map[dev->output]; - if (!dev->cec_tx_adap[bus_idx]) - break; - - if (ctrl->val && dev->edid_blocks) - cec_s_phys_addr(dev->cec_tx_adap[bus_idx], - dev->cec_tx_adap[bus_idx]->phys_addr, - false); - else - cec_phys_addr_invalidate(dev->cec_tx_adap[bus_idx]); - break; } return 0; @@ -1195,16 +1153,6 @@ static const struct v4l2_ctrl_config vivid_ctrl_has_scaler_out = { .step = 1, }; -static const struct v4l2_ctrl_config vivid_ctrl_display_present = { - .ops = &vivid_vid_out_ctrl_ops, - .id = VIVID_CID_DISPLAY_PRESENT, - .name = "Display Present", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .max = 1, - .def = 1, - .step = 1, -}; - /* Streaming Controls */ static int vivid_streaming_s_ctrl(struct v4l2_ctrl *ctrl) @@ -1914,21 +1862,13 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, dev->ctrl_tx_mode = v4l2_ctrl_new_std_menu(hdl_vid_out, NULL, V4L2_CID_DV_TX_MODE, V4L2_DV_TX_MODE_HDMI, 0, V4L2_DV_TX_MODE_HDMI); - dev->ctrl_display_present = v4l2_ctrl_new_custom(hdl_vid_out, - &vivid_ctrl_display_present, NULL); - dev->ctrl_tx_hotplug = v4l2_ctrl_new_std(hdl_vid_out, - NULL, V4L2_CID_DV_TX_HOTPLUG, 0, hdmi_output_mask, - 0, hdmi_output_mask); - dev->ctrl_tx_rxsense = v4l2_ctrl_new_std(hdl_vid_out, - NULL, V4L2_CID_DV_TX_RXSENSE, 0, hdmi_output_mask, - 0, hdmi_output_mask); - dev->ctrl_tx_edid_present = v4l2_ctrl_new_std(hdl_vid_out, - NULL, V4L2_CID_DV_TX_EDID_PRESENT, 0, hdmi_output_mask, - 0, hdmi_output_mask); + dev->ctrl_tx_hotplug = v4l2_ctrl_new_std(hdl_vid_out, NULL, + V4L2_CID_DV_TX_HOTPLUG, 0, hdmi_output_mask, 0, 0); + dev->ctrl_tx_rxsense = v4l2_ctrl_new_std(hdl_vid_out, NULL, + V4L2_CID_DV_TX_RXSENSE, 0, hdmi_output_mask, 0, 0); + dev->ctrl_tx_edid_present = v4l2_ctrl_new_std(hdl_vid_out, NULL, + V4L2_CID_DV_TX_EDID_PRESENT, 0, hdmi_output_mask, 0, 0); } - if ((dev->has_vid_cap && dev->has_vid_out) || - (dev->has_vbi_cap && dev->has_vbi_out)) - v4l2_ctrl_new_custom(hdl_loop_cap, &vivid_ctrl_loop_video, NULL); if (dev->has_fb) v4l2_ctrl_new_custom(hdl_fb, &vivid_ctrl_clear_fb, NULL); diff --git a/drivers/media/test-drivers/vivid/vivid-kthread-cap.c b/drivers/media/test-drivers/vivid/vivid-kthread-cap.c index 42048727d7ff..669bd96da4c7 100644 --- a/drivers/media/test-drivers/vivid/vivid-kthread-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-kthread-cap.c @@ -142,7 +142,7 @@ static void scale_line(const u8 *src, u8 *dst, unsigned srcw, unsigned dstw, uns * (loop_vid_overlay). Finally calculate the part of the capture buffer that * will receive that overlaid video. */ -static void vivid_precalc_copy_rects(struct vivid_dev *dev) +static void vivid_precalc_copy_rects(struct vivid_dev *dev, struct vivid_dev *out_dev) { /* Framebuffer rectangle */ struct v4l2_rect r_fb = { @@ -150,16 +150,16 @@ static void vivid_precalc_copy_rects(struct vivid_dev *dev) }; /* Overlay window rectangle in framebuffer coordinates */ struct v4l2_rect r_overlay = { - dev->overlay_out_left, dev->overlay_out_top, - dev->compose_out.width, dev->compose_out.height + out_dev->overlay_out_left, out_dev->overlay_out_top, + out_dev->compose_out.width, out_dev->compose_out.height }; - v4l2_rect_intersect(&dev->loop_vid_copy, &dev->crop_cap, &dev->compose_out); + v4l2_rect_intersect(&dev->loop_vid_copy, &dev->crop_cap, &out_dev->compose_out); dev->loop_vid_out = dev->loop_vid_copy; - v4l2_rect_scale(&dev->loop_vid_out, &dev->compose_out, &dev->crop_out); - dev->loop_vid_out.left += dev->crop_out.left; - dev->loop_vid_out.top += dev->crop_out.top; + v4l2_rect_scale(&dev->loop_vid_out, &out_dev->compose_out, &out_dev->crop_out); + dev->loop_vid_out.left += out_dev->crop_out.left; + dev->loop_vid_out.top += out_dev->crop_out.top; dev->loop_vid_cap = dev->loop_vid_copy; v4l2_rect_scale(&dev->loop_vid_cap, &dev->crop_cap, &dev->compose_cap); @@ -176,15 +176,15 @@ static void vivid_precalc_copy_rects(struct vivid_dev *dev) v4l2_rect_intersect(&r_overlay, &r_fb, &r_overlay); /* shift r_overlay to the same origin as compose_out */ - r_overlay.left += dev->compose_out.left - dev->overlay_out_left; - r_overlay.top += dev->compose_out.top - dev->overlay_out_top; + r_overlay.left += out_dev->compose_out.left - out_dev->overlay_out_left; + r_overlay.top += out_dev->compose_out.top - out_dev->overlay_out_top; v4l2_rect_intersect(&dev->loop_vid_overlay, &r_overlay, &dev->loop_vid_copy); dev->loop_fb_copy = dev->loop_vid_overlay; /* shift dev->loop_fb_copy back again to the fb origin */ - dev->loop_fb_copy.left -= dev->compose_out.left - dev->overlay_out_left; - dev->loop_fb_copy.top -= dev->compose_out.top - dev->overlay_out_top; + dev->loop_fb_copy.left -= out_dev->compose_out.left - out_dev->overlay_out_left; + dev->loop_fb_copy.top -= out_dev->compose_out.top - out_dev->overlay_out_top; dev->loop_vid_overlay_cap = dev->loop_vid_overlay; v4l2_rect_scale(&dev->loop_vid_overlay_cap, &dev->crop_cap, &dev->compose_cap); @@ -213,24 +213,25 @@ static void *plane_vaddr(struct tpg_data *tpg, struct vivid_buffer *buf, return vbuf; } -static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned p, - u8 *vcapbuf, struct vivid_buffer *vid_cap_buf) +static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, + struct vivid_dev *out_dev, unsigned p, + u8 *vcapbuf, struct vivid_buffer *vid_cap_buf) { bool blank = dev->must_blank[vid_cap_buf->vb.vb2_buf.index]; struct tpg_data *tpg = &dev->tpg; struct vivid_buffer *vid_out_buf = NULL; - unsigned vdiv = dev->fmt_out->vdownsampling[p]; + unsigned vdiv = out_dev->fmt_out->vdownsampling[p]; unsigned twopixsize = tpg_g_twopixelsize(tpg, p); unsigned img_width = tpg_hdiv(tpg, p, dev->compose_cap.width); unsigned img_height = dev->compose_cap.height; unsigned stride_cap = tpg->bytesperline[p]; - unsigned stride_out = dev->bytesperline_out[p]; + unsigned stride_out = out_dev->bytesperline_out[p]; unsigned stride_osd = dev->display_byte_stride; unsigned hmax = (img_height * tpg->perc_fill) / 100; u8 *voutbuf; u8 *vosdbuf = NULL; unsigned y; - bool blend = dev->fbuf_out_flags; + bool blend = out_dev->fbuf_out_flags; /* Coarse scaling with Bresenham */ unsigned vid_out_int_part; unsigned vid_out_fract_part; @@ -247,8 +248,8 @@ static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned vid_out_int_part = dev->loop_vid_out.height / dev->loop_vid_cap.height; vid_out_fract_part = dev->loop_vid_out.height % dev->loop_vid_cap.height; - if (!list_empty(&dev->vid_out_active)) - vid_out_buf = list_entry(dev->vid_out_active.next, + if (!list_empty(&out_dev->vid_out_active)) + vid_out_buf = list_entry(out_dev->vid_out_active.next, struct vivid_buffer, list); if (vid_out_buf == NULL) return -ENODATA; @@ -256,8 +257,8 @@ static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned vid_cap_buf->vb.field = vid_out_buf->vb.field; voutbuf = plane_vaddr(tpg, vid_out_buf, p, - dev->bytesperline_out, dev->fmt_out_rect.height); - if (p < dev->fmt_out->buffers) + out_dev->bytesperline_out, out_dev->fmt_out_rect.height); + if (p < out_dev->fmt_out->buffers) voutbuf += vid_out_buf->vb.vb2_buf.planes[p].data_offset; voutbuf += tpg_hdiv(tpg, p, dev->loop_vid_out.left) + (dev->loop_vid_out.top / vdiv) * stride_out; @@ -274,7 +275,7 @@ static noinline_for_stack int vivid_copy_buffer(struct vivid_dev *dev, unsigned return 0; } - if (dev->overlay_out_enabled && + if (out_dev->overlay_out_enabled && dev->loop_vid_overlay.width && dev->loop_vid_overlay.height) { vosdbuf = dev->video_vbase; vosdbuf += (dev->loop_fb_copy.left * twopixsize) / 2 + @@ -385,6 +386,7 @@ update_vid_out_y: static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) { + struct vivid_dev *out_dev = NULL; struct tpg_data *tpg = &dev->tpg; unsigned factor = V4L2_FIELD_HAS_T_OR_B(dev->field_cap) ? 2 : 1; unsigned line_height = 16 / factor; @@ -396,14 +398,6 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) unsigned ms; char str[100]; s32 gain; - bool is_loop = false; - - if (dev->loop_video && dev->can_loop_video && - ((vivid_is_svid_cap(dev) && - !VIVID_INVALID_SIGNAL(dev->std_signal_mode[dev->input])) || - (vivid_is_hdmi_cap(dev) && - !VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode[dev->input])))) - is_loop = true; buf->vb.sequence = dev->vid_cap_seq_count; v4l2_ctrl_s_ctrl(dev->ro_int32, buf->vb.sequence & 0xff); @@ -428,7 +422,34 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) dev->field_cap == V4L2_FIELD_ALTERNATE); tpg_s_perc_fill_blank(tpg, dev->must_blank[buf->vb.vb2_buf.index]); - vivid_precalc_copy_rects(dev); + if (vivid_vid_can_loop(dev) && + ((vivid_is_svid_cap(dev) && + !VIVID_INVALID_SIGNAL(dev->std_signal_mode[dev->input])) || + (vivid_is_hdmi_cap(dev) && + !VIVID_INVALID_SIGNAL(dev->dv_timings_signal_mode[dev->input])))) { + out_dev = vivid_input_is_connected_to(dev); + /* + * If the vivid instance of the output device is different + * from the vivid instance of this input device, then we + * must take care to properly serialize the output device to + * prevent that the buffer we are copying from is being freed. + * + * If the output device is part of the same instance, then the + * lock is already taken and there is no need to take the mutex. + * + * The problem with taking the mutex is that you can get + * deadlocked if instance A locks instance B and vice versa. + * It is not really worth trying to be very smart about this, + * so just try to take the lock, and if you can't, then just + * set out_dev to NULL and you will end up with a single frame + * of Noise (the default test pattern in this case). + */ + if (out_dev && dev != out_dev && !mutex_trylock(&out_dev->mutex)) + out_dev = NULL; + } + + if (out_dev) + vivid_precalc_copy_rects(dev, out_dev); for (p = 0; p < tpg_g_planes(tpg); p++) { void *vbuf = plane_vaddr(tpg, buf, p, @@ -445,10 +466,13 @@ static void vivid_fillbuff(struct vivid_dev *dev, struct vivid_buffer *buf) vbuf += dev->fmt_cap->data_offset[p]; } tpg_calc_text_basep(tpg, basep, p, vbuf); - if (!is_loop || vivid_copy_buffer(dev, p, vbuf, buf)) + if (!out_dev || vivid_copy_buffer(dev, out_dev, p, vbuf, buf)) tpg_fill_plane_buffer(tpg, vivid_get_std_cap(dev), p, vbuf); } + if (out_dev && dev != out_dev) + mutex_unlock(&out_dev->mutex); + dev->must_blank[buf->vb.vb2_buf.index] = false; /* Updates stream time, only update at the start of a new frame. */ diff --git a/drivers/media/test-drivers/vivid/vivid-vbi-cap.c b/drivers/media/test-drivers/vivid/vivid-vbi-cap.c index 95387d57eb93..99138f63585c 100644 --- a/drivers/media/test-drivers/vivid/vivid-vbi-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vbi-cap.c @@ -14,6 +14,7 @@ #include "vivid-kthread-cap.h" #include "vivid-vbi-cap.h" #include "vivid-vbi-gen.h" +#include "vivid-vid-common.h" static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr) { @@ -23,7 +24,7 @@ static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr) vivid_vbi_gen_sliced(vbi_gen, is_60hz, seqnr); if (!is_60hz) { - if (dev->loop_video) { + if (vivid_vid_can_loop(dev)) { if (dev->vbi_out_have_wss) { vbi_gen->data[12].data[0] = dev->vbi_out_wss[0]; vbi_gen->data[12].data[1] = dev->vbi_out_wss[1]; @@ -47,7 +48,7 @@ static void vivid_sliced_vbi_cap_fill(struct vivid_dev *dev, unsigned seqnr) break; } } - } else if (dev->loop_video && is_60hz) { + } else if (vivid_vid_can_loop(dev) && is_60hz) { if (dev->vbi_out_have_cc[0]) { vbi_gen->data[0].data[0] = dev->vbi_out_cc[0][0]; vbi_gen->data[0].data[1] = dev->vbi_out_cc[0][1]; diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c index b41a7c87aaa7..69620e0a35a0 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c @@ -211,9 +211,6 @@ static int vid_cap_start_streaming(struct vb2_queue *vq, unsigned count) unsigned i; int err; - if (vb2_is_streaming(&dev->vb_vid_out_q)) - dev->can_loop_video = vivid_vid_can_loop(dev); - dev->vid_cap_seq_count = 0; dprintk(dev, 1, "%s\n", __func__); for (i = 0; i < VIDEO_MAX_FRAME; i++) @@ -243,7 +240,6 @@ static void vid_cap_stop_streaming(struct vb2_queue *vq) dprintk(dev, 1, "%s\n", __func__); vivid_stop_generating_vid_cap(dev, &dev->vid_cap_streaming); - dev->can_loop_video = false; } static void vid_cap_buf_request_complete(struct vb2_buffer *vb) @@ -274,7 +270,7 @@ void vivid_update_quality(struct vivid_dev *dev) { unsigned freq_modulus; - if (dev->loop_video && (vivid_is_svid_cap(dev) || vivid_is_hdmi_cap(dev))) { + if (dev->input_is_connected_to_output[dev->input]) { /* * The 'noise' will only be replaced by the actual video * if the output video matches the input video settings. @@ -488,35 +484,35 @@ static enum v4l2_field vivid_field_cap(struct vivid_dev *dev, enum v4l2_field fi static unsigned vivid_colorspace_cap(struct vivid_dev *dev) { - if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) + if (!vivid_input_is_connected_to(dev)) return tpg_g_colorspace(&dev->tpg); return dev->colorspace_out; } static unsigned vivid_xfer_func_cap(struct vivid_dev *dev) { - if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) + if (!vivid_input_is_connected_to(dev)) return tpg_g_xfer_func(&dev->tpg); return dev->xfer_func_out; } static unsigned vivid_ycbcr_enc_cap(struct vivid_dev *dev) { - if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) + if (!vivid_input_is_connected_to(dev)) return tpg_g_ycbcr_enc(&dev->tpg); return dev->ycbcr_enc_out; } static unsigned int vivid_hsv_enc_cap(struct vivid_dev *dev) { - if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) + if (!vivid_input_is_connected_to(dev)) return tpg_g_hsv_enc(&dev->tpg); return dev->hsv_enc_out; } static unsigned vivid_quantization_cap(struct vivid_dev *dev) { - if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev)) + if (!vivid_input_is_connected_to(dev)) return tpg_g_quantization(&dev->tpg); return dev->quantization_out; } @@ -1538,13 +1534,65 @@ int vidioc_query_dv_timings(struct file *file, void *_fh, return 0; } +void vivid_update_outputs(struct vivid_dev *dev) +{ + u32 edid_present = 0; + + if (!dev || !dev->num_outputs) + return; + for (unsigned int i = 0, j = 0; i < dev->num_outputs; i++) { + if (dev->output_type[i] != HDMI) + continue; + + struct vivid_dev *dev_rx = dev->output_to_input_instance[i]; + + if (dev_rx && dev_rx->edid_blocks) + edid_present |= 1 << j; + j++; + } + v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, edid_present); + v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, edid_present); + v4l2_ctrl_s_ctrl(dev->ctrl_tx_rxsense, edid_present); +} + +void vivid_update_connected_outputs(struct vivid_dev *dev) +{ + u16 phys_addr = cec_get_edid_phys_addr(dev->edid, dev->edid_blocks * 128, NULL); + + for (unsigned int i = 0, j = 0; i < dev->num_inputs; i++) { + unsigned int menu_idx = + dev->input_is_connected_to_output[i]; + + if (dev->input_type[i] != HDMI) + continue; + j++; + if (menu_idx < FIXED_MENU_ITEMS) + continue; + + struct vivid_dev *dev_tx = vivid_ctrl_hdmi_to_output_instance[menu_idx]; + unsigned int output = vivid_ctrl_hdmi_to_output_index[menu_idx]; + + if (!dev_tx) + continue; + + unsigned int hdmi_output = dev_tx->output_to_iface_index[output]; + + vivid_update_outputs(dev_tx); + if (dev->edid_blocks) { + cec_s_phys_addr(dev_tx->cec_tx_adap[hdmi_output], + v4l2_phys_addr_for_input(phys_addr, j), + false); + } else { + cec_phys_addr_invalidate(dev_tx->cec_tx_adap[hdmi_output]); + } + } +} + int vidioc_s_edid(struct file *file, void *_fh, struct v4l2_edid *edid) { struct vivid_dev *dev = video_drvdata(file); u16 phys_addr; - u32 display_present = 0; - unsigned int i, j; int ret; memset(edid->reserved, 0, sizeof(edid->reserved)); @@ -1553,13 +1601,11 @@ int vidioc_s_edid(struct file *file, void *_fh, if (dev->input_type[edid->pad] != HDMI || edid->start_block) return -EINVAL; if (edid->blocks == 0) { + if (vb2_is_busy(&dev->vb_vid_cap_q)) + return -EBUSY; dev->edid_blocks = 0; - if (dev->num_outputs) { - v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, 0); - v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, 0); - } - phys_addr = CEC_PHYS_ADDR_INVALID; - goto set_phys_addr; + vivid_update_connected_outputs(dev); + return 0; } if (edid->blocks > dev->edid_max_blocks) { edid->blocks = dev->edid_max_blocks; @@ -1576,26 +1622,7 @@ int vidioc_s_edid(struct file *file, void *_fh, dev->edid_blocks = edid->blocks; memcpy(dev->edid, edid->edid, edid->blocks * 128); - for (i = 0, j = 0; i < dev->num_outputs; i++) - if (dev->output_type[i] == HDMI) - display_present |= - dev->display_present[i] << j++; - - if (dev->num_outputs) { - v4l2_ctrl_s_ctrl(dev->ctrl_tx_edid_present, display_present); - v4l2_ctrl_s_ctrl(dev->ctrl_tx_hotplug, display_present); - } - -set_phys_addr: - /* TODO: a proper hotplug detect cycle should be emulated here */ - cec_s_phys_addr(dev->cec_rx_adap, phys_addr, false); - - for (i = 0; i < MAX_OUTPUTS && dev->cec_tx_adap[i]; i++) - cec_s_phys_addr(dev->cec_tx_adap[i], - dev->display_present[i] ? - v4l2_phys_addr_for_input(phys_addr, i + 1) : - CEC_PHYS_ADDR_INVALID, - false); + vivid_update_connected_outputs(dev); return 0; } diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.h b/drivers/media/test-drivers/vivid/vivid-vid-cap.h index 949768652d38..7a8daf0af2ca 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-cap.h +++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.h @@ -10,6 +10,8 @@ void vivid_update_quality(struct vivid_dev *dev); void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls); +void vivid_update_outputs(struct vivid_dev *dev); +void vivid_update_connected_outputs(struct vivid_dev *dev); enum tpg_video_aspect vivid_get_video_aspect(const struct vivid_dev *dev); extern const v4l2_std_id vivid_standard[]; diff --git a/drivers/media/test-drivers/vivid/vivid-vid-common.c b/drivers/media/test-drivers/vivid/vivid-vid-common.c index a3e8eb90f11b..df7678db67fb 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-common.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-common.c @@ -769,14 +769,55 @@ const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat) return NULL; } +struct vivid_dev *vivid_output_is_connected_to(struct vivid_dev *dev) +{ + struct vivid_dev *input_inst = dev->output_to_input_instance[dev->output]; + + if (!input_inst) + return NULL; + if (input_inst->input != dev->output_to_input_index[dev->output]) + return NULL; + return input_inst; +} + +struct vivid_dev *vivid_input_is_connected_to(struct vivid_dev *dev) +{ + s32 connected_output = dev->input_is_connected_to_output[dev->input]; + + if (connected_output < FIXED_MENU_ITEMS) + return NULL; + struct vivid_dev *output_inst = NULL; + + if (vivid_is_hdmi_cap(dev)) { + output_inst = vivid_ctrl_hdmi_to_output_instance[connected_output]; + if (vivid_ctrl_hdmi_to_output_index[connected_output] != output_inst->output) + return NULL; + return output_inst; + } else if (vivid_is_svid_cap(dev)) { + output_inst = vivid_ctrl_svid_to_output_instance[connected_output]; + if (vivid_ctrl_svid_to_output_index[connected_output] != output_inst->output) + return NULL; + return output_inst; + } else { + return NULL; + } + return output_inst; +} + bool vivid_vid_can_loop(struct vivid_dev *dev) { - if (dev->src_rect.width != dev->sink_rect.width || - dev->src_rect.height != dev->sink_rect.height) + struct vivid_dev *output_inst = vivid_input_is_connected_to(dev); + + if (!output_inst) return false; - if (dev->fmt_cap->fourcc != dev->fmt_out->fourcc) + if (!vb2_is_streaming(&output_inst->vb_vid_out_q)) return false; - if (dev->field_cap != dev->field_out) + if (dev->src_rect.width != output_inst->sink_rect.width || + dev->src_rect.height != output_inst->sink_rect.height) + return false; + if (dev->fmt_cap->fourcc != output_inst->fmt_out->fourcc) + return false; + if (dev->field_cap != output_inst->field_out) return false; /* * While this can be supported, it is just too much work @@ -785,34 +826,34 @@ bool vivid_vid_can_loop(struct vivid_dev *dev) if (dev->field_cap == V4L2_FIELD_SEQ_TB || dev->field_cap == V4L2_FIELD_SEQ_BT) return false; - if (vivid_is_svid_cap(dev) && vivid_is_svid_out(dev)) { - if (!(dev->std_cap[dev->input] & V4L2_STD_525_60) != - !(dev->std_out & V4L2_STD_525_60)) - return false; - return true; - } - if (vivid_is_hdmi_cap(dev) && vivid_is_hdmi_out(dev)) + if (vivid_is_hdmi_cap(dev)) return true; - return false; + if (!(dev->std_cap[dev->input] & V4L2_STD_525_60) != + !(output_inst->std_out & V4L2_STD_525_60)) + return false; + return true; } -void vivid_send_source_change(struct vivid_dev *dev, unsigned type) +void vivid_send_input_source_change(struct vivid_dev *dev, unsigned int input_index) { struct v4l2_event ev = { .type = V4L2_EVENT_SOURCE_CHANGE, .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, }; - unsigned i; + ev.id = input_index; - for (i = 0; i < dev->num_inputs; i++) { - ev.id = i; - if (dev->input_type[i] == type) { - if (video_is_registered(&dev->vid_cap_dev) && dev->has_vid_cap) - v4l2_event_queue(&dev->vid_cap_dev, &ev); - if (video_is_registered(&dev->vbi_cap_dev) && dev->has_vbi_cap) - v4l2_event_queue(&dev->vbi_cap_dev, &ev); - } - } + if (video_is_registered(&dev->vid_cap_dev) && dev->has_vid_cap) + v4l2_event_queue(&dev->vid_cap_dev, &ev); + if (dev->input_type[input_index] == TV || dev->input_type[input_index] == SVID) + if (video_is_registered(&dev->vbi_cap_dev) && dev->has_vbi_cap) + v4l2_event_queue(&dev->vbi_cap_dev, &ev); +} + +void vivid_send_source_change(struct vivid_dev *dev, unsigned int type) +{ + for (int i = 0; i < dev->num_inputs; i++) + if (dev->input_type[i] == type) + vivid_send_input_source_change(dev, i); } /* @@ -1036,6 +1077,7 @@ int vidioc_g_edid(struct file *file, void *_fh, struct v4l2_edid *edid) { struct vivid_dev *dev = video_drvdata(file); + struct vivid_dev *dev_rx = dev; struct video_device *vdev = video_devdata(file); struct cec_adapter *adap; unsigned int loc; @@ -1048,31 +1090,33 @@ int vidioc_g_edid(struct file *file, void *_fh, return -EINVAL; adap = dev->cec_rx_adap; } else { - unsigned int bus_idx; - if (edid->pad >= dev->num_outputs) return -EINVAL; if (dev->output_type[edid->pad] != HDMI) return -EINVAL; - if (!dev->display_present[edid->pad]) + dev_rx = dev->output_to_input_instance[edid->pad]; + if (!dev_rx) return -ENODATA; - bus_idx = dev->cec_output2bus_map[edid->pad]; - adap = dev->cec_tx_adap[bus_idx]; + + unsigned int hdmi_output = dev->output_to_iface_index[edid->pad]; + + adap = dev->cec_tx_adap[hdmi_output]; } if (edid->start_block == 0 && edid->blocks == 0) { - edid->blocks = dev->edid_blocks; + edid->blocks = dev_rx->edid_blocks; return 0; } - if (dev->edid_blocks == 0) + if (dev_rx->edid_blocks == 0) return -ENODATA; - if (edid->start_block >= dev->edid_blocks) + if (edid->start_block >= dev_rx->edid_blocks) return -EINVAL; - if (edid->blocks > dev->edid_blocks - edid->start_block) - edid->blocks = dev->edid_blocks - edid->start_block; + if (edid->blocks > dev_rx->edid_blocks - edid->start_block) + edid->blocks = dev_rx->edid_blocks - edid->start_block; - memcpy(edid->edid, dev->edid + edid->start_block * 128, edid->blocks * 128); + memcpy(edid->edid, dev_rx->edid + edid->start_block * 128, edid->blocks * 128); - loc = cec_get_edid_spa_location(dev->edid, dev->edid_blocks * 128); + loc = cec_get_edid_spa_location(dev_rx->edid, + dev_rx->edid_blocks * 128); if (vdev->vfl_dir == VFL_DIR_TX && adap && loc && loc >= edid->start_block * 128 && loc < (edid->start_block + edid->blocks) * 128) { diff --git a/drivers/media/test-drivers/vivid/vivid-vid-common.h b/drivers/media/test-drivers/vivid/vivid-vid-common.h index d908d9725283..c49ac85abaed 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-common.h +++ b/drivers/media/test-drivers/vivid/vivid-vid-common.h @@ -22,8 +22,11 @@ extern const struct v4l2_dv_timings_cap vivid_dv_timings_cap; const struct vivid_fmt *vivid_get_format(struct vivid_dev *dev, u32 pixelformat); +struct vivid_dev *vivid_input_is_connected_to(struct vivid_dev *dev); +struct vivid_dev *vivid_output_is_connected_to(struct vivid_dev *dev); bool vivid_vid_can_loop(struct vivid_dev *dev); -void vivid_send_source_change(struct vivid_dev *dev, unsigned type); +void vivid_send_source_change(struct vivid_dev *dev, unsigned int type); +void vivid_send_input_source_change(struct vivid_dev *dev, unsigned int input_index); int vivid_vid_adjust_sel(unsigned flags, struct v4l2_rect *r); diff --git a/drivers/media/test-drivers/vivid/vivid-vid-out.c b/drivers/media/test-drivers/vivid/vivid-vid-out.c index 5e47bdcb9923..60327f3612af 100644 --- a/drivers/media/test-drivers/vivid/vivid-vid-out.c +++ b/drivers/media/test-drivers/vivid/vivid-vid-out.c @@ -157,9 +157,6 @@ static int vid_out_start_streaming(struct vb2_queue *vq, unsigned count) struct vivid_dev *dev = vb2_get_drv_priv(vq); int err; - if (vb2_is_streaming(&dev->vb_vid_cap_q)) - dev->can_loop_video = vivid_vid_can_loop(dev); - dev->vid_out_seq_count = 0; dprintk(dev, 1, "%s\n", __func__); if (dev->start_streaming_error) { @@ -187,7 +184,6 @@ static void vid_out_stop_streaming(struct vb2_queue *vq) dprintk(dev, 1, "%s\n", __func__); vivid_stop_generating_vid_out(dev, &dev->vid_out_streaming); - dev->can_loop_video = false; } static void vid_out_buf_request_complete(struct vb2_buffer *vb) @@ -564,9 +560,11 @@ set_colorspace: dev->xfer_func_out = mp->xfer_func; dev->ycbcr_enc_out = mp->ycbcr_enc; dev->quantization_out = mp->quantization; - if (dev->loop_video) { - vivid_send_source_change(dev, SVID); - vivid_send_source_change(dev, HDMI); + struct vivid_dev *in_dev = vivid_output_is_connected_to(dev); + + if (in_dev) { + vivid_send_source_change(in_dev, SVID); + vivid_send_source_change(in_dev, HDMI); } return 0; } @@ -1016,11 +1014,6 @@ int vidioc_s_output(struct file *file, void *priv, unsigned o) dev->meta_out_dev.tvnorms = dev->vid_out_dev.tvnorms; vivid_update_format_out(dev); - v4l2_ctrl_activate(dev->ctrl_display_present, vivid_is_hdmi_out(dev)); - if (vivid_is_hdmi_out(dev)) - v4l2_ctrl_s_ctrl(dev->ctrl_display_present, - dev->display_present[dev->output]); - return 0; } -- cgit v1.2.3 From f5306b757cb78aeec45e03ee52ec038c6423ad7a Mon Sep 17 00:00:00 2001 From: Dorcas Anono Litunya Date: Mon, 24 Jun 2024 12:53:06 +0300 Subject: documentation: media: vivid: Update documentation on vivid loopback support Modify section "Video and Sliced VBI Looping" in Documentation to explain the vivid loopback support for video across multiple vivid instances. Previous documentation is out-of-date as it was explaining looping in a single vivid instance only. Also, in "Some Future Improvements" the item "Add support to loop from a specific output to a specific input across vivid instances" can be dropped since that's now implemented. Signed-off-by: Dorcas Anono Litunya Signed-off-by: Hans Verkuil --- Documentation/admin-guide/media/vivid.rst | 52 +++++++++++++++++-------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/Documentation/admin-guide/media/vivid.rst b/Documentation/admin-guide/media/vivid.rst index 29481241d7cb..1306f19ecb5a 100644 --- a/Documentation/admin-guide/media/vivid.rst +++ b/Documentation/admin-guide/media/vivid.rst @@ -1135,27 +1135,33 @@ Metadata Capture Controls if set, then the generated metadata stream contains Source Clock information. -Video and Sliced VBI Looping ----------------------------- - -The vivid driver supports looping of video output to video input, and VBI -output to VBI input. For video/VBI looping this emulates as if a cable was -hooked up between the output and input connector. So video and VBI looping -is only supported between S-Video and HDMI inputs and outputs. -VBI is only valid for S-Video as it makes no sense for HDMI. - -Looping is currently supported only between devices created by the same -vivid driver instance. - -The way to enable video/VBI looping is currently fairly crude. A 'Loop Video' -control is available in the "Vivid" control class of the video -capture and VBI capture devices. When checked the video looping will be enabled. -Once enabled any video S-Video or HDMI input will show a static test pattern -until the video output has started. At that time the video output will be -looped to the video input provided that: - -- the input type matches the output type. So the HDMI input cannot receive - video from the S-Video output. +Video, Sliced VBI and HDMI CEC Looping +-------------------------------------- + +Video Looping functionality is supported for devices created by the same +vivid driver instance, as well as across multiple instances of the vivid driver. +The vivid driver supports looping of video and Sliced VBI data between an S-Video output +and an S-Video input. It also supports looping of video and HDMI CEC data between an +HDMI output and an HDMI input. + +To enable looping, set the 'HDMI/S-Video XXX-N Is Connected To' control(s) to select +whether an input uses the Test Pattern Generator, or is disconnected, or is connected +to an output. An input can be connected to an output from any vivid instance. +The inputs and outputs are numbered XXX-N where XXX is the vivid instance number +(see module option n_devs). If there is only one vivid instance (the default), then +XXX will be 000. And N is the Nth S-Video/HDMI input or output of that instance. +If vivid is loaded without module options, then you can connect the S-Video 000-0 input +to the S-Video 000-0 output, or the HDMI 000-0 input to the HDMI 000-0 output. +This is the equivalent of connecting or disconnecting a cable between an input and an +output in a physical device. + +If an 'HDMI/S-Video XXX-N Is Connected To' control selected an output, then the video +output will be looped to the video input provided that: + +- the currently selected input matches the input indicated by the control name. + +- in the vivid instance of the output connector, the currently selected output matches + the output indicated by the control's value. - the video resolution of the video input must match that of the video output. So it is not possible to loop a 50 Hz (720x576) S-Video output to a 60 Hz @@ -1182,6 +1188,8 @@ looped to the video input provided that: "DV Timings Signal Mode" for the HDMI input should be configured so that a valid signal is passed to the video input. +If any condition is not valid, then the 'Noise' test pattern is shown. + The framerates do not have to match, although this might change in the future. By default you will see the OSD text superimposed on top of the looped video. @@ -1344,8 +1352,6 @@ Just as a reminder and in no particular order: - Add ARGB888 overlay support: better testing of the alpha channel - Improve pixel aspect support in the tpg code by passing a real v4l2_fract - Use per-queue locks and/or per-device locks to improve throughput -- Add support to loop from a specific output to a specific input across - vivid instances - The SDR radio should use the same 'frequencies' for stations as the normal radio receiver, and give back noise if the frequency doesn't match up with a station frequency -- cgit v1.2.3 From ae44829a4a97a34486a89dbafc2653e9cbf896dd Mon Sep 17 00:00:00 2001 From: Radoslav Tsvetkov Date: Wed, 22 May 2024 18:46:52 +0300 Subject: media: qcom: camss: Add per sub-device type resources Currently resources structure grows with additional parameters required for each sub-deivce. However each sub-device has some specific resources or configurations which need to be passed during the initialization. This change adds per sub-device type structure to simplify the things and removes the magical void pointer to hw_ops. Signed-off-by: Radoslav Tsvetkov Signed-off-by: Gjorgji Rosikopulos Signed-off-by: Bryan O'Donoghue Reviewed-by: Bryan O'Donoghue Tested-by: Bryan O'Donoghue # sc8280xp/sm8250/sdm845/apq8016 Acked-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/camss/camss-csid.c | 22 +- drivers/media/platform/qcom/camss/camss-csid.h | 7 +- drivers/media/platform/qcom/camss/camss-csiphy.c | 14 +- drivers/media/platform/qcom/camss/camss-csiphy.h | 6 +- drivers/media/platform/qcom/camss/camss-vfe-17x.c | 10 +- drivers/media/platform/qcom/camss/camss-vfe-4-1.c | 4 +- drivers/media/platform/qcom/camss/camss-vfe-4-7.c | 6 +- drivers/media/platform/qcom/camss/camss-vfe-4-8.c | 6 +- drivers/media/platform/qcom/camss/camss-vfe-gen1.c | 8 +- drivers/media/platform/qcom/camss/camss-vfe.c | 54 ++- drivers/media/platform/qcom/camss/camss-vfe.h | 11 +- drivers/media/platform/qcom/camss/camss.c | 381 ++++++++++++++------- drivers/media/platform/qcom/camss/camss.h | 10 +- 13 files changed, 343 insertions(+), 196 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index eb27d69e89a1..d1a22e07fdb6 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -202,7 +202,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) enable_irq(csid->irq); - ret = csid->ops->reset(csid); + ret = csid->res->hw_ops->reset(csid); if (ret < 0) { disable_irq(csid->irq); camss_disable_clocks(csid->nclocks, csid->clock); @@ -212,7 +212,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) return ret; } - csid->ops->hw_version(csid); + csid->res->hw_ops->hw_version(csid); } else { disable_irq(csid->irq); camss_disable_clocks(csid->nclocks, csid->clock); @@ -253,7 +253,7 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) } if (csid->phy.need_vc_update) { - csid->ops->configure_stream(csid, enable); + csid->res->hw_ops->configure_stream(csid, enable); csid->phy.need_vc_update = false; } @@ -325,7 +325,7 @@ static void csid_try_format(struct csid_device *csid, *fmt = *__csid_get_format(csid, sd_state, MSM_CSID_PAD_SINK, which); - fmt->code = csid->ops->src_pad_code(csid, fmt->code, 0, code); + fmt->code = csid->res->hw_ops->src_pad_code(csid, fmt->code, 0, code); } else { /* Test generator is enabled, set format on source */ /* pad to allow test generator usage */ @@ -375,8 +375,8 @@ static int csid_enum_mbus_code(struct v4l2_subdev *sd, MSM_CSID_PAD_SINK, code->which); - code->code = csid->ops->src_pad_code(csid, sink_fmt->code, - code->index, 0); + code->code = csid->res->hw_ops->src_pad_code(csid, sink_fmt->code, + code->index, 0); if (!code->code) return -EINVAL; } else { @@ -529,7 +529,7 @@ static int csid_set_test_pattern(struct csid_device *csid, s32 value) tg->enabled = !!value; - return csid->ops->configure_testgen_pattern(csid, value); + return csid->res->hw_ops->configure_testgen_pattern(csid, value); } /* @@ -575,9 +575,9 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, csid->camss = camss; csid->id = id; - csid->ops = res->ops; + csid->res = &res->csid; - csid->ops->subdev_init(csid); + csid->res->hw_ops->subdev_init(csid); /* Memory */ @@ -605,7 +605,7 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, csid->irq = ret; snprintf(csid->irq_name, sizeof(csid->irq_name), "%s_%s%d", dev_name(dev), MSM_CSID_NAME, csid->id); - ret = devm_request_irq(dev, csid->irq, csid->ops->isr, + ret = devm_request_irq(dev, csid->irq, csid->res->hw_ops->isr, IRQF_TRIGGER_RISING | IRQF_NO_AUTOEN, csid->irq_name, csid); if (ret < 0) { @@ -899,5 +899,5 @@ void msm_csid_unregister_entity(struct csid_device *csid) inline bool csid_is_lite(struct csid_device *csid) { - return csid->camss->res->csid_res[csid->id].is_lite; + return csid->camss->res->csid_res[csid->id].csid.is_lite; } diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h index fddccb69da13..8d2971aa9ef8 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.h +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -149,6 +149,11 @@ struct csid_hw_ops { void (*subdev_init)(struct csid_device *csid); }; +struct csid_subdev_resources { + bool is_lite; + const struct csid_hw_ops *hw_ops; +}; + struct csid_device { struct camss *camss; u8 id; @@ -169,7 +174,7 @@ struct csid_device { struct v4l2_ctrl *testgen_mode; const struct csid_format *formats; unsigned int nformats; - const struct csid_hw_ops *ops; + const struct csid_subdev_resources *res; }; struct camss_subdev_resources; diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 45b3a8e5dea4..f26ddf1af9d4 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -216,9 +216,9 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on) enable_irq(csiphy->irq); - csiphy->ops->reset(csiphy); + csiphy->res->hw_ops->reset(csiphy); - csiphy->ops->hw_version_read(csiphy, dev); + csiphy->res->hw_ops->hw_version_read(csiphy, dev); } else { disable_irq(csiphy->irq); @@ -243,7 +243,7 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) { struct csiphy_config *cfg = &csiphy->cfg; s64 link_freq; - u8 lane_mask = csiphy->ops->get_lane_mask(&cfg->csi2->lane_cfg); + u8 lane_mask = csiphy->res->hw_ops->get_lane_mask(&cfg->csi2->lane_cfg); u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats, csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; @@ -272,7 +272,7 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) wmb(); } - csiphy->ops->lanes_enable(csiphy, cfg, link_freq, lane_mask); + csiphy->res->hw_ops->lanes_enable(csiphy, cfg, link_freq, lane_mask); return 0; } @@ -285,7 +285,7 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) */ static void csiphy_stream_off(struct csiphy_device *csiphy) { - csiphy->ops->lanes_disable(csiphy, &csiphy->cfg); + csiphy->res->hw_ops->lanes_disable(csiphy, &csiphy->cfg); } @@ -564,7 +564,7 @@ int msm_csiphy_subdev_init(struct camss *camss, csiphy->camss = camss; csiphy->id = id; csiphy->cfg.combo_mode = 0; - csiphy->ops = res->ops; + csiphy->res = &res->csiphy; switch (camss->res->version) { case CAMSS_8x16: @@ -610,7 +610,7 @@ int msm_csiphy_subdev_init(struct camss *camss, snprintf(csiphy->irq_name, sizeof(csiphy->irq_name), "%s_%s%d", dev_name(dev), MSM_CSIPHY_NAME, csiphy->id); - ret = devm_request_irq(dev, csiphy->irq, csiphy->ops->isr, + ret = devm_request_irq(dev, csiphy->irq, csiphy->res->hw_ops->isr, IRQF_TRIGGER_RISING | IRQF_NO_AUTOEN, csiphy->irq_name, csiphy); if (ret < 0) { diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index c9b7fe82b1f0..7bd68129ca49 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -63,6 +63,10 @@ struct csiphy_hw_ops { irqreturn_t (*isr)(int irq, void *dev); }; +struct csiphy_subdev_resources { + const struct csiphy_hw_ops *hw_ops; +}; + struct csiphy_device { struct camss *camss; u8 id; @@ -78,7 +82,7 @@ struct csiphy_device { u32 timer_clk_rate; struct csiphy_config cfg; struct v4l2_mbus_framefmt fmt[MSM_CSIPHY_PADS_NUM]; - const struct csiphy_hw_ops *ops; + const struct csiphy_subdev_resources *res; const struct csiphy_format *formats; unsigned int nformats; }; diff --git a/drivers/media/platform/qcom/camss/camss-vfe-17x.c b/drivers/media/platform/qcom/camss/camss-vfe-17x.c index 795ac3815339..380c99321030 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-17x.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-17x.c @@ -353,7 +353,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) writel_relaxed(status0, vfe->base + VFE_IRQ_CLEAR_0); writel_relaxed(status1, vfe->base + VFE_IRQ_CLEAR_1); - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) { + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) { vfe_bus_status[i] = readl_relaxed(vfe->base + VFE_BUS_IRQ_STATUS(i)); writel_relaxed(vfe_bus_status[i], vfe->base + VFE_BUS_IRQ_CLEAR(i)); } @@ -367,11 +367,11 @@ static irqreturn_t vfe_isr(int irq, void *dev) if (status0 & STATUS_0_RESET_ACK) vfe->isr_ops.reset_ack(vfe); - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) if (status0 & STATUS_0_RDI_REG_UPDATE(i)) vfe->isr_ops.reg_update(vfe, i); - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) if (status0 & STATUS_1_RDI_SOF(i)) vfe->isr_ops.sof(vfe, i); @@ -442,7 +442,7 @@ static int vfe_enable_output(struct vfe_line *line) { struct vfe_device *vfe = to_vfe(line); struct vfe_output *output = &line->output; - const struct vfe_hw_ops *ops = vfe->ops; + const struct vfe_hw_ops *ops = vfe->res->hw_ops; struct media_entity *sensor; unsigned long flags; unsigned int frame_skip = 0; @@ -560,7 +560,7 @@ static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) unsigned long flags; spin_lock_irqsave(&vfe->output_lock, flags); - vfe->ops->reg_update_clear(vfe, line_id); + vfe->res->hw_ops->reg_update_clear(vfe, line_id); output = &vfe->line[line_id].output; diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c index ef6b34c915df..1bd3a6ef1d04 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c @@ -892,7 +892,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) u32 value0, value1; int i, j; - vfe->ops->isr_read(vfe, &value0, &value1); + vfe->res->hw_ops->isr_read(vfe, &value0, &value1); dev_dbg(vfe->camss->dev, "VFE: status0 = 0x%08x, status1 = 0x%08x\n", value0, value1); @@ -901,7 +901,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) vfe->isr_ops.reset_ack(vfe); if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) - vfe->ops->violation_read(vfe); + vfe->res->hw_ops->violation_read(vfe); if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK) vfe->isr_ops.halt_ack(vfe); diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c index 7655d22a9fda..ce0719106bd3 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c @@ -1050,7 +1050,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) u32 value0, value1; int i, j; - vfe->ops->isr_read(vfe, &value0, &value1); + vfe->res->hw_ops->isr_read(vfe, &value0, &value1); dev_dbg(vfe->camss->dev, "VFE: status0 = 0x%08x, status1 = 0x%08x\n", value0, value1); @@ -1059,12 +1059,12 @@ static irqreturn_t vfe_isr(int irq, void *dev) vfe->isr_ops.reset_ack(vfe); if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) - vfe->ops->violation_read(vfe); + vfe->res->hw_ops->violation_read(vfe); if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK) vfe->isr_ops.halt_ack(vfe); - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i)) vfe->isr_ops.reg_update(vfe, i); diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-8.c b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c index f52fa30f3853..6b59c8107a3c 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-8.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c @@ -980,7 +980,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) u32 value0, value1; int i, j; - vfe->ops->isr_read(vfe, &value0, &value1); + vfe->res->hw_ops->isr_read(vfe, &value0, &value1); dev_dbg(vfe->camss->dev, "VFE: status0 = 0x%08x, status1 = 0x%08x\n", value0, value1); @@ -989,12 +989,12 @@ static irqreturn_t vfe_isr(int irq, void *dev) vfe->isr_ops.reset_ack(vfe); if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) - vfe->ops->violation_read(vfe); + vfe->res->hw_ops->violation_read(vfe); if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK) vfe->isr_ops.halt_ack(vfe); - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i)) vfe->isr_ops.reg_update(vfe, i); diff --git a/drivers/media/platform/qcom/camss/camss-vfe-gen1.c b/drivers/media/platform/qcom/camss/camss-vfe-gen1.c index 239d3d4ac666..eb33c03df27e 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-gen1.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-gen1.c @@ -37,7 +37,7 @@ static int vfe_disable_output(struct vfe_line *line) { struct vfe_device *vfe = to_vfe(line); struct vfe_output *output = &line->output; - const struct vfe_hw_ops *ops = vfe->ops; + const struct vfe_hw_ops *ops = vfe->res->hw_ops; unsigned long flags; unsigned long time; unsigned int i; @@ -162,14 +162,14 @@ static void vfe_output_frame_drop(struct vfe_device *vfe, vfe->ops_gen1->wm_set_framedrop_pattern(vfe, output->wm_idx[i], drop_pattern); } - vfe->ops->reg_update(vfe, container_of(output, struct vfe_line, output)->id); + vfe->res->hw_ops->reg_update(vfe, container_of(output, struct vfe_line, output)->id); } static int vfe_enable_output(struct vfe_line *line) { struct vfe_device *vfe = to_vfe(line); struct vfe_output *output = &line->output; - const struct vfe_hw_ops *ops = vfe->ops; + const struct vfe_hw_ops *ops = vfe->res->hw_ops; struct media_entity *sensor; unsigned long flags; unsigned int frame_skip = 0; @@ -545,7 +545,7 @@ static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) unsigned long flags; spin_lock_irqsave(&vfe->output_lock, flags); - vfe->ops->reg_update_clear(vfe, line_id); + vfe->res->hw_ops->reg_update_clear(vfe, line_id); output = &line->output; diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index d875237cf244..459c70a4b319 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -296,7 +296,7 @@ int vfe_reset(struct vfe_device *vfe) reinit_completion(&vfe->reset_complete); - vfe->ops->global_reset(vfe); + vfe->res->hw_ops->global_reset(vfe); time = wait_for_completion_timeout(&vfe->reset_complete, msecs_to_jiffies(VFE_RESET_TIMEOUT_MS)); @@ -312,7 +312,7 @@ static void vfe_init_outputs(struct vfe_device *vfe) { int i; - for (i = 0; i < vfe->line_num; i++) { + for (i = 0; i < vfe->res->line_num; i++) { struct vfe_output *output = &vfe->line[i].output; output->state = VFE_OUTPUT_OFF; @@ -421,7 +421,7 @@ static int vfe_disable_output(struct vfe_line *line) spin_lock_irqsave(&vfe->output_lock, flags); for (i = 0; i < output->wm_num; i++) - vfe->ops->vfe_wm_stop(vfe, output->wm_idx[i]); + vfe->res->hw_ops->vfe_wm_stop(vfe, output->wm_idx[i]); output->gen2.active_num = 0; spin_unlock_irqrestore(&vfe->output_lock, flags); @@ -537,7 +537,7 @@ static int vfe_set_clock_rates(struct vfe_device *vfe) int i, j; int ret; - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) { + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) { ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity, &pixel_clock[i]); if (ret) @@ -551,7 +551,7 @@ static int vfe_set_clock_rates(struct vfe_device *vfe) u64 min_rate = 0; long rate; - for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) { + for (j = VFE_LINE_RDI0; j < vfe->res->line_num; j++) { u32 tmp; u8 bpp; @@ -618,7 +618,7 @@ static int vfe_check_clock_rates(struct vfe_device *vfe) int i, j; int ret; - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) { + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) { ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity, &pixel_clock[i]); if (ret) @@ -632,7 +632,7 @@ static int vfe_check_clock_rates(struct vfe_device *vfe) u64 min_rate = 0; unsigned long rate; - for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) { + for (j = VFE_LINE_RDI0; j < vfe->res->line_num; j++) { u32 tmp; u8 bpp; @@ -675,7 +675,7 @@ int vfe_get(struct vfe_device *vfe) mutex_lock(&vfe->power_lock); if (vfe->power_count == 0) { - ret = vfe->ops->pm_domain_on(vfe); + ret = vfe->res->hw_ops->pm_domain_on(vfe); if (ret < 0) goto error_pm_domain; @@ -700,7 +700,7 @@ int vfe_get(struct vfe_device *vfe) vfe_init_outputs(vfe); - vfe->ops->hw_version(vfe); + vfe->res->hw_ops->hw_version(vfe); } else { ret = vfe_check_clock_rates(vfe); if (ret < 0) @@ -718,7 +718,7 @@ error_reset: error_pm_runtime_get: pm_runtime_put_sync(vfe->camss->dev); error_domain_off: - vfe->ops->pm_domain_off(vfe); + vfe->res->hw_ops->pm_domain_off(vfe); error_pm_domain: mutex_unlock(&vfe->power_lock); @@ -740,11 +740,11 @@ void vfe_put(struct vfe_device *vfe) } else if (vfe->power_count == 1) { if (vfe->was_streaming) { vfe->was_streaming = 0; - vfe->ops->vfe_halt(vfe); + vfe->res->hw_ops->vfe_halt(vfe); } camss_disable_clocks(vfe->nclocks, vfe->clock); pm_runtime_put_sync(vfe->camss->dev); - vfe->ops->pm_domain_off(vfe); + vfe->res->hw_ops->pm_domain_off(vfe); } vfe->power_count--; @@ -834,12 +834,12 @@ static int vfe_set_stream(struct v4l2_subdev *sd, int enable) if (enable) { line->output.state = VFE_OUTPUT_RESERVED; - ret = vfe->ops->vfe_enable(line); + ret = vfe->res->hw_ops->vfe_enable(line); if (ret < 0) dev_err(vfe->camss->dev, "Failed to enable vfe outputs\n"); } else { - ret = vfe->ops->vfe_disable(line); + ret = vfe->res->hw_ops->vfe_disable(line); if (ret < 0) dev_err(vfe->camss->dev, "Failed to disable vfe outputs\n"); @@ -1376,23 +1376,24 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, int i, j; int ret; - vfe->ops = res->ops; - - if (!res->line_num) + if (!res->vfe.line_num) return -EINVAL; + vfe->res = &res->vfe; + vfe->res->hw_ops->subdev_init(dev, vfe); + /* Power domain */ - if (res->pd_name) { + if (res->vfe.pd_name) { vfe->genpd = dev_pm_domain_attach_by_name(camss->dev, - res->pd_name); + res->vfe.pd_name); if (IS_ERR(vfe->genpd)) { ret = PTR_ERR(vfe->genpd); return ret; } } - if (!vfe->genpd && res->has_pd) { + if (!vfe->genpd && res->vfe.has_pd) { /* * Legacy magic index. * Requires @@ -1409,9 +1410,6 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, return PTR_ERR(vfe->genpd); } - vfe->line_num = res->line_num; - vfe->ops->subdev_init(dev, vfe); - /* Memory */ vfe->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]); @@ -1429,7 +1427,7 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, vfe->irq = ret; snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d", dev_name(dev), MSM_VFE_NAME, id); - ret = devm_request_irq(dev, vfe->irq, vfe->ops->isr, + ret = devm_request_irq(dev, vfe->irq, vfe->res->hw_ops->isr, IRQF_TRIGGER_RISING, vfe->irq_name, vfe); if (ret < 0) { dev_err(dev, "request_irq failed: %d\n", ret); @@ -1488,7 +1486,7 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, vfe->id = id; vfe->reg_update = 0; - for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) { + for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) { struct vfe_line *l = &vfe->line[i]; l->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; @@ -1636,7 +1634,7 @@ int msm_vfe_register_entities(struct vfe_device *vfe, int ret; int i; - for (i = 0; i < vfe->line_num; i++) { + for (i = 0; i < vfe->res->line_num; i++) { char name[32]; sd = &vfe->line[i].subdev; @@ -1743,7 +1741,7 @@ void msm_vfe_unregister_entities(struct vfe_device *vfe) mutex_destroy(&vfe->power_lock); mutex_destroy(&vfe->stream_lock); - for (i = 0; i < vfe->line_num; i++) { + for (i = 0; i < vfe->res->line_num; i++) { struct v4l2_subdev *sd = &vfe->line[i].subdev; struct camss_video *video_out = &vfe->line[i].video_out; @@ -1755,5 +1753,5 @@ void msm_vfe_unregister_entities(struct vfe_device *vfe) bool vfe_is_lite(struct vfe_device *vfe) { - return vfe->camss->res->vfe_res[vfe->id].is_lite; + return vfe->camss->res->vfe_res[vfe->id].vfe.is_lite; } diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h index 0572c9b08e11..87fc159c48cc 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.h +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -126,6 +126,14 @@ struct vfe_isr_ops { void (*wm_done)(struct vfe_device *vfe, u8 wm); }; +struct vfe_subdev_resources { + bool is_lite; + u8 line_num; + bool has_pd; + char *pd_name; + const struct vfe_hw_ops *hw_ops; +}; + struct vfe_device { struct camss *camss; u8 id; @@ -143,10 +151,9 @@ struct vfe_device { spinlock_t output_lock; enum vfe_line_id wm_output_map[MSM_VFE_IMAGE_MASTERS_NUM]; struct vfe_line line[VFE_LINE_NUM_MAX]; - u8 line_num; u32 reg_update; u8 was_streaming; - const struct vfe_hw_ops *ops; + const struct vfe_subdev_resources *res; const struct vfe_hw_ops_gen1 *ops_gen1; struct vfe_isr_ops isr_ops; struct camss_video_ops video_ops; diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 1923615f0eea..34bac001073c 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -43,7 +43,9 @@ static const struct camss_subdev_resources csiphy_res_8x16[] = { { 100000000, 200000000 } }, .reg = { "csiphy0", "csiphy0_clk_mux" }, .interrupt = { "csiphy0" }, - .ops = &csiphy_ops_2ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_2ph_1_0 + } }, /* CSIPHY1 */ @@ -56,7 +58,9 @@ static const struct camss_subdev_resources csiphy_res_8x16[] = { { 100000000, 200000000 } }, .reg = { "csiphy1", "csiphy1_clk_mux" }, .interrupt = { "csiphy1" }, - .ops = &csiphy_ops_2ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_2ph_1_0 + } } }; @@ -76,7 +80,9 @@ static const struct camss_subdev_resources csid_res_8x16[] = { { 0 } }, .reg = { "csid0" }, .interrupt = { "csid0" }, - .ops = &csid_ops_4_1, + .csid = { + .hw_ops = &csid_ops_4_1 + } }, /* CSID1 */ @@ -94,7 +100,9 @@ static const struct camss_subdev_resources csid_res_8x16[] = { { 0 } }, .reg = { "csid1" }, .interrupt = { "csid1" }, - .ops = &csid_ops_4_1, + .csid = { + .hw_ops = &csid_ops_4_1 + } }, }; @@ -105,8 +113,7 @@ static const struct camss_subdev_resources ispif_res_8x16 = { "csi1", "csi1_pix", "csi1_rdi" }, .clock_for_reset = { "vfe0", "csi_vfe0" }, .reg = { "ispif", "csi_clk_mux" }, - .interrupt = { "ispif" } - + .interrupt = { "ispif" }, }; static const struct camss_subdev_resources vfe_res_8x16[] = { @@ -128,8 +135,10 @@ static const struct camss_subdev_resources vfe_res_8x16[] = { { 0 } }, .reg = { "vfe0" }, .interrupt = { "vfe0" }, - .line_num = 3, - .ops = &vfe_ops_4_1 + .vfe = { + .line_num = 3, + .hw_ops = &vfe_ops_4_1 + } } }; @@ -144,7 +153,9 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = { { 100000000, 200000000, 266666667 } }, .reg = { "csiphy0", "csiphy0_clk_mux" }, .interrupt = { "csiphy0" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0 + } }, /* CSIPHY1 */ @@ -157,7 +168,9 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = { { 100000000, 200000000, 266666667 } }, .reg = { "csiphy1", "csiphy1_clk_mux" }, .interrupt = { "csiphy1" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0 + } }, /* CSIPHY2 */ @@ -170,7 +183,9 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = { { 100000000, 200000000, 266666667 } }, .reg = { "csiphy2", "csiphy2_clk_mux" }, .interrupt = { "csiphy2" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0 + } } }; @@ -190,7 +205,9 @@ static const struct camss_subdev_resources csid_res_8x96[] = { { 0 } }, .reg = { "csid0" }, .interrupt = { "csid0" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7 + } }, /* CSID1 */ @@ -208,7 +225,9 @@ static const struct camss_subdev_resources csid_res_8x96[] = { { 0 } }, .reg = { "csid1" }, .interrupt = { "csid1" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7 + } }, /* CSID2 */ @@ -226,7 +245,9 @@ static const struct camss_subdev_resources csid_res_8x96[] = { { 0 } }, .reg = { "csid2" }, .interrupt = { "csid2" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7 + } }, /* CSID3 */ @@ -244,7 +265,9 @@ static const struct camss_subdev_resources csid_res_8x96[] = { { 0 } }, .reg = { "csid3" }, .interrupt = { "csid3" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7 + } } }; @@ -257,7 +280,7 @@ static const struct camss_subdev_resources ispif_res_8x96 = { "csi3", "csi3_pix", "csi3_rdi" }, .clock_for_reset = { "vfe0", "csi_vfe0", "vfe1", "csi_vfe1" }, .reg = { "ispif", "csi_clk_mux" }, - .interrupt = { "ispif" } + .interrupt = { "ispif" }, }; static const struct camss_subdev_resources vfe_res_8x96[] = { @@ -277,9 +300,11 @@ static const struct camss_subdev_resources vfe_res_8x96[] = { { 0 } }, .reg = { "vfe0" }, .interrupt = { "vfe0" }, - .line_num = 3, - .has_pd = true, - .ops = &vfe_ops_4_7 + .vfe = { + .line_num = 3, + .has_pd = true, + .hw_ops = &vfe_ops_4_7 + } }, /* VFE1 */ @@ -298,9 +323,11 @@ static const struct camss_subdev_resources vfe_res_8x96[] = { { 0 } }, .reg = { "vfe1" }, .interrupt = { "vfe1" }, - .line_num = 3, - .has_pd = true, - .ops = &vfe_ops_4_7 + .vfe = { + .line_num = 3, + .has_pd = true, + .hw_ops = &vfe_ops_4_7 + } } }; @@ -317,7 +344,9 @@ static const struct camss_subdev_resources csiphy_res_660[] = { { 0 } }, .reg = { "csiphy0", "csiphy0_clk_mux" }, .interrupt = { "csiphy0" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0 + } }, /* CSIPHY1 */ @@ -332,7 +361,9 @@ static const struct camss_subdev_resources csiphy_res_660[] = { { 0 } }, .reg = { "csiphy1", "csiphy1_clk_mux" }, .interrupt = { "csiphy1" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0 + } }, /* CSIPHY2 */ @@ -347,7 +378,9 @@ static const struct camss_subdev_resources csiphy_res_660[] = { { 0 } }, .reg = { "csiphy2", "csiphy2_clk_mux" }, .interrupt = { "csiphy2" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0 + } } }; @@ -370,7 +403,9 @@ static const struct camss_subdev_resources csid_res_660[] = { { 0 } }, .reg = { "csid0" }, .interrupt = { "csid0" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7 + } }, /* CSID1 */ @@ -391,7 +426,9 @@ static const struct camss_subdev_resources csid_res_660[] = { { 0 } }, .reg = { "csid1" }, .interrupt = { "csid1" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7 + } }, /* CSID2 */ @@ -412,7 +449,9 @@ static const struct camss_subdev_resources csid_res_660[] = { { 0 } }, .reg = { "csid2" }, .interrupt = { "csid2" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7 + } }, /* CSID3 */ @@ -433,7 +472,9 @@ static const struct camss_subdev_resources csid_res_660[] = { { 0 } }, .reg = { "csid3" }, .interrupt = { "csid3" }, - .ops = &csid_ops_4_7, + .csid = { + .hw_ops = &csid_ops_4_7 + } } }; @@ -446,7 +487,7 @@ static const struct camss_subdev_resources ispif_res_660 = { "csi3", "csi3_pix", "csi3_rdi" }, .clock_for_reset = { "vfe0", "csi_vfe0", "vfe1", "csi_vfe1" }, .reg = { "ispif", "csi_clk_mux" }, - .interrupt = { "ispif" } + .interrupt = { "ispif" }, }; static const struct camss_subdev_resources vfe_res_660[] = { @@ -469,9 +510,11 @@ static const struct camss_subdev_resources vfe_res_660[] = { { 0 } }, .reg = { "vfe0" }, .interrupt = { "vfe0" }, - .line_num = 3, - .has_pd = true, - .ops = &vfe_ops_4_8 + .vfe = { + .line_num = 3, + .has_pd = true, + .hw_ops = &vfe_ops_4_8 + } }, /* VFE1 */ @@ -493,9 +536,11 @@ static const struct camss_subdev_resources vfe_res_660[] = { { 0 } }, .reg = { "vfe1" }, .interrupt = { "vfe1" }, - .line_num = 3, - .has_pd = true, - .ops = &vfe_ops_4_8 + .vfe = { + .line_num = 3, + .has_pd = true, + .hw_ops = &vfe_ops_4_8 + } } }; @@ -516,7 +561,9 @@ static const struct camss_subdev_resources csiphy_res_845[] = { { 19200000, 240000000, 269333333 } }, .reg = { "csiphy0" }, .interrupt = { "csiphy0" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0 + } }, /* CSIPHY1 */ @@ -535,7 +582,9 @@ static const struct camss_subdev_resources csiphy_res_845[] = { { 19200000, 240000000, 269333333 } }, .reg = { "csiphy1" }, .interrupt = { "csiphy1" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0 + } }, /* CSIPHY2 */ @@ -554,7 +603,9 @@ static const struct camss_subdev_resources csiphy_res_845[] = { { 19200000, 240000000, 269333333 } }, .reg = { "csiphy2" }, .interrupt = { "csiphy2" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0 + } }, /* CSIPHY3 */ @@ -573,7 +624,9 @@ static const struct camss_subdev_resources csiphy_res_845[] = { { 19200000, 240000000, 269333333 } }, .reg = { "csiphy3" }, .interrupt = { "csiphy3" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0 + } } }; @@ -596,7 +649,9 @@ static const struct camss_subdev_resources csid_res_845[] = { { 384000000 } }, .reg = { "csid0" }, .interrupt = { "csid0" }, - .ops = &csid_ops_gen2 + .csid = { + .hw_ops = &csid_ops_gen2 + } }, /* CSID1 */ @@ -617,7 +672,9 @@ static const struct camss_subdev_resources csid_res_845[] = { { 384000000 } }, .reg = { "csid1" }, .interrupt = { "csid1" }, - .ops = &csid_ops_gen2 + .csid = { + .hw_ops = &csid_ops_gen2 + } }, /* CSID2 */ @@ -638,8 +695,10 @@ static const struct camss_subdev_resources csid_res_845[] = { { 384000000 } }, .reg = { "csid2" }, .interrupt = { "csid2" }, - .is_lite = true, - .ops = &csid_ops_gen2 + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2 + } } }; @@ -662,9 +721,11 @@ static const struct camss_subdev_resources vfe_res_845[] = { { 384000000 } }, .reg = { "vfe0" }, .interrupt = { "vfe0" }, - .line_num = 4, - .has_pd = true, - .ops = &vfe_ops_170 + .vfe = { + .line_num = 4, + .has_pd = true, + .hw_ops = &vfe_ops_170 + } }, /* VFE1 */ @@ -685,9 +746,11 @@ static const struct camss_subdev_resources vfe_res_845[] = { { 384000000 } }, .reg = { "vfe1" }, .interrupt = { "vfe1" }, - .line_num = 4, - .has_pd = true, - .ops = &vfe_ops_170 + .vfe = { + .line_num = 4, + .has_pd = true, + .hw_ops = &vfe_ops_170 + } }, /* VFE-lite */ @@ -707,9 +770,11 @@ static const struct camss_subdev_resources vfe_res_845[] = { { 384000000 } }, .reg = { "vfe_lite" }, .interrupt = { "vfe_lite" }, - .is_lite = true, - .line_num = 4, - .ops = &vfe_ops_170 + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_170 + } } }; @@ -722,7 +787,9 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { { 300000000 } }, .reg = { "csiphy0" }, .interrupt = { "csiphy0" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0 + } }, /* CSIPHY1 */ { @@ -732,7 +799,9 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { { 300000000 } }, .reg = { "csiphy1" }, .interrupt = { "csiphy1" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0 + } }, /* CSIPHY2 */ { @@ -742,7 +811,9 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { { 300000000 } }, .reg = { "csiphy2" }, .interrupt = { "csiphy2" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0 + } }, /* CSIPHY3 */ { @@ -752,7 +823,9 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { { 300000000 } }, .reg = { "csiphy3" }, .interrupt = { "csiphy3" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0 + } }, /* CSIPHY4 */ { @@ -762,7 +835,9 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { { 300000000 } }, .reg = { "csiphy4" }, .interrupt = { "csiphy4" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0 + } }, /* CSIPHY5 */ { @@ -772,7 +847,9 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { { 300000000 } }, .reg = { "csiphy5" }, .interrupt = { "csiphy5" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0 + } } }; @@ -788,7 +865,9 @@ static const struct camss_subdev_resources csid_res_8250[] = { { 0 } }, .reg = { "csid0" }, .interrupt = { "csid0" }, - .ops = &csid_ops_gen2 + .csid = { + .hw_ops = &csid_ops_gen2 + } }, /* CSID1 */ { @@ -801,7 +880,9 @@ static const struct camss_subdev_resources csid_res_8250[] = { { 0 } }, .reg = { "csid1" }, .interrupt = { "csid1" }, - .ops = &csid_ops_gen2 + .csid = { + .hw_ops = &csid_ops_gen2 + } }, /* CSID2 */ { @@ -813,8 +894,10 @@ static const struct camss_subdev_resources csid_res_8250[] = { { 0 } }, .reg = { "csid2" }, .interrupt = { "csid2" }, - .is_lite = true, - .ops = &csid_ops_gen2 + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2 + } }, /* CSID3 */ { @@ -826,8 +909,10 @@ static const struct camss_subdev_resources csid_res_8250[] = { { 0 } }, .reg = { "csid3" }, .interrupt = { "csid3" }, - .is_lite = true, - .ops = &csid_ops_gen2 + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2 + } } }; @@ -849,10 +934,12 @@ static const struct camss_subdev_resources vfe_res_8250[] = { { 0 } }, .reg = { "vfe0" }, .interrupt = { "vfe0" }, - .pd_name = "ife0", - .line_num = 3, - .has_pd = true, - .ops = &vfe_ops_480 + .vfe = { + .line_num = 3, + .has_pd = true, + .pd_name = "ife0", + .hw_ops = &vfe_ops_480 + } }, /* VFE1 */ { @@ -871,10 +958,12 @@ static const struct camss_subdev_resources vfe_res_8250[] = { { 0 } }, .reg = { "vfe1" }, .interrupt = { "vfe1" }, - .pd_name = "ife1", - .line_num = 3, - .has_pd = true, - .ops = &vfe_ops_480 + .vfe = { + .line_num = 3, + .has_pd = true, + .pd_name = "ife1", + .hw_ops = &vfe_ops_480 + } }, /* VFE2 (lite) */ { @@ -892,9 +981,11 @@ static const struct camss_subdev_resources vfe_res_8250[] = { { 0 } }, .reg = { "vfe_lite0" }, .interrupt = { "vfe_lite0" }, - .is_lite = true, - .line_num = 4, - .ops = &vfe_ops_480 + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_480 + } }, /* VFE3 (lite) */ { @@ -912,9 +1003,11 @@ static const struct camss_subdev_resources vfe_res_8250[] = { { 0 } }, .reg = { "vfe_lite1" }, .interrupt = { "vfe_lite1" }, - .is_lite = true, - .line_num = 4, - .ops = &vfe_ops_480 + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_480 + } }, }; @@ -950,7 +1043,9 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = { { 300000000 } }, .reg = { "csiphy0" }, .interrupt = { "csiphy0" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0 + } }, /* CSIPHY1 */ { @@ -960,7 +1055,9 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = { { 300000000 } }, .reg = { "csiphy1" }, .interrupt = { "csiphy1" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0 + } }, /* CSIPHY2 */ { @@ -970,7 +1067,9 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = { { 300000000 } }, .reg = { "csiphy2" }, .interrupt = { "csiphy2" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0 + } }, /* CSIPHY3 */ { @@ -980,7 +1079,9 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = { { 300000000 } }, .reg = { "csiphy3" }, .interrupt = { "csiphy3" }, - .ops = &csiphy_ops_3ph_1_0 + .csiphy = { + .hw_ops = &csiphy_ops_3ph_1_0 + } }, }; @@ -995,7 +1096,9 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { { 0 } }, .reg = { "csid0" }, .interrupt = { "csid0" }, - .ops = &csid_ops_gen2 + .csid = { + .hw_ops = &csid_ops_gen2 + } }, /* CSID1 */ { @@ -1007,7 +1110,9 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { { 0 } }, .reg = { "csid1" }, .interrupt = { "csid1" }, - .ops = &csid_ops_gen2 + .csid = { + .hw_ops = &csid_ops_gen2 + } }, /* CSID2 */ { @@ -1019,7 +1124,9 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { { 0 } }, .reg = { "csid2" }, .interrupt = { "csid2" }, - .ops = &csid_ops_gen2 + .csid = { + .hw_ops = &csid_ops_gen2 + } }, /* CSID3 */ { @@ -1031,7 +1138,9 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { { 0 } }, .reg = { "csid3" }, .interrupt = { "csid3" }, - .ops = &csid_ops_gen2 + .csid = { + .hw_ops = &csid_ops_gen2 + } }, /* CSID_LITE0 */ { @@ -1042,8 +1151,10 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { { 0 }, }, .reg = { "csid0_lite" }, .interrupt = { "csid0_lite" }, - .is_lite = true, - .ops = &csid_ops_gen2 + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2 + } }, /* CSID_LITE1 */ { @@ -1054,8 +1165,10 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { { 0 }, }, .reg = { "csid1_lite" }, .interrupt = { "csid1_lite" }, - .is_lite = true, - .ops = &csid_ops_gen2 + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2 + } }, /* CSID_LITE2 */ { @@ -1066,8 +1179,10 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { { 0 }, }, .reg = { "csid2_lite" }, .interrupt = { "csid2_lite" }, - .is_lite = true, - .ops = &csid_ops_gen2 + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2 + } }, /* CSID_LITE3 */ { @@ -1078,8 +1193,10 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { { 0 }, }, .reg = { "csid3_lite" }, .interrupt = { "csid3_lite" }, - .is_lite = true, - .ops = &csid_ops_gen2 + .csid = { + .is_lite = true, + .hw_ops = &csid_ops_gen2 + } } }; @@ -1096,9 +1213,11 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { { 0 }, }, .reg = { "vfe0" }, .interrupt = { "vfe0" }, - .pd_name = "ife0", - .line_num = 4, - .ops = &vfe_ops_170 + .vfe = { + .line_num = 4, + .pd_name = "ife0", + .hw_ops = &vfe_ops_170 + } }, /* VFE1 */ { @@ -1112,9 +1231,11 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { { 0 }, }, .reg = { "vfe1" }, .interrupt = { "vfe1" }, - .pd_name = "ife1", - .line_num = 4, - .ops = &vfe_ops_170 + .vfe = { + .line_num = 4, + .pd_name = "ife1", + .hw_ops = &vfe_ops_170 + } }, /* VFE2 */ { @@ -1128,9 +1249,11 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { { 0 }, }, .reg = { "vfe2" }, .interrupt = { "vfe2" }, - .pd_name = "ife2", - .line_num = 4, - .ops = &vfe_ops_170 + .vfe = { + .line_num = 4, + .pd_name = "ife2", + .hw_ops = &vfe_ops_170 + } }, /* VFE3 */ { @@ -1144,9 +1267,11 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { { 0 }, }, .reg = { "vfe3" }, .interrupt = { "vfe3" }, - .pd_name = "ife3", - .line_num = 4, - .ops = &vfe_ops_170 + .vfe = { + .line_num = 4, + .pd_name = "ife3", + .hw_ops = &vfe_ops_170 + } }, /* VFE_LITE_0 */ { @@ -1159,9 +1284,11 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { { 320000000, 400000000, 480000000, 600000000 }, }, .reg = { "vfe_lite0" }, .interrupt = { "vfe_lite0" }, - .is_lite = true, - .line_num = 4, - .ops = &vfe_ops_170 + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_170 + } }, /* VFE_LITE_1 */ { @@ -1174,9 +1301,11 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { { 320000000, 400000000, 480000000, 600000000 }, }, .reg = { "vfe_lite1" }, .interrupt = { "vfe_lite1" }, - .is_lite = true, - .line_num = 4, - .ops = &vfe_ops_170 + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_170 + } }, /* VFE_LITE_2 */ { @@ -1189,9 +1318,11 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { { 320000000, 400000000, 480000000, 600000000, }, }, .reg = { "vfe_lite2" }, .interrupt = { "vfe_lite2" }, - .is_lite = true, - .line_num = 4, - .ops = &vfe_ops_170 + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_170 + } }, /* VFE_LITE_3 */ { @@ -1204,9 +1335,11 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { { 320000000, 400000000, 480000000, 600000000 }, }, .reg = { "vfe_lite3" }, .interrupt = { "vfe_lite3" }, - .is_lite = true, - .line_num = 4, - .ops = &vfe_ops_170 + .vfe = { + .is_lite = true, + .line_num = 4, + .hw_ops = &vfe_ops_170 + } }, }; @@ -1375,7 +1508,7 @@ int camss_pm_domain_on(struct camss *camss, int id) if (id < camss->res->vfe_num) { struct vfe_device *vfe = &camss->vfe[id]; - ret = vfe->ops->pm_domain_on(vfe); + ret = vfe->res->hw_ops->pm_domain_on(vfe); } return ret; @@ -1386,7 +1519,7 @@ void camss_pm_domain_off(struct camss *camss, int id) if (id < camss->res->vfe_num) { struct vfe_device *vfe = &camss->vfe[id]; - vfe->ops->pm_domain_off(vfe); + vfe->res->hw_ops->pm_domain_off(vfe); } } @@ -1628,7 +1761,7 @@ static int camss_register_entities(struct camss *camss) for (i = 0; i < camss->ispif->line_num; i++) for (k = 0; k < camss->res->vfe_num; k++) - for (j = 0; j < camss->vfe[k].line_num; j++) { + for (j = 0; j < camss->vfe[k].res->line_num; j++) { struct v4l2_subdev *ispif = &camss->ispif->line[i].subdev; struct v4l2_subdev *vfe = &camss->vfe[k].line[j].subdev; @@ -1649,7 +1782,7 @@ static int camss_register_entities(struct camss *camss) } else { for (i = 0; i < camss->res->csid_num; i++) for (k = 0; k < camss->res->vfe_num; k++) - for (j = 0; j < camss->vfe[k].line_num; j++) { + for (j = 0; j < camss->vfe[k].res->line_num; j++) { struct v4l2_subdev *csid = &camss->csid[i].subdev; struct v4l2_subdev *vfe = &camss->vfe[k].line[j].subdev; @@ -1810,7 +1943,7 @@ static int camss_configure_pd(struct camss *camss) /* count the # of VFEs which have flagged power-domain */ for (vfepd_num = i = 0; i < camss->res->vfe_num; i++) { - if (res->vfe_res[i].has_pd) + if (res->vfe_res[i].vfe.has_pd) vfepd_num++; } diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index ac15fe23a702..e6d9fba646a1 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -48,11 +48,11 @@ struct camss_subdev_resources { u32 clock_rate[CAMSS_RES_MAX][CAMSS_RES_MAX]; char *reg[CAMSS_RES_MAX]; char *interrupt[CAMSS_RES_MAX]; - char *pd_name; - u8 line_num; - bool has_pd; - bool is_lite; - const void *ops; + union { + struct csiphy_subdev_resources csiphy; + struct csid_subdev_resources csid; + struct vfe_subdev_resources vfe; + }; }; struct icc_bw_tbl { -- cgit v1.2.3 From 6c46cb0d0aa0a9339d317d55fd3895caa0e655d4 Mon Sep 17 00:00:00 2001 From: Radoslav Tsvetkov Date: Wed, 22 May 2024 18:46:53 +0300 Subject: media: qcom: camss: Attach formats to VFE resources Video node formats have direct dependency by the sub-device pad formats. Remove dependency for SoC version and move format definitions in device which creates video node. This commit attaches a struct to the VFE resources that holds format description, so it is much easier to assign them to the video node. No need to use a switch-case. NOTE: The mbus_bpp is used to calculate the clock rates and is different from bpp which is the bits per pixel written to memory. We need to keep both values to not break the calcualtions. Signed-off-by: Radoslav Tsvetkov Signed-off-by: Gjorgji Rosikopulos Acked-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/camss/camss-format.h | 57 ++++ drivers/media/platform/qcom/camss/camss-vfe.c | 359 ++++++++++++++++------- drivers/media/platform/qcom/camss/camss-vfe.h | 11 +- drivers/media/platform/qcom/camss/camss-video.c | 268 +---------------- drivers/media/platform/qcom/camss/camss-video.h | 4 +- drivers/media/platform/qcom/camss/camss.c | 80 +++-- drivers/media/platform/qcom/camss/camss.h | 1 + 7 files changed, 378 insertions(+), 402 deletions(-) create mode 100644 drivers/media/platform/qcom/camss/camss-format.h diff --git a/drivers/media/platform/qcom/camss/camss-format.h b/drivers/media/platform/qcom/camss/camss-format.h new file mode 100644 index 000000000000..e0231ca6a5ca --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-format.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * camss-format.h + * + * Qualcomm MSM Camera Subsystem - Format helpers + * + * Copyright (c) 2023, The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Technologies, Inc. + */ +#ifndef __CAMSS_FORMAT_H__ +#define __CAMSS_FORMAT_H__ + +#include + +#define PER_PLANE_DATA(plane, h_fract_num, h_fract_den, v_fract_num, v_fract_den, _bpp) \ + .hsub[(plane)].numerator = (h_fract_num), \ + .hsub[(plane)].denominator = (h_fract_den), \ + .vsub[(plane)].numerator = (v_fract_num), \ + .vsub[(plane)].denominator = (v_fract_den), \ + .bpp[(plane)] = (_bpp) + +/* + * struct fract - Represents a fraction + * @numerator: Store the numerator part of the fraction + * @denominator: Store the denominator part of the fraction + */ +struct fract { + u8 numerator; + u8 denominator; +}; + +/* + * struct camss_format_info - ISP media bus format information + * @code: V4L2 media bus format code + * @mbus_bpp: Media bus bits per pixel + * @pixelformat: V4L2 pixel format FCC identifier + * @planes: Number of planes + * @hsub: Horizontal subsampling (for each plane) + * @vsub: Vertical subsampling (for each plane) + * @bpp: Bits per pixel when stored in memory (for each plane) + */ +struct camss_format_info { + u32 code; + u32 mbus_bpp; + u32 pixelformat; + u8 planes; + struct fract hsub[3]; + struct fract vsub[3]; + unsigned int bpp[3]; +}; + +struct camss_formats { + unsigned int nformats; + const struct camss_format_info *formats; +}; + +#endif /* __CAMSS_FORMAT_H__ */ diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 459c70a4b319..2d5a64c055f1 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -32,96 +32,250 @@ #define SCALER_RATIO_MAX 16 -struct vfe_format { - u32 code; - u8 bpp; +static const struct camss_format_info formats_rdi_8x16[] = { + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8, V4L2_PIX_FMT_SBGGR8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8, V4L2_PIX_FMT_SGBRG8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8, V4L2_PIX_FMT_SGRBG8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8, V4L2_PIX_FMT_SRGGB8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10, V4L2_PIX_FMT_SBGGR10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10, V4L2_PIX_FMT_SGBRG10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10, V4L2_PIX_FMT_SGRBG10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10, V4L2_PIX_FMT_SRGGB10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12, V4L2_PIX_FMT_SBGGR12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12, V4L2_PIX_FMT_SGBRG12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12, V4L2_PIX_FMT_SGRBG12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12, V4L2_PIX_FMT_SRGGB12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_Y10_1X10, 10, V4L2_PIX_FMT_Y10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, +}; + +static const struct camss_format_info formats_rdi_8x96[] = { + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8, V4L2_PIX_FMT_SBGGR8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8, V4L2_PIX_FMT_SGBRG8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8, V4L2_PIX_FMT_SGRBG8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8, V4L2_PIX_FMT_SRGGB8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10, V4L2_PIX_FMT_SBGGR10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10, V4L2_PIX_FMT_SGBRG10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10, V4L2_PIX_FMT_SGRBG10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10, V4L2_PIX_FMT_SRGGB10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_SBGGR10, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12, V4L2_PIX_FMT_SBGGR12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12, V4L2_PIX_FMT_SGBRG12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12, V4L2_PIX_FMT_SGRBG12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12, V4L2_PIX_FMT_SRGGB12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SBGGR14_1X14, 14, V4L2_PIX_FMT_SBGGR14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_SGBRG14_1X14, 14, V4L2_PIX_FMT_SGBRG14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_SGRBG14_1X14, 14, V4L2_PIX_FMT_SGRBG14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_SRGGB14_1X14, 14, V4L2_PIX_FMT_SRGGB14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_Y10_1X10, 10, V4L2_PIX_FMT_Y10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_Y10, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, +}; + +static const struct camss_format_info formats_rdi_845[] = { + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8, V4L2_PIX_FMT_SBGGR8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8, V4L2_PIX_FMT_SGBRG8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8, V4L2_PIX_FMT_SGRBG8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8, V4L2_PIX_FMT_SRGGB8, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10, V4L2_PIX_FMT_SBGGR10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10, V4L2_PIX_FMT_SGBRG10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10, V4L2_PIX_FMT_SGRBG10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10, V4L2_PIX_FMT_SRGGB10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_SBGGR10, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12, V4L2_PIX_FMT_SBGGR12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12, V4L2_PIX_FMT_SGBRG12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12, V4L2_PIX_FMT_SGRBG12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12, V4L2_PIX_FMT_SRGGB12P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 12) }, + { MEDIA_BUS_FMT_SBGGR14_1X14, 14, V4L2_PIX_FMT_SBGGR14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_SGBRG14_1X14, 14, V4L2_PIX_FMT_SGBRG14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_SGRBG14_1X14, 14, V4L2_PIX_FMT_SGRBG14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_SRGGB14_1X14, 14, V4L2_PIX_FMT_SRGGB14P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 14) }, + { MEDIA_BUS_FMT_Y8_1X8, 8, V4L2_PIX_FMT_GREY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 8) }, + { MEDIA_BUS_FMT_Y10_1X10, 10, V4L2_PIX_FMT_Y10P, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 10) }, + { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_Y10, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, +}; + +static const struct camss_format_info formats_pix_8x16[] = { + { MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YVYU8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_UYVY8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_VYUY8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YVYU8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_UYVY8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_VYUY8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, +}; + +static const struct camss_format_info formats_pix_8x96[] = { + { MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YVYU8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_UYVY8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_VYUY8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YVYU8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_UYVY8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_VYUY8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1, + PER_PLANE_DATA(0, 1, 1, 2, 3, 8) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_NV16, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_NV61, 1, + PER_PLANE_DATA(0, 1, 1, 1, 2, 8) }, + { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, + { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1, + PER_PLANE_DATA(0, 1, 1, 1, 1, 16) }, +}; + +const struct camss_formats vfe_formats_rdi_8x16 = { + .nformats = ARRAY_SIZE(formats_rdi_8x16), + .formats = formats_rdi_8x16 }; -static const struct vfe_format formats_rdi_8x16[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, - { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, - { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, - { MEDIA_BUS_FMT_YVYU8_1X16, 8 }, - { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, - { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, - { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, - { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, - { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, - { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, - { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, - { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, - { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, - { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, - { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, - { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, - { MEDIA_BUS_FMT_Y10_1X10, 10 }, +const struct camss_formats vfe_formats_pix_8x16 = { + .nformats = ARRAY_SIZE(formats_pix_8x16), + .formats = formats_pix_8x16 }; -static const struct vfe_format formats_pix_8x16[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, - { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, - { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, - { MEDIA_BUS_FMT_YVYU8_1X16, 8 }, +const struct camss_formats vfe_formats_rdi_8x96 = { + .nformats = ARRAY_SIZE(formats_rdi_8x96), + .formats = formats_rdi_8x96 }; -static const struct vfe_format formats_rdi_8x96[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, - { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, - { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, - { MEDIA_BUS_FMT_YVYU8_1X16, 8 }, - { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, - { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, - { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, - { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, - { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, - { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, - { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, - { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, - { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16 }, - { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, - { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, - { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, - { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, - { MEDIA_BUS_FMT_SBGGR14_1X14, 14 }, - { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, - { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, - { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, - { MEDIA_BUS_FMT_Y10_1X10, 10 }, - { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 }, +const struct camss_formats vfe_formats_pix_8x96 = { + .nformats = ARRAY_SIZE(formats_pix_8x96), + .formats = formats_pix_8x96 }; -static const struct vfe_format formats_pix_8x96[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, - { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, - { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, - { MEDIA_BUS_FMT_YVYU8_1X16, 8 }, +const struct camss_formats vfe_formats_rdi_845 = { + .nformats = ARRAY_SIZE(formats_rdi_845), + .formats = formats_rdi_845 }; -static const struct vfe_format formats_rdi_845[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, - { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, - { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, - { MEDIA_BUS_FMT_YVYU8_1X16, 8 }, - { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, - { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, - { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, - { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, - { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, - { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, - { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, - { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, - { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16 }, - { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, - { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, - { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, - { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, - { MEDIA_BUS_FMT_SBGGR14_1X14, 14 }, - { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, - { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, - { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, - { MEDIA_BUS_FMT_Y8_1X8, 8 }, - { MEDIA_BUS_FMT_Y10_1X10, 10 }, - { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 }, +/* TODO: Replace with pix formats */ +const struct camss_formats vfe_formats_pix_845 = { + .nformats = ARRAY_SIZE(formats_rdi_845), + .formats = formats_rdi_845 }; /* @@ -132,18 +286,18 @@ static const struct vfe_format formats_rdi_845[] = { * * Return number of bits per pixel */ -static u8 vfe_get_bpp(const struct vfe_format *formats, +static u8 vfe_get_bpp(const struct camss_format_info *formats, unsigned int nformats, u32 code) { unsigned int i; for (i = 0; i < nformats; i++) if (code == formats[i].code) - return formats[i].bpp; + return formats[i].mbus_bpp; WARN(1, "Unknown format\n"); - return formats[0].bpp; + return formats[0].mbus_bpp; } static u32 vfe_find_code(u32 *code, unsigned int n_code, @@ -1495,32 +1649,12 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, init_completion(&l->output.sof); init_completion(&l->output.reg_update); - switch (camss->res->version) { - case CAMSS_8x16: - if (i == VFE_LINE_PIX) { - l->formats = formats_pix_8x16; - l->nformats = ARRAY_SIZE(formats_pix_8x16); - } else { - l->formats = formats_rdi_8x16; - l->nformats = ARRAY_SIZE(formats_rdi_8x16); - } - break; - case CAMSS_8x96: - case CAMSS_660: - if (i == VFE_LINE_PIX) { - l->formats = formats_pix_8x96; - l->nformats = ARRAY_SIZE(formats_pix_8x96); - } else { - l->formats = formats_rdi_8x96; - l->nformats = ARRAY_SIZE(formats_rdi_8x96); - } - break; - case CAMSS_845: - case CAMSS_8250: - case CAMSS_8280XP: - l->formats = formats_rdi_845; - l->nformats = ARRAY_SIZE(formats_rdi_845); - break; + if (i == VFE_LINE_PIX) { + l->nformats = res->vfe.formats_pix->nformats; + l->formats = res->vfe.formats_pix->formats; + } else { + l->nformats = res->vfe.formats_rdi->nformats; + l->formats = res->vfe.formats_rdi->formats; } } @@ -1684,10 +1818,13 @@ int msm_vfe_register_entities(struct vfe_device *vfe, video_out->bpl_alignment = 16; video_out->line_based = 1; } + + video_out->nformats = vfe->line[i].nformats; + video_out->formats = vfe->line[i].formats; + snprintf(name, ARRAY_SIZE(name), "%s%d_%s%d", MSM_VFE_NAME, vfe->id, "video", i); - ret = msm_video_register(video_out, v4l2_dev, name, - i == VFE_LINE_PIX ? 1 : 0); + ret = msm_video_register(video_out, v4l2_dev, name); if (ret < 0) { dev_err(dev, "Failed to register video node: %d\n", ret); diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h index 87fc159c48cc..10e2cc3c0b83 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.h +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -92,7 +92,7 @@ struct vfe_line { struct v4l2_rect crop; struct camss_video video_out; struct vfe_output output; - const struct vfe_format *formats; + const struct camss_format_info *formats; unsigned int nformats; }; @@ -132,6 +132,8 @@ struct vfe_subdev_resources { bool has_pd; char *pd_name; const struct vfe_hw_ops *hw_ops; + const struct camss_formats *formats_rdi; + const struct camss_formats *formats_pix; }; struct vfe_device { @@ -224,6 +226,13 @@ void vfe_pm_domain_off(struct vfe_device *vfe); */ int vfe_pm_domain_on(struct vfe_device *vfe); +extern const struct camss_formats vfe_formats_rdi_8x16; +extern const struct camss_formats vfe_formats_pix_8x16; +extern const struct camss_formats vfe_formats_rdi_8x96; +extern const struct camss_formats vfe_formats_pix_8x96; +extern const struct camss_formats vfe_formats_rdi_845; +extern const struct camss_formats vfe_formats_pix_845; + extern const struct vfe_hw_ops vfe_ops_4_1; extern const struct vfe_hw_ops vfe_ops_4_7; extern const struct vfe_hw_ops vfe_ops_4_8; diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 54cd82f74115..2fe706838469 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -24,244 +24,6 @@ #define CAMSS_FRAME_MAX_HEIGHT_RDI 8191 #define CAMSS_FRAME_MAX_HEIGHT_PIX 4096 -struct fract { - u8 numerator; - u8 denominator; -}; - -/* - * struct camss_format_info - ISP media bus format information - * @code: V4L2 media bus format code - * @pixelformat: V4L2 pixel format FCC identifier - * @planes: Number of planes - * @hsub: Horizontal subsampling (for each plane) - * @vsub: Vertical subsampling (for each plane) - * @bpp: Bits per pixel when stored in memory (for each plane) - */ -struct camss_format_info { - u32 code; - u32 pixelformat; - u8 planes; - struct fract hsub[3]; - struct fract vsub[3]; - unsigned int bpp[3]; -}; - -static const struct camss_format_info formats_rdi_8x16[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_VYUY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_YVYU, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, -}; - -static const struct camss_format_info formats_rdi_8x96[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_VYUY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_YVYU, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_PIX_FMT_SBGGR10, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SBGGR14_1X14, V4L2_PIX_FMT_SBGGR14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_SGBRG14_1X14, V4L2_PIX_FMT_SGBRG14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_SGRBG14_1X14, V4L2_PIX_FMT_SGRBG14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_SRGGB14_1X14, V4L2_PIX_FMT_SRGGB14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, V4L2_PIX_FMT_Y10, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, -}; - -static const struct camss_format_info formats_rdi_845[] = { - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_VYUY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_YVYU, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_PIX_FMT_SBGGR10, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 12 } }, - { MEDIA_BUS_FMT_SBGGR14_1X14, V4L2_PIX_FMT_SBGGR14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_SGBRG14_1X14, V4L2_PIX_FMT_SGBRG14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_SGRBG14_1X14, V4L2_PIX_FMT_SGRBG14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_SRGGB14_1X14, V4L2_PIX_FMT_SRGGB14P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 14 } }, - { MEDIA_BUS_FMT_Y8_1X8, V4L2_PIX_FMT_GREY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 8 } }, - { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1, - { { 1, 1 } }, { { 1, 1 } }, { 10 } }, - { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, V4L2_PIX_FMT_Y10, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, -}; - -static const struct camss_format_info formats_pix_8x16[] = { - { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, -}; - -static const struct camss_format_info formats_pix_8x96[] = { - { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1, - { { 1, 1 } }, { { 2, 3 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_NV16, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_NV61, 1, - { { 1, 1 } }, { { 1, 2 } }, { 8 } }, - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_VYUY, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, - { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_YVYU, 1, - { { 1, 1 } }, { { 1, 1 } }, { 16 } }, -}; - /* ----------------------------------------------------------------------------- * Helper functions */ @@ -969,7 +731,7 @@ static int msm_video_init_format(struct camss_video *video) */ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, - const char *name, int is_pix) + const char *name) { struct media_pad *pad = &video->pad; struct video_device *vdev; @@ -1006,34 +768,6 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, mutex_init(&video->lock); - switch (video->camss->res->version) { - case CAMSS_8x16: - if (is_pix) { - video->formats = formats_pix_8x16; - video->nformats = ARRAY_SIZE(formats_pix_8x16); - } else { - video->formats = formats_rdi_8x16; - video->nformats = ARRAY_SIZE(formats_rdi_8x16); - } - break; - case CAMSS_8x96: - case CAMSS_660: - if (is_pix) { - video->formats = formats_pix_8x96; - video->nformats = ARRAY_SIZE(formats_pix_8x96); - } else { - video->formats = formats_rdi_8x96; - video->nformats = ARRAY_SIZE(formats_rdi_8x96); - } - break; - case CAMSS_845: - case CAMSS_8250: - case CAMSS_8280XP: - video->formats = formats_rdi_845; - video->nformats = ARRAY_SIZE(formats_rdi_845); - break; - } - ret = msm_video_init_format(video); if (ret < 0) { dev_err(v4l2_dev->dev, "Failed to init format: %d\n", ret); diff --git a/drivers/media/platform/qcom/camss/camss-video.h b/drivers/media/platform/qcom/camss/camss-video.h index bdbae8424140..d3e56e240a88 100644 --- a/drivers/media/platform/qcom/camss/camss-video.h +++ b/drivers/media/platform/qcom/camss/camss-video.h @@ -33,8 +33,6 @@ struct camss_video_ops { enum vb2_buffer_state state); }; -struct camss_format_info; - struct camss_video { struct camss *camss; struct vb2_queue vb2_q; @@ -53,7 +51,7 @@ struct camss_video { }; int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, - const char *name, int is_pix); + const char *name); void msm_video_unregister(struct camss_video *video); diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 34bac001073c..8f2414eed89d 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -137,7 +137,9 @@ static const struct camss_subdev_resources vfe_res_8x16[] = { .interrupt = { "vfe0" }, .vfe = { .line_num = 3, - .hw_ops = &vfe_ops_4_1 + .hw_ops = &vfe_ops_4_1, + .formats_rdi = &vfe_formats_rdi_8x16, + .formats_pix = &vfe_formats_pix_8x16 } } }; @@ -303,7 +305,9 @@ static const struct camss_subdev_resources vfe_res_8x96[] = { .vfe = { .line_num = 3, .has_pd = true, - .hw_ops = &vfe_ops_4_7 + .hw_ops = &vfe_ops_4_7, + .formats_rdi = &vfe_formats_rdi_8x96, + .formats_pix = &vfe_formats_pix_8x96 } }, @@ -326,7 +330,9 @@ static const struct camss_subdev_resources vfe_res_8x96[] = { .vfe = { .line_num = 3, .has_pd = true, - .hw_ops = &vfe_ops_4_7 + .hw_ops = &vfe_ops_4_7, + .formats_rdi = &vfe_formats_rdi_8x96, + .formats_pix = &vfe_formats_pix_8x96 } } }; @@ -513,7 +519,9 @@ static const struct camss_subdev_resources vfe_res_660[] = { .vfe = { .line_num = 3, .has_pd = true, - .hw_ops = &vfe_ops_4_8 + .hw_ops = &vfe_ops_4_8, + .formats_rdi = &vfe_formats_rdi_8x96, + .formats_pix = &vfe_formats_pix_8x96 } }, @@ -539,7 +547,9 @@ static const struct camss_subdev_resources vfe_res_660[] = { .vfe = { .line_num = 3, .has_pd = true, - .hw_ops = &vfe_ops_4_8 + .hw_ops = &vfe_ops_4_8, + .formats_rdi = &vfe_formats_rdi_8x96, + .formats_pix = &vfe_formats_pix_8x96 } } }; @@ -724,7 +734,9 @@ static const struct camss_subdev_resources vfe_res_845[] = { .vfe = { .line_num = 4, .has_pd = true, - .hw_ops = &vfe_ops_170 + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 } }, @@ -749,7 +761,9 @@ static const struct camss_subdev_resources vfe_res_845[] = { .vfe = { .line_num = 4, .has_pd = true, - .hw_ops = &vfe_ops_170 + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 } }, @@ -773,7 +787,9 @@ static const struct camss_subdev_resources vfe_res_845[] = { .vfe = { .is_lite = true, .line_num = 4, - .hw_ops = &vfe_ops_170 + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 } } }; @@ -938,7 +954,9 @@ static const struct camss_subdev_resources vfe_res_8250[] = { .line_num = 3, .has_pd = true, .pd_name = "ife0", - .hw_ops = &vfe_ops_480 + .hw_ops = &vfe_ops_480, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 } }, /* VFE1 */ @@ -962,7 +980,9 @@ static const struct camss_subdev_resources vfe_res_8250[] = { .line_num = 3, .has_pd = true, .pd_name = "ife1", - .hw_ops = &vfe_ops_480 + .hw_ops = &vfe_ops_480, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 } }, /* VFE2 (lite) */ @@ -984,7 +1004,9 @@ static const struct camss_subdev_resources vfe_res_8250[] = { .vfe = { .is_lite = true, .line_num = 4, - .hw_ops = &vfe_ops_480 + .hw_ops = &vfe_ops_480, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 } }, /* VFE3 (lite) */ @@ -1006,7 +1028,9 @@ static const struct camss_subdev_resources vfe_res_8250[] = { .vfe = { .is_lite = true, .line_num = 4, - .hw_ops = &vfe_ops_480 + .hw_ops = &vfe_ops_480, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 } }, }; @@ -1216,7 +1240,9 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { .vfe = { .line_num = 4, .pd_name = "ife0", - .hw_ops = &vfe_ops_170 + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 } }, /* VFE1 */ @@ -1234,7 +1260,9 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { .vfe = { .line_num = 4, .pd_name = "ife1", - .hw_ops = &vfe_ops_170 + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 } }, /* VFE2 */ @@ -1252,7 +1280,9 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { .vfe = { .line_num = 4, .pd_name = "ife2", - .hw_ops = &vfe_ops_170 + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 } }, /* VFE3 */ @@ -1270,7 +1300,9 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { .vfe = { .line_num = 4, .pd_name = "ife3", - .hw_ops = &vfe_ops_170 + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 } }, /* VFE_LITE_0 */ @@ -1287,7 +1319,9 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { .vfe = { .is_lite = true, .line_num = 4, - .hw_ops = &vfe_ops_170 + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 } }, /* VFE_LITE_1 */ @@ -1304,7 +1338,9 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { .vfe = { .is_lite = true, .line_num = 4, - .hw_ops = &vfe_ops_170 + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 } }, /* VFE_LITE_2 */ @@ -1321,7 +1357,9 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { .vfe = { .is_lite = true, .line_num = 4, - .hw_ops = &vfe_ops_170 + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 } }, /* VFE_LITE_3 */ @@ -1338,7 +1376,9 @@ static const struct camss_subdev_resources vfe_res_sc8280xp[] = { .vfe = { .is_lite = true, .line_num = 4, - .hw_ops = &vfe_ops_170 + .hw_ops = &vfe_ops_170, + .formats_rdi = &vfe_formats_rdi_845, + .formats_pix = &vfe_formats_pix_845 } }, }; diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index e6d9fba646a1..9e73a55a257b 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -22,6 +22,7 @@ #include "camss-csiphy.h" #include "camss-ispif.h" #include "camss-vfe.h" +#include "camss-format.h" #define to_camss(ptr_module) \ container_of(ptr_module, struct camss, ptr_module) -- cgit v1.2.3 From 57cf33cc204bffab75a79b98a5932b48a6653d12 Mon Sep 17 00:00:00 2001 From: Radoslav Tsvetkov Date: Wed, 22 May 2024 18:46:54 +0300 Subject: media: qcom: camss: Attach formats to CSID resources Following the example of VFE, move all formats of the decoder to camss-csid.c and attach them to the subdevices resources. Signed-off-by: Radoslav Tsvetkov Signed-off-by: Gjorgji Rosikopulos Reviewed-by: Bryan O'Donoghue Tested-by: Bryan O'Donoghue # sc8280xp/sm8250/sdm845/apq8016 Acked-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/camss/camss-csid-4-1.c | 132 +----- drivers/media/platform/qcom/camss/camss-csid-4-7.c | 160 +------ .../media/platform/qcom/camss/camss-csid-gen2.c | 164 +------ drivers/media/platform/qcom/camss/camss-csid.c | 474 ++++++++++++++++++++- drivers/media/platform/qcom/camss/camss-csid.h | 24 +- drivers/media/platform/qcom/camss/camss.c | 75 ++-- 6 files changed, 538 insertions(+), 491 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-4-1.c b/drivers/media/platform/qcom/camss/camss-csid-4-1.c index dd49a40e6a70..c95861420502 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-4-1.c +++ b/drivers/media/platform/qcom/camss/camss-csid-4-1.c @@ -45,128 +45,6 @@ #define CAMSS_CSID_TG_DT_n_CGG_1(n) (0x0b0 + 0xc * (n)) #define CAMSS_CSID_TG_DT_n_CGG_2(n) (0x0b4 + 0xc * (n)) -static const struct csid_format csid_formats[] = { - { - MEDIA_BUS_FMT_UYVY8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_VYUY8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_YUYV8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_YVYU8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_SBGGR8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_Y10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, -}; - static void csid_configure_stream(struct csid_device *csid, u8 enable) { struct csid_testgen_config *tg = &csid->testgen; @@ -174,7 +52,7 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) if (enable) { struct v4l2_mbus_framefmt *input_format; - const struct csid_format *format; + const struct csid_format_info *format; u8 vc = 0; /* Virtual Channel 0 */ u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */ u8 dt_shift; @@ -184,7 +62,8 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) u32 num_lines, num_bytes_per_line; input_format = &csid->fmt[MSM_CSID_PAD_SRC]; - format = csid_get_fmt_entry(csid->formats, csid->nformats, + format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, input_format->code); num_bytes_per_line = input_format->width * format->bpp * format->spp / 8; num_lines = input_format->height; @@ -211,7 +90,8 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) struct csid_phy_config *phy = &csid->phy; input_format = &csid->fmt[MSM_CSID_PAD_SINK]; - format = csid_get_fmt_entry(csid->formats, csid->nformats, + format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, input_format->code); val = phy->lane_cnt - 1; @@ -311,8 +191,6 @@ static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code, static void csid_subdev_init(struct csid_device *csid) { - csid->formats = csid_formats; - csid->nformats = ARRAY_SIZE(csid_formats); csid->testgen.modes = csid_testgen_modes; csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN1; } diff --git a/drivers/media/platform/qcom/camss/camss-csid-4-7.c b/drivers/media/platform/qcom/camss/camss-csid-4-7.c index 6b26e036294e..08578a143688 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-4-7.c +++ b/drivers/media/platform/qcom/camss/camss-csid-4-7.c @@ -44,156 +44,6 @@ #define CAMSS_CSID_TG_DT_n_CGG_1(n) (0x0b8 + 0xc * (n)) #define CAMSS_CSID_TG_DT_n_CGG_2(n) (0x0bc + 0xc * (n)) -static const struct csid_format csid_formats[] = { - { - MEDIA_BUS_FMT_UYVY8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_VYUY8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_YUYV8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_YVYU8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_SBGGR8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_Y10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, -}; - static void csid_configure_stream(struct csid_device *csid, u8 enable) { struct csid_testgen_config *tg = &csid->testgen; @@ -203,7 +53,7 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) if (enable) { struct v4l2_mbus_framefmt *input_format; - const struct csid_format *format; + const struct csid_format_info *format; u8 vc = 0; /* Virtual Channel 0 */ u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */ u8 dt_shift; @@ -213,7 +63,8 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) u32 num_bytes_per_line, num_lines; input_format = &csid->fmt[MSM_CSID_PAD_SRC]; - format = csid_get_fmt_entry(csid->formats, csid->nformats, + format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, input_format->code); num_bytes_per_line = input_format->width * format->bpp * format->spp / 8; num_lines = input_format->height; @@ -240,7 +91,8 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable) struct csid_phy_config *phy = &csid->phy; input_format = &csid->fmt[MSM_CSID_PAD_SINK]; - format = csid_get_fmt_entry(csid->formats, csid->nformats, + format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, input_format->code); val = phy->lane_cnt - 1; @@ -387,8 +239,6 @@ static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code, static void csid_subdev_init(struct csid_device *csid) { - csid->formats = csid_formats; - csid->nformats = ARRAY_SIZE(csid_formats); csid->testgen.modes = csid_testgen_modes; csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN1; } diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c index b11de4797cca..eb5dabe2639a 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-gen2.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c @@ -176,163 +176,6 @@ #define TPG_COLOR_BOX_CFG_MODE 0 #define TPG_COLOR_BOX_PATTERN_SEL 2 -static const struct csid_format csid_formats[] = { - { - MEDIA_BUS_FMT_UYVY8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_VYUY8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_YUYV8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_YVYU8_1X16, - DATA_TYPE_YUV422_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 2, - }, - { - MEDIA_BUS_FMT_SBGGR8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_Y8_1X8, - DATA_TYPE_RAW_8BIT, - DECODE_FORMAT_UNCOMPRESSED_8_BIT, - 8, - 1, - }, - { - MEDIA_BUS_FMT_Y10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB12_1X12, - DATA_TYPE_RAW_12BIT, - DECODE_FORMAT_UNCOMPRESSED_12_BIT, - 12, - 1, - }, - { - MEDIA_BUS_FMT_SBGGR14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_SGBRG14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_SGRBG14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, - { - MEDIA_BUS_FMT_SRGGB14_1X14, - DATA_TYPE_RAW_14BIT, - DECODE_FORMAT_UNCOMPRESSED_14_BIT, - 14, - 1, - }, -}; - static void __csid_configure_stream(struct csid_device *csid, u8 enable, u8 vc) { struct csid_testgen_config *tg = &csid->testgen; @@ -341,8 +184,9 @@ static void __csid_configure_stream(struct csid_device *csid, u8 enable, u8 vc) u8 lane_cnt = csid->phy.lane_cnt; /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */ struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; - const struct csid_format *format = csid_get_fmt_entry(csid->formats, csid->nformats, - input_format->code); + const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, + input_format->code); if (!lane_cnt) lane_cnt = 4; @@ -612,8 +456,6 @@ static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code, static void csid_subdev_init(struct csid_device *csid) { - csid->formats = csid_formats; - csid->nformats = ARRAY_SIZE(csid_formats); csid->testgen.modes = csid_testgen_modes; csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN2; } diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index d1a22e07fdb6..5b23f5b8746d 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -45,6 +45,450 @@ const char * const csid_testgen_modes[] = { NULL }; +static const struct csid_format_info formats_4_1[] = { + { + MEDIA_BUS_FMT_UYVY8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_VYUY8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YUYV8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YVYU8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_SBGGR8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_Y10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, +}; + +static const struct csid_format_info formats_4_7[] = { + { + MEDIA_BUS_FMT_UYVY8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_VYUY8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YUYV8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YVYU8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_SBGGR8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_Y10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, +}; + +static const struct csid_format_info formats_gen2[] = { + { + MEDIA_BUS_FMT_UYVY8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_VYUY8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YUYV8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_YVYU8_1X16, + DATA_TYPE_YUV422_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 2, + }, + { + MEDIA_BUS_FMT_SBGGR8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_Y8_1X8, + DATA_TYPE_RAW_8BIT, + DECODE_FORMAT_UNCOMPRESSED_8_BIT, + 8, + 1, + }, + { + MEDIA_BUS_FMT_Y10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB12_1X12, + DATA_TYPE_RAW_12BIT, + DECODE_FORMAT_UNCOMPRESSED_12_BIT, + 12, + 1, + }, + { + MEDIA_BUS_FMT_SBGGR14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SGBRG14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SGRBG14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, + { + MEDIA_BUS_FMT_SRGGB14_1X14, + DATA_TYPE_RAW_14BIT, + DECODE_FORMAT_UNCOMPRESSED_14_BIT, + 14, + 1, + }, +}; + +const struct csid_formats csid_formats_4_1 = { + .nformats = ARRAY_SIZE(formats_4_1), + .formats = formats_4_1 +}; + +const struct csid_formats csid_formats_4_7 = { + .nformats = ARRAY_SIZE(formats_4_7), + .formats = formats_4_7 +}; + +const struct csid_formats csid_formats_gen2 = { + .nformats = ARRAY_SIZE(formats_gen2), + .formats = formats_gen2 +}; + u32 csid_find_code(u32 *codes, unsigned int ncodes, unsigned int match_format_idx, u32 match_code) { @@ -65,9 +509,9 @@ u32 csid_find_code(u32 *codes, unsigned int ncodes, return codes[0]; } -const struct csid_format *csid_get_fmt_entry(const struct csid_format *formats, - unsigned int nformats, - u32 code) +const struct csid_format_info *csid_get_fmt_entry(const struct csid_format_info *formats, + unsigned int nformats, + u32 code) { unsigned int i; @@ -87,12 +531,12 @@ const struct csid_format *csid_get_fmt_entry(const struct csid_format *formats, static int csid_set_clock_rates(struct csid_device *csid) { struct device *dev = csid->camss->dev; - const struct csid_format *fmt; + const struct csid_format_info *fmt; s64 link_freq; int i, j; int ret; - fmt = csid_get_fmt_entry(csid->formats, csid->nformats, + fmt = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats, csid->fmt[MSM_CSIPHY_PAD_SINK].code); link_freq = camss_get_link_freq(&csid->subdev.entity, fmt->bpp, csid->phy.lane_cnt); @@ -301,12 +745,12 @@ static void csid_try_format(struct csid_device *csid, case MSM_CSID_PAD_SINK: /* Set format on sink pad */ - for (i = 0; i < csid->nformats; i++) - if (fmt->code == csid->formats[i].code) + for (i = 0; i < csid->res->formats->nformats; i++) + if (fmt->code == csid->res->formats->formats[i].code) break; /* If not found, use UYVY as default */ - if (i >= csid->nformats) + if (i >= csid->res->formats->nformats) fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; fmt->width = clamp_t(u32, fmt->width, 1, 8191); @@ -330,12 +774,12 @@ static void csid_try_format(struct csid_device *csid, /* Test generator is enabled, set format on source */ /* pad to allow test generator usage */ - for (i = 0; i < csid->nformats; i++) - if (csid->formats[i].code == fmt->code) + for (i = 0; i < csid->res->formats->nformats; i++) + if (csid->res->formats->formats[i].code == fmt->code) break; /* If not found, use UYVY as default */ - if (i >= csid->nformats) + if (i >= csid->res->formats->nformats) fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; fmt->width = clamp_t(u32, fmt->width, 1, 8191); @@ -363,10 +807,10 @@ static int csid_enum_mbus_code(struct v4l2_subdev *sd, struct csid_device *csid = v4l2_get_subdevdata(sd); if (code->pad == MSM_CSID_PAD_SINK) { - if (code->index >= csid->nformats) + if (code->index >= csid->res->formats->nformats) return -EINVAL; - code->code = csid->formats[code->index].code; + code->code = csid->res->formats->formats[code->index].code; } else { if (csid->testgen_mode->cur.val == 0) { struct v4l2_mbus_framefmt *sink_fmt; @@ -380,10 +824,10 @@ static int csid_enum_mbus_code(struct v4l2_subdev *sd, if (!code->code) return -EINVAL; } else { - if (code->index >= csid->nformats) + if (code->index >= csid->res->formats->nformats) return -EINVAL; - code->code = csid->formats[code->index].code; + code->code = csid->res->formats->formats[code->index].code; } } diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h index 8d2971aa9ef8..0e385d17c250 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.h +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -67,7 +67,7 @@ enum csid_testgen_mode { CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN2 = 9, /* excluding disabled */ }; -struct csid_format { +struct csid_format_info { u32 code; u8 data_type; u8 decode_format; @@ -75,6 +75,11 @@ struct csid_format { u8 spp; /* bus samples per pixel */ }; +struct csid_formats { + unsigned int nformats; + const struct csid_format_info *formats; +}; + struct csid_testgen_config { enum csid_testgen_mode mode; const char * const*modes; @@ -152,6 +157,7 @@ struct csid_hw_ops { struct csid_subdev_resources { bool is_lite; const struct csid_hw_ops *hw_ops; + const struct csid_formats *formats; }; struct csid_device { @@ -172,8 +178,6 @@ struct csid_device { struct v4l2_mbus_framefmt fmt[MSM_CSID_PADS_NUM]; struct v4l2_ctrl_handler ctrls; struct v4l2_ctrl *testgen_mode; - const struct csid_format *formats; - unsigned int nformats; const struct csid_subdev_resources *res; }; @@ -193,16 +197,16 @@ u32 csid_find_code(u32 *codes, unsigned int ncode, unsigned int match_format_idx, u32 match_code); /* - * csid_get_fmt_entry - Find csid_format entry with matching format code - * @formats: Array of format csid_format entries + * csid_get_fmt_entry - Find csid_format_info entry with matching format code + * @formats: Array of format csid_format_info entries * @nformats: Length of @nformats array * @code: Desired format code * * Return formats[0] on failure to find code */ -const struct csid_format *csid_get_fmt_entry(const struct csid_format *formats, - unsigned int nformats, - u32 code); +const struct csid_format_info *csid_get_fmt_entry(const struct csid_format_info *formats, + unsigned int nformats, + u32 code); int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, const struct camss_subdev_resources *res, u8 id); @@ -216,6 +220,10 @@ void msm_csid_get_csid_id(struct media_entity *entity, u8 *id); extern const char * const csid_testgen_modes[]; +extern const struct csid_formats csid_formats_4_1; +extern const struct csid_formats csid_formats_4_7; +extern const struct csid_formats csid_formats_gen2; + extern const struct csid_hw_ops csid_ops_4_1; extern const struct csid_hw_ops csid_ops_4_7; extern const struct csid_hw_ops csid_ops_gen2; diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 8f2414eed89d..8b7f02fcda44 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -81,7 +81,8 @@ static const struct camss_subdev_resources csid_res_8x16[] = { .reg = { "csid0" }, .interrupt = { "csid0" }, .csid = { - .hw_ops = &csid_ops_4_1 + .hw_ops = &csid_ops_4_1, + .formats = &csid_formats_4_1 } }, @@ -101,7 +102,8 @@ static const struct camss_subdev_resources csid_res_8x16[] = { .reg = { "csid1" }, .interrupt = { "csid1" }, .csid = { - .hw_ops = &csid_ops_4_1 + .hw_ops = &csid_ops_4_1, + .formats = &csid_formats_4_1 } }, }; @@ -208,7 +210,8 @@ static const struct camss_subdev_resources csid_res_8x96[] = { .reg = { "csid0" }, .interrupt = { "csid0" }, .csid = { - .hw_ops = &csid_ops_4_7 + .hw_ops = &csid_ops_4_7, + .formats = &csid_formats_4_7 } }, @@ -228,7 +231,8 @@ static const struct camss_subdev_resources csid_res_8x96[] = { .reg = { "csid1" }, .interrupt = { "csid1" }, .csid = { - .hw_ops = &csid_ops_4_7 + .hw_ops = &csid_ops_4_7, + .formats = &csid_formats_4_7 } }, @@ -248,7 +252,8 @@ static const struct camss_subdev_resources csid_res_8x96[] = { .reg = { "csid2" }, .interrupt = { "csid2" }, .csid = { - .hw_ops = &csid_ops_4_7 + .hw_ops = &csid_ops_4_7, + .formats = &csid_formats_4_7 } }, @@ -268,7 +273,8 @@ static const struct camss_subdev_resources csid_res_8x96[] = { .reg = { "csid3" }, .interrupt = { "csid3" }, .csid = { - .hw_ops = &csid_ops_4_7 + .hw_ops = &csid_ops_4_7, + .formats = &csid_formats_4_7 } } }; @@ -410,7 +416,8 @@ static const struct camss_subdev_resources csid_res_660[] = { .reg = { "csid0" }, .interrupt = { "csid0" }, .csid = { - .hw_ops = &csid_ops_4_7 + .hw_ops = &csid_ops_4_7, + .formats = &csid_formats_4_7 } }, @@ -433,7 +440,8 @@ static const struct camss_subdev_resources csid_res_660[] = { .reg = { "csid1" }, .interrupt = { "csid1" }, .csid = { - .hw_ops = &csid_ops_4_7 + .hw_ops = &csid_ops_4_7, + .formats = &csid_formats_4_7 } }, @@ -456,7 +464,8 @@ static const struct camss_subdev_resources csid_res_660[] = { .reg = { "csid2" }, .interrupt = { "csid2" }, .csid = { - .hw_ops = &csid_ops_4_7 + .hw_ops = &csid_ops_4_7, + .formats = &csid_formats_4_7 } }, @@ -479,7 +488,8 @@ static const struct camss_subdev_resources csid_res_660[] = { .reg = { "csid3" }, .interrupt = { "csid3" }, .csid = { - .hw_ops = &csid_ops_4_7 + .hw_ops = &csid_ops_4_7, + .formats = &csid_formats_4_7 } } }; @@ -660,7 +670,8 @@ static const struct camss_subdev_resources csid_res_845[] = { .reg = { "csid0" }, .interrupt = { "csid0" }, .csid = { - .hw_ops = &csid_ops_gen2 + .hw_ops = &csid_ops_gen2, + .formats = &csid_formats_gen2 } }, @@ -683,7 +694,8 @@ static const struct camss_subdev_resources csid_res_845[] = { .reg = { "csid1" }, .interrupt = { "csid1" }, .csid = { - .hw_ops = &csid_ops_gen2 + .hw_ops = &csid_ops_gen2, + .formats = &csid_formats_gen2 } }, @@ -707,7 +719,8 @@ static const struct camss_subdev_resources csid_res_845[] = { .interrupt = { "csid2" }, .csid = { .is_lite = true, - .hw_ops = &csid_ops_gen2 + .hw_ops = &csid_ops_gen2, + .formats = &csid_formats_gen2 } } }; @@ -882,7 +895,8 @@ static const struct camss_subdev_resources csid_res_8250[] = { .reg = { "csid0" }, .interrupt = { "csid0" }, .csid = { - .hw_ops = &csid_ops_gen2 + .hw_ops = &csid_ops_gen2, + .formats = &csid_formats_gen2 } }, /* CSID1 */ @@ -897,7 +911,8 @@ static const struct camss_subdev_resources csid_res_8250[] = { .reg = { "csid1" }, .interrupt = { "csid1" }, .csid = { - .hw_ops = &csid_ops_gen2 + .hw_ops = &csid_ops_gen2, + .formats = &csid_formats_gen2 } }, /* CSID2 */ @@ -912,7 +927,8 @@ static const struct camss_subdev_resources csid_res_8250[] = { .interrupt = { "csid2" }, .csid = { .is_lite = true, - .hw_ops = &csid_ops_gen2 + .hw_ops = &csid_ops_gen2, + .formats = &csid_formats_gen2 } }, /* CSID3 */ @@ -927,7 +943,8 @@ static const struct camss_subdev_resources csid_res_8250[] = { .interrupt = { "csid3" }, .csid = { .is_lite = true, - .hw_ops = &csid_ops_gen2 + .hw_ops = &csid_ops_gen2, + .formats = &csid_formats_gen2 } } }; @@ -1121,7 +1138,8 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { .reg = { "csid0" }, .interrupt = { "csid0" }, .csid = { - .hw_ops = &csid_ops_gen2 + .hw_ops = &csid_ops_gen2, + .formats = &csid_formats_gen2 } }, /* CSID1 */ @@ -1135,7 +1153,8 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { .reg = { "csid1" }, .interrupt = { "csid1" }, .csid = { - .hw_ops = &csid_ops_gen2 + .hw_ops = &csid_ops_gen2, + .formats = &csid_formats_gen2 } }, /* CSID2 */ @@ -1149,7 +1168,8 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { .reg = { "csid2" }, .interrupt = { "csid2" }, .csid = { - .hw_ops = &csid_ops_gen2 + .hw_ops = &csid_ops_gen2, + .formats = &csid_formats_gen2 } }, /* CSID3 */ @@ -1163,7 +1183,8 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { .reg = { "csid3" }, .interrupt = { "csid3" }, .csid = { - .hw_ops = &csid_ops_gen2 + .hw_ops = &csid_ops_gen2, + .formats = &csid_formats_gen2 } }, /* CSID_LITE0 */ @@ -1177,7 +1198,8 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { .interrupt = { "csid0_lite" }, .csid = { .is_lite = true, - .hw_ops = &csid_ops_gen2 + .hw_ops = &csid_ops_gen2, + .formats = &csid_formats_gen2 } }, /* CSID_LITE1 */ @@ -1191,7 +1213,8 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { .interrupt = { "csid1_lite" }, .csid = { .is_lite = true, - .hw_ops = &csid_ops_gen2 + .hw_ops = &csid_ops_gen2, + .formats = &csid_formats_gen2 } }, /* CSID_LITE2 */ @@ -1205,7 +1228,8 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { .interrupt = { "csid2_lite" }, .csid = { .is_lite = true, - .hw_ops = &csid_ops_gen2 + .hw_ops = &csid_ops_gen2, + .formats = &csid_formats_gen2 } }, /* CSID_LITE3 */ @@ -1219,7 +1243,8 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { .interrupt = { "csid3_lite" }, .csid = { .is_lite = true, - .hw_ops = &csid_ops_gen2 + .hw_ops = &csid_ops_gen2, + .formats = &csid_formats_gen2 } } }; -- cgit v1.2.3 From e05d1be95880cc359c5a32f9f168e18b4538d776 Mon Sep 17 00:00:00 2001 From: Radoslav Tsvetkov Date: Wed, 22 May 2024 18:46:55 +0300 Subject: media: qcom: camss: Attach formats to CSIPHY resources Following the example of VFE and CSID, attach the CSIPHY formats to the subdevices resources. Signed-off-by: Radoslav Tsvetkov Signed-off-by: Gjorgji Rosikopulos Reviewed-by: Bryan O'Donoghue Tested-by: Bryan O'Donoghue # sc8280xp/sm8250/sdm845/apq8016 Acked-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/camss/camss-csiphy.c | 60 ++++++++++----------- drivers/media/platform/qcom/camss/camss-csiphy.h | 17 +++++- drivers/media/platform/qcom/camss/camss.c | 66 ++++++++++++++++-------- 3 files changed, 85 insertions(+), 58 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index f26ddf1af9d4..2f7361dfd461 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -24,12 +24,7 @@ #define MSM_CSIPHY_NAME "msm_csiphy" -struct csiphy_format { - u32 code; - u8 bpp; -}; - -static const struct csiphy_format csiphy_formats_8x16[] = { +static const struct csiphy_format_info formats_8x16[] = { { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, @@ -49,7 +44,7 @@ static const struct csiphy_format csiphy_formats_8x16[] = { { MEDIA_BUS_FMT_Y10_1X10, 10 }, }; -static const struct csiphy_format csiphy_formats_8x96[] = { +static const struct csiphy_format_info formats_8x96[] = { { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, @@ -73,7 +68,7 @@ static const struct csiphy_format csiphy_formats_8x96[] = { { MEDIA_BUS_FMT_Y10_1X10, 10 }, }; -static const struct csiphy_format csiphy_formats_sdm845[] = { +static const struct csiphy_format_info formats_sdm845[] = { { MEDIA_BUS_FMT_UYVY8_1X16, 8 }, { MEDIA_BUS_FMT_VYUY8_1X16, 8 }, { MEDIA_BUS_FMT_YUYV8_1X16, 8 }, @@ -98,6 +93,21 @@ static const struct csiphy_format csiphy_formats_sdm845[] = { { MEDIA_BUS_FMT_Y10_1X10, 10 }, }; +const struct csiphy_formats csiphy_formats_8x16 = { + .nformats = ARRAY_SIZE(formats_8x16), + .formats = formats_8x16 +}; + +const struct csiphy_formats csiphy_formats_8x96 = { + .nformats = ARRAY_SIZE(formats_8x96), + .formats = formats_8x96 +}; + +const struct csiphy_formats csiphy_formats_sdm845 = { + .nformats = ARRAY_SIZE(formats_sdm845), + .formats = formats_sdm845 +}; + /* * csiphy_get_bpp - map media bus format to bits per pixel * @formats: supported media bus formats array @@ -106,7 +116,7 @@ static const struct csiphy_format csiphy_formats_sdm845[] = { * * Return number of bits per pixel */ -static u8 csiphy_get_bpp(const struct csiphy_format *formats, +static u8 csiphy_get_bpp(const struct csiphy_format_info *formats, unsigned int nformats, u32 code) { unsigned int i; @@ -131,7 +141,7 @@ static int csiphy_set_clock_rates(struct csiphy_device *csiphy) int i, j; int ret; - u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats, + u8 bpp = csiphy_get_bpp(csiphy->res->formats->formats, csiphy->res->formats->nformats, csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; @@ -244,7 +254,7 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) struct csiphy_config *cfg = &csiphy->cfg; s64 link_freq; u8 lane_mask = csiphy->res->hw_ops->get_lane_mask(&cfg->csi2->lane_cfg); - u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats, + u8 bpp = csiphy_get_bpp(csiphy->res->formats->formats, csiphy->res->formats->nformats, csiphy->fmt[MSM_CSIPHY_PAD_SINK].code); u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data; u8 val; @@ -350,12 +360,12 @@ static void csiphy_try_format(struct csiphy_device *csiphy, case MSM_CSIPHY_PAD_SINK: /* Set format on sink pad */ - for (i = 0; i < csiphy->nformats; i++) - if (fmt->code == csiphy->formats[i].code) + for (i = 0; i < csiphy->res->formats->nformats; i++) + if (fmt->code == csiphy->res->formats->formats[i].code) break; /* If not found, use UYVY as default */ - if (i >= csiphy->nformats) + if (i >= csiphy->res->formats->nformats) fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; fmt->width = clamp_t(u32, fmt->width, 1, 8191); @@ -392,10 +402,10 @@ static int csiphy_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format; if (code->pad == MSM_CSIPHY_PAD_SINK) { - if (code->index >= csiphy->nformats) + if (code->index >= csiphy->res->formats->nformats) return -EINVAL; - code->code = csiphy->formats[code->index].code; + code->code = csiphy->res->formats->formats[code->index].code; } else { if (code->index > 0) return -EINVAL; @@ -566,24 +576,6 @@ int msm_csiphy_subdev_init(struct camss *camss, csiphy->cfg.combo_mode = 0; csiphy->res = &res->csiphy; - switch (camss->res->version) { - case CAMSS_8x16: - csiphy->formats = csiphy_formats_8x16; - csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x16); - break; - case CAMSS_8x96: - case CAMSS_660: - csiphy->formats = csiphy_formats_8x96; - csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x96); - break; - case CAMSS_845: - case CAMSS_8250: - case CAMSS_8280XP: - csiphy->formats = csiphy_formats_sdm845; - csiphy->nformats = ARRAY_SIZE(csiphy_formats_sdm845); - break; - } - /* Memory */ csiphy->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]); diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index 7bd68129ca49..47f0b6b09eba 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -42,6 +42,16 @@ struct csiphy_config { struct csiphy_csi2_cfg *csi2; }; +struct csiphy_format_info { + u32 code; + u8 bpp; +}; + +struct csiphy_formats { + unsigned int nformats; + const struct csiphy_format_info *formats; +}; + struct csiphy_device; struct csiphy_hw_ops { @@ -65,6 +75,7 @@ struct csiphy_hw_ops { struct csiphy_subdev_resources { const struct csiphy_hw_ops *hw_ops; + const struct csiphy_formats *formats; }; struct csiphy_device { @@ -83,8 +94,6 @@ struct csiphy_device { struct csiphy_config cfg; struct v4l2_mbus_framefmt fmt[MSM_CSIPHY_PADS_NUM]; const struct csiphy_subdev_resources *res; - const struct csiphy_format *formats; - unsigned int nformats; }; struct camss_subdev_resources; @@ -98,6 +107,10 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy, void msm_csiphy_unregister_entity(struct csiphy_device *csiphy); +extern const struct csiphy_formats csiphy_formats_8x16; +extern const struct csiphy_formats csiphy_formats_8x96; +extern const struct csiphy_formats csiphy_formats_sdm845; + extern const struct csiphy_hw_ops csiphy_ops_2ph_1_0; extern const struct csiphy_hw_ops csiphy_ops_3ph_1_0; diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 8b7f02fcda44..1b75918fc35b 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -44,7 +44,8 @@ static const struct camss_subdev_resources csiphy_res_8x16[] = { .reg = { "csiphy0", "csiphy0_clk_mux" }, .interrupt = { "csiphy0" }, .csiphy = { - .hw_ops = &csiphy_ops_2ph_1_0 + .hw_ops = &csiphy_ops_2ph_1_0, + .formats = &csiphy_formats_8x16 } }, @@ -59,7 +60,8 @@ static const struct camss_subdev_resources csiphy_res_8x16[] = { .reg = { "csiphy1", "csiphy1_clk_mux" }, .interrupt = { "csiphy1" }, .csiphy = { - .hw_ops = &csiphy_ops_2ph_1_0 + .hw_ops = &csiphy_ops_2ph_1_0, + .formats = &csiphy_formats_8x16 } } }; @@ -158,7 +160,8 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = { .reg = { "csiphy0", "csiphy0_clk_mux" }, .interrupt = { "csiphy0" }, .csiphy = { - .hw_ops = &csiphy_ops_3ph_1_0 + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_8x96 } }, @@ -173,7 +176,8 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = { .reg = { "csiphy1", "csiphy1_clk_mux" }, .interrupt = { "csiphy1" }, .csiphy = { - .hw_ops = &csiphy_ops_3ph_1_0 + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_8x96 } }, @@ -188,7 +192,8 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = { .reg = { "csiphy2", "csiphy2_clk_mux" }, .interrupt = { "csiphy2" }, .csiphy = { - .hw_ops = &csiphy_ops_3ph_1_0 + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_8x96 } } }; @@ -357,7 +362,8 @@ static const struct camss_subdev_resources csiphy_res_660[] = { .reg = { "csiphy0", "csiphy0_clk_mux" }, .interrupt = { "csiphy0" }, .csiphy = { - .hw_ops = &csiphy_ops_3ph_1_0 + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_8x96 } }, @@ -374,7 +380,8 @@ static const struct camss_subdev_resources csiphy_res_660[] = { .reg = { "csiphy1", "csiphy1_clk_mux" }, .interrupt = { "csiphy1" }, .csiphy = { - .hw_ops = &csiphy_ops_3ph_1_0 + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_8x96 } }, @@ -391,7 +398,8 @@ static const struct camss_subdev_resources csiphy_res_660[] = { .reg = { "csiphy2", "csiphy2_clk_mux" }, .interrupt = { "csiphy2" }, .csiphy = { - .hw_ops = &csiphy_ops_3ph_1_0 + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_8x96 } } }; @@ -582,7 +590,8 @@ static const struct camss_subdev_resources csiphy_res_845[] = { .reg = { "csiphy0" }, .interrupt = { "csiphy0" }, .csiphy = { - .hw_ops = &csiphy_ops_3ph_1_0 + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 } }, @@ -603,7 +612,8 @@ static const struct camss_subdev_resources csiphy_res_845[] = { .reg = { "csiphy1" }, .interrupt = { "csiphy1" }, .csiphy = { - .hw_ops = &csiphy_ops_3ph_1_0 + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 } }, @@ -624,7 +634,8 @@ static const struct camss_subdev_resources csiphy_res_845[] = { .reg = { "csiphy2" }, .interrupt = { "csiphy2" }, .csiphy = { - .hw_ops = &csiphy_ops_3ph_1_0 + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 } }, @@ -645,7 +656,8 @@ static const struct camss_subdev_resources csiphy_res_845[] = { .reg = { "csiphy3" }, .interrupt = { "csiphy3" }, .csiphy = { - .hw_ops = &csiphy_ops_3ph_1_0 + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 } } }; @@ -817,7 +829,8 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { .reg = { "csiphy0" }, .interrupt = { "csiphy0" }, .csiphy = { - .hw_ops = &csiphy_ops_3ph_1_0 + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 } }, /* CSIPHY1 */ @@ -829,7 +842,8 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { .reg = { "csiphy1" }, .interrupt = { "csiphy1" }, .csiphy = { - .hw_ops = &csiphy_ops_3ph_1_0 + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 } }, /* CSIPHY2 */ @@ -841,7 +855,8 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { .reg = { "csiphy2" }, .interrupt = { "csiphy2" }, .csiphy = { - .hw_ops = &csiphy_ops_3ph_1_0 + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 } }, /* CSIPHY3 */ @@ -853,7 +868,8 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { .reg = { "csiphy3" }, .interrupt = { "csiphy3" }, .csiphy = { - .hw_ops = &csiphy_ops_3ph_1_0 + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 } }, /* CSIPHY4 */ @@ -865,7 +881,8 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { .reg = { "csiphy4" }, .interrupt = { "csiphy4" }, .csiphy = { - .hw_ops = &csiphy_ops_3ph_1_0 + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 } }, /* CSIPHY5 */ @@ -877,7 +894,8 @@ static const struct camss_subdev_resources csiphy_res_8250[] = { .reg = { "csiphy5" }, .interrupt = { "csiphy5" }, .csiphy = { - .hw_ops = &csiphy_ops_3ph_1_0 + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 } } }; @@ -1085,7 +1103,8 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = { .reg = { "csiphy0" }, .interrupt = { "csiphy0" }, .csiphy = { - .hw_ops = &csiphy_ops_3ph_1_0 + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 } }, /* CSIPHY1 */ @@ -1097,7 +1116,8 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = { .reg = { "csiphy1" }, .interrupt = { "csiphy1" }, .csiphy = { - .hw_ops = &csiphy_ops_3ph_1_0 + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 } }, /* CSIPHY2 */ @@ -1109,7 +1129,8 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = { .reg = { "csiphy2" }, .interrupt = { "csiphy2" }, .csiphy = { - .hw_ops = &csiphy_ops_3ph_1_0 + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 } }, /* CSIPHY3 */ @@ -1121,7 +1142,8 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = { .reg = { "csiphy3" }, .interrupt = { "csiphy3" }, .csiphy = { - .hw_ops = &csiphy_ops_3ph_1_0 + .hw_ops = &csiphy_ops_3ph_1_0, + .formats = &csiphy_formats_sdm845 } }, }; -- cgit v1.2.3 From ad458cb90f2c380b750294e72baf53b609a911e1 Mon Sep 17 00:00:00 2001 From: Radoslav Tsvetkov Date: Wed, 22 May 2024 18:46:56 +0300 Subject: media: qcom: camss: Move format related functions Move out the format related helper functions from vfe and video in a separate file. The goal here is to create a format API. Signed-off-by: Radoslav Tsvetkov Signed-off-by: Gjorgji Rosikopulos Acked-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/camss/Makefile | 1 + drivers/media/platform/qcom/camss/camss-format.c | 91 ++++++++++++++++++++++++ drivers/media/platform/qcom/camss/camss-format.h | 5 ++ drivers/media/platform/qcom/camss/camss-vfe.c | 86 ++++++---------------- drivers/media/platform/qcom/camss/camss-video.c | 26 +------ 5 files changed, 121 insertions(+), 88 deletions(-) create mode 100644 drivers/media/platform/qcom/camss/camss-format.c diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile index 0d4389ab312d..e636968a1126 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -19,5 +19,6 @@ qcom-camss-objs += \ camss-vfe-gen1.o \ camss-vfe.o \ camss-video.o \ + camss-format.o \ obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom-camss.o diff --git a/drivers/media/platform/qcom/camss/camss-format.c b/drivers/media/platform/qcom/camss/camss-format.c new file mode 100644 index 000000000000..4a3d5549615c --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-format.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-format.c + * + * Qualcomm MSM Camera Subsystem - Format helpers + * + * Copyright (c) 2023, The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Technologies, Inc. + */ +#include +#include + +#include "camss-format.h" + +/* + * camss_format_get_bpp - Map media bus format to bits per pixel + * @formats: supported media bus formats array + * @nformats: size of @formats array + * @code: media bus format code + * + * Return number of bits per pixel + */ +u8 camss_format_get_bpp(const struct camss_format_info *formats, unsigned int nformats, u32 code) +{ + unsigned int i; + + for (i = 0; i < nformats; i++) + if (code == formats[i].code) + return formats[i].mbus_bpp; + + WARN(1, "Unknown format\n"); + + return formats[0].mbus_bpp; +} + +/* + * camss_format_find_code - Find a format code in an array + * @code: a pointer to media bus format codes array + * @n_code: size of @code array + * @index: index of code in the array + * @req_code: required code + * + * Return media bus format code + */ +u32 camss_format_find_code(u32 *code, unsigned int n_code, unsigned int index, u32 req_code) +{ + unsigned int i; + + if (!req_code && index >= n_code) + return 0; + + for (i = 0; i < n_code; i++) { + if (req_code) { + if (req_code == code[i]) + return req_code; + } else { + if (i == index) + return code[i]; + } + } + + return code[0]; +} + +/* + * camss_format_find_format - Find a format in an array + * @code: media bus format code + * @pixelformat: V4L2 pixel format FCC identifier + * @formats: a pointer to formats array + * @nformats: size of @formats array + * + * Return index of a format or a negative error code otherwise + */ +int camss_format_find_format(u32 code, u32 pixelformat, const struct camss_format_info *formats, + unsigned int nformats) +{ + unsigned int i; + + for (i = 0; i < nformats; i++) { + if (formats[i].code == code && + formats[i].pixelformat == pixelformat) + return i; + } + + for (i = 0; i < nformats; i++) { + if (formats[i].code == code) + return i; + } + + return -EINVAL; +} diff --git a/drivers/media/platform/qcom/camss/camss-format.h b/drivers/media/platform/qcom/camss/camss-format.h index e0231ca6a5ca..923a48c9c3fb 100644 --- a/drivers/media/platform/qcom/camss/camss-format.h +++ b/drivers/media/platform/qcom/camss/camss-format.h @@ -54,4 +54,9 @@ struct camss_formats { const struct camss_format_info *formats; }; +u8 camss_format_get_bpp(const struct camss_format_info *formats, unsigned int nformats, u32 code); +u32 camss_format_find_code(u32 *code, unsigned int n_code, unsigned int index, u32 req_code); +int camss_format_find_format(u32 code, u32 pixelformat, const struct camss_format_info *formats, + unsigned int nformats); + #endif /* __CAMSS_FORMAT_H__ */ diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index 2d5a64c055f1..83c5a36d071f 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -278,48 +278,6 @@ const struct camss_formats vfe_formats_pix_845 = { .formats = formats_rdi_845 }; -/* - * vfe_get_bpp - map media bus format to bits per pixel - * @formats: supported media bus formats array - * @nformats: size of @formats array - * @code: media bus format code - * - * Return number of bits per pixel - */ -static u8 vfe_get_bpp(const struct camss_format_info *formats, - unsigned int nformats, u32 code) -{ - unsigned int i; - - for (i = 0; i < nformats; i++) - if (code == formats[i].code) - return formats[i].mbus_bpp; - - WARN(1, "Unknown format\n"); - - return formats[0].mbus_bpp; -} - -static u32 vfe_find_code(u32 *code, unsigned int n_code, - unsigned int index, u32 req_code) -{ - int i; - - if (!req_code && (index >= n_code)) - return 0; - - for (i = 0; i < n_code; i++) - if (req_code) { - if (req_code == code[i]) - return req_code; - } else { - if (i == index) - return code[i]; - } - - return code[0]; -} - static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, unsigned int index, u32 src_req_code) { @@ -335,8 +293,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_YUYV8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } case MEDIA_BUS_FMT_YVYU8_1X16: { @@ -345,8 +303,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_YVYU8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } case MEDIA_BUS_FMT_UYVY8_1X16: { @@ -355,8 +313,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_UYVY8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } case MEDIA_BUS_FMT_VYUY8_1X16: { @@ -365,8 +323,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_VYUY8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } default: if (index > 0) @@ -391,8 +349,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_YUYV8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } case MEDIA_BUS_FMT_YVYU8_1X16: { @@ -404,8 +362,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_YVYU8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } case MEDIA_BUS_FMT_UYVY8_1X16: { @@ -417,8 +375,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_UYVY8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } case MEDIA_BUS_FMT_VYUY8_1X16: { @@ -430,8 +388,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, MEDIA_BUS_FMT_VYUY8_1_5X8, }; - return vfe_find_code(src_code, ARRAY_SIZE(src_code), - index, src_req_code); + return camss_format_find_code(src_code, ARRAY_SIZE(src_code), + index, src_req_code); } default: if (index > 0) @@ -714,9 +672,9 @@ static int vfe_set_clock_rates(struct vfe_device *vfe) } else { struct vfe_line *l = &vfe->line[j]; - bpp = vfe_get_bpp(l->formats, - l->nformats, - l->fmt[MSM_VFE_PAD_SINK].code); + bpp = camss_format_get_bpp(l->formats, + l->nformats, + l->fmt[MSM_VFE_PAD_SINK].code); tmp = pixel_clock[j] * bpp / 64; } @@ -795,9 +753,9 @@ static int vfe_check_clock_rates(struct vfe_device *vfe) } else { struct vfe_line *l = &vfe->line[j]; - bpp = vfe_get_bpp(l->formats, - l->nformats, - l->fmt[MSM_VFE_PAD_SINK].code); + bpp = camss_format_get_bpp(l->formats, + l->nformats, + l->fmt[MSM_VFE_PAD_SINK].code); tmp = pixel_clock[j] * bpp / 64; } diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 2fe706838469..cd72feca618c 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -28,27 +28,6 @@ * Helper functions */ -static int video_find_format(u32 code, u32 pixelformat, - const struct camss_format_info *formats, - unsigned int nformats) -{ - int i; - - for (i = 0; i < nformats; i++) { - if (formats[i].code == code && - formats[i].pixelformat == pixelformat) - return i; - } - - for (i = 0; i < nformats; i++) - if (formats[i].code == code) - return i; - - WARN_ON(1); - - return -EINVAL; -} - /* * video_mbus_to_pix_mp - Convert v4l2_mbus_framefmt to v4l2_pix_format_mplane * @mbus: v4l2_mbus_framefmt format (input) @@ -121,9 +100,8 @@ static int video_get_subdev_format(struct camss_video *video, if (ret) return ret; - ret = video_find_format(fmt.format.code, - format->fmt.pix_mp.pixelformat, - video->formats, video->nformats); + ret = camss_format_find_format(fmt.format.code, format->fmt.pix_mp.pixelformat, + video->formats, video->nformats); if (ret < 0) return ret; -- cgit v1.2.3 From 729fc005c8e2a8533edad8b3cdd9d8ec8057fed7 Mon Sep 17 00:00:00 2001 From: Milen Mitkov Date: Wed, 22 May 2024 18:46:57 +0300 Subject: media: qcom: camss: Split testgen, RDI and RX for CSID 170 Split the RAW interface (RDI), the CSID receiver (RX) and test pattern generator (testgen), configurations for CSID on Titan 170 Signed-off-by: Milen Mitkov Signed-off-by: Gjorgji Rosikopulos Reviewed-by: Bryan O'Donoghue Tested-by: Bryan O'Donoghue # sc8280xp/sm8250/sdm845/apq8016 Acked-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil [hverkuil: folded https://lore.kernel.org/linux-media/20240626074730.85-1-quic_grosikop@quicinc.com/ into this patch] --- .../media/platform/qcom/camss/camss-csid-gen2.c | 249 +++++++++++---------- 1 file changed, 135 insertions(+), 114 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c index eb5dabe2639a..e1c757933e27 100644 --- a/drivers/media/platform/qcom/camss/camss-csid-gen2.c +++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c @@ -176,150 +176,171 @@ #define TPG_COLOR_BOX_CFG_MODE 0 #define TPG_COLOR_BOX_PATTERN_SEL 2 -static void __csid_configure_stream(struct csid_device *csid, u8 enable, u8 vc) +static void __csid_configure_rx(struct csid_device *csid, + struct csid_phy_config *phy, int vc) { - struct csid_testgen_config *tg = &csid->testgen; - u32 val; - u32 phy_sel = 0; u8 lane_cnt = csid->phy.lane_cnt; - /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */ - struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; - const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, - csid->res->formats->nformats, - input_format->code); + int val; if (!lane_cnt) lane_cnt = 4; - if (!tg->enabled) - phy_sel = csid->phy.csiphy_id; - - if (enable) { - /* - * DT_ID is a two bit bitfield that is concatenated with - * the four least significant bits of the five bit VC - * bitfield to generate an internal CID value. - * - * CSID_RDI_CFG0(vc) - * DT_ID : 28:27 - * VC : 26:22 - * DT : 21:16 - * - * CID : VC 3:0 << 2 | DT_ID 1:0 - */ - u8 dt_id = vc & 0x03; - - if (tg->enabled) { - /* configure one DT, infinite frames */ - val = vc << TPG_VC_CFG0_VC_NUM; - val |= INTELEAVING_MODE_ONE_SHOT << TPG_VC_CFG0_LINE_INTERLEAVING_MODE; - val |= 0 << TPG_VC_CFG0_NUM_FRAMES; - writel_relaxed(val, csid->base + CSID_TPG_VC_CFG0); - - val = 0x740 << TPG_VC_CFG1_H_BLANKING_COUNT; - val |= 0x3ff << TPG_VC_CFG1_V_BLANKING_COUNT; - writel_relaxed(val, csid->base + CSID_TPG_VC_CFG1); - - writel_relaxed(0x12345678, csid->base + CSID_TPG_LFSR_SEED); - - val = (input_format->height & 0x1fff) << TPG_DT_n_CFG_0_FRAME_HEIGHT; - val |= (input_format->width & 0x1fff) << TPG_DT_n_CFG_0_FRAME_WIDTH; - writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_0(0)); - - val = format->data_type << TPG_DT_n_CFG_1_DATA_TYPE; - writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_1(0)); - - val = (tg->mode - 1) << TPG_DT_n_CFG_2_PAYLOAD_MODE; - val |= 0xBE << TPG_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD; - val |= format->decode_format << TPG_DT_n_CFG_2_ENCODE_FORMAT; - writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_2(0)); - - writel_relaxed(0, csid->base + CSID_TPG_COLOR_BARS_CFG); - - writel_relaxed(0, csid->base + CSID_TPG_COLOR_BOX_CFG); - } + val = (lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES; + val |= phy->lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL; + val |= phy->csiphy_id << CSI2_RX_CFG0_PHY_NUM_SEL; + writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG0); - val = 1 << RDI_CFG0_BYTE_CNTR_EN; - val |= 1 << RDI_CFG0_FORMAT_MEASURE_EN; - val |= 1 << RDI_CFG0_TIMESTAMP_EN; - /* note: for non-RDI path, this should be format->decode_format */ - val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT; - val |= format->data_type << RDI_CFG0_DATA_TYPE; - val |= vc << RDI_CFG0_VIRTUAL_CHANNEL; - val |= dt_id << RDI_CFG0_DT_ID; - writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); + val = 1 << CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN; + if (vc > 3) + val |= 1 << CSI2_RX_CFG1_VC_MODE; + val |= 1 << CSI2_RX_CFG1_MISR_EN; + writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1); +} - /* CSID_TIMESTAMP_STB_POST_IRQ */ - val = 2 << RDI_CFG1_TIMESTAMP_STB_SEL; - writel_relaxed(val, csid->base + CSID_RDI_CFG1(vc)); +static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi) +{ + int val; - val = 1; - writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc)); + if (enable) + val = HALT_CMD_RESUME_AT_FRAME_BOUNDARY << RDI_CTRL_HALT_CMD; + else + val = HALT_CMD_HALT_AT_FRAME_BOUNDARY << RDI_CTRL_HALT_CMD; + writel_relaxed(val, csid->base + CSID_RDI_CTRL(rdi)); +} - val = 0; - writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(vc)); +static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 vc) +{ + struct csid_testgen_config *tg = &csid->testgen; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, + input_format->code); + u8 lane_cnt = csid->phy.lane_cnt; + u32 val; - val = 1; - writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); + if (!lane_cnt) + lane_cnt = 4; - val = 0; - writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc)); + /* configure one DT, infinite frames */ + val = vc << TPG_VC_CFG0_VC_NUM; + val |= INTELEAVING_MODE_ONE_SHOT << TPG_VC_CFG0_LINE_INTERLEAVING_MODE; + val |= 0 << TPG_VC_CFG0_NUM_FRAMES; + writel_relaxed(val, csid->base + CSID_TPG_VC_CFG0); - val = 1; - writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(vc)); + val = 0x740 << TPG_VC_CFG1_H_BLANKING_COUNT; + val |= 0x3ff << TPG_VC_CFG1_V_BLANKING_COUNT; + writel_relaxed(val, csid->base + CSID_TPG_VC_CFG1); - val = 0; - writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(vc)); + writel_relaxed(0x12345678, csid->base + CSID_TPG_LFSR_SEED); - val = 1; - writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(vc)); + val = (input_format->height & 0x1fff) << TPG_DT_n_CFG_0_FRAME_HEIGHT; + val |= (input_format->width & 0x1fff) << TPG_DT_n_CFG_0_FRAME_WIDTH; + writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_0(0)); - val = 0; - writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(vc)); + val = format->data_type << TPG_DT_n_CFG_1_DATA_TYPE; + writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_1(0)); - val = 0; - writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc)); + val = (tg->mode - 1) << TPG_DT_n_CFG_2_PAYLOAD_MODE; + val |= 0xBE << TPG_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD; + val |= format->decode_format << TPG_DT_n_CFG_2_ENCODE_FORMAT; + writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_2(0)); - val = readl_relaxed(csid->base + CSID_RDI_CFG0(vc)); - val |= 1 << RDI_CFG0_ENABLE; - writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); - } + writel_relaxed(0, csid->base + CSID_TPG_COLOR_BARS_CFG); - if (tg->enabled) { - val = enable << TPG_CTRL_TEST_EN; - val |= 1 << TPG_CTRL_FS_PKT_EN; - val |= 1 << TPG_CTRL_FE_PKT_EN; - val |= (lane_cnt - 1) << TPG_CTRL_NUM_ACTIVE_LANES; - val |= 0x64 << TPG_CTRL_CYCLES_BETWEEN_PKTS; - val |= 0xA << TPG_CTRL_NUM_TRAIL_BYTES; - writel_relaxed(val, csid->base + CSID_TPG_CTRL); - } + writel_relaxed(0, csid->base + CSID_TPG_COLOR_BOX_CFG); - val = (lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES; - val |= csid->phy.lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL; - val |= phy_sel << CSI2_RX_CFG0_PHY_NUM_SEL; - writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG0); + val = enable << TPG_CTRL_TEST_EN; + val |= 1 << TPG_CTRL_FS_PKT_EN; + val |= 1 << TPG_CTRL_FE_PKT_EN; + val |= (lane_cnt - 1) << TPG_CTRL_NUM_ACTIVE_LANES; + val |= 0x64 << TPG_CTRL_CYCLES_BETWEEN_PKTS; + val |= 0xA << TPG_CTRL_NUM_TRAIL_BYTES; + writel_relaxed(val, csid->base + CSID_TPG_CTRL); +} - val = 1 << CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN; - if (vc > 3) - val |= 1 << CSI2_RX_CFG1_VC_MODE; - val |= 1 << CSI2_RX_CFG1_MISR_EN; - writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1); +static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc) +{ + /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */ + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc]; + const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats, + csid->res->formats->nformats, + input_format->code); + u32 val; - if (enable) - val = HALT_CMD_RESUME_AT_FRAME_BOUNDARY << RDI_CTRL_HALT_CMD; - else - val = HALT_CMD_HALT_AT_FRAME_BOUNDARY << RDI_CTRL_HALT_CMD; + /* + * DT_ID is a two bit bitfield that is concatenated with + * the four least significant bits of the five bit VC + * bitfield to generate an internal CID value. + * + * CSID_RDI_CFG0(vc) + * DT_ID : 28:27 + * VC : 26:22 + * DT : 21:16 + * + * CID : VC 3:0 << 2 | DT_ID 1:0 + */ + u8 dt_id = vc & 0x03; + + val = 1 << RDI_CFG0_BYTE_CNTR_EN; + val |= 1 << RDI_CFG0_FORMAT_MEASURE_EN; + val |= 1 << RDI_CFG0_TIMESTAMP_EN; + /* note: for non-RDI path, this should be format->decode_format */ + val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT; + val |= format->data_type << RDI_CFG0_DATA_TYPE; + val |= vc << RDI_CFG0_VIRTUAL_CHANNEL; + val |= dt_id << RDI_CFG0_DT_ID; + writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); + + /* CSID_TIMESTAMP_STB_POST_IRQ */ + val = 2 << RDI_CFG1_TIMESTAMP_STB_SEL; + writel_relaxed(val, csid->base + CSID_RDI_CFG1(vc)); + + val = 1; + writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc)); + + val = 0; + writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(vc)); + + val = 1; + writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc)); + + val = 0; + writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc)); + + val = 1; + writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(vc)); + + val = 0; + writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(vc)); + + val = 1; + writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(vc)); + + val = 0; + writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(vc)); + + val = 0; writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc)); + + val = readl_relaxed(csid->base + CSID_RDI_CFG0(vc)); + val |= enable << RDI_CFG0_ENABLE; + writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc)); } static void csid_configure_stream(struct csid_device *csid, u8 enable) { + struct csid_testgen_config *tg = &csid->testgen; u8 i; /* Loop through all enabled VCs and configure stream for each */ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) - if (csid->phy.en_vc & BIT(i)) - __csid_configure_stream(csid, enable, i); + if (csid->phy.en_vc & BIT(i)) { + if (tg->enabled) + __csid_configure_testgen(csid, enable, i); + + __csid_configure_rdi_stream(csid, enable, i); + __csid_configure_rx(csid, &csid->phy, i); + __csid_ctrl_rdi(csid, enable, i); + } } static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val) -- cgit v1.2.3 From 73ac545caf45fbb7a8ab45df47a0fd9e401d51f8 Mon Sep 17 00:00:00 2001 From: Atanas Filipov Date: Wed, 22 May 2024 18:46:58 +0300 Subject: media: qcom: camss: Decompose register and link operations Split link and register operations. Add dedicated link callback according to SoC identifier. Signed-off-by: Atanas Filipov Signed-off-by: Gjorgji Rosikopulos Reviewed-by: Bryan O'Donoghue Tested-by: Bryan O'Donoghue # sc8280xp/sm8250/sdm845/apq8016 Acked-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/camss/camss.c | 141 +++++++++++++++++------------- drivers/media/platform/qcom/camss/camss.h | 1 + 2 files changed, 81 insertions(+), 61 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 1b75918fc35b..8a3968d1cd83 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -1756,72 +1756,30 @@ static int camss_init_subdevices(struct camss *camss) } /* - * camss_register_entities - Register subdev nodes and create links + * camss_link_entities - Register subdev nodes and create links * @camss: CAMSS device * * Return 0 on success or a negative error code on failure */ -static int camss_register_entities(struct camss *camss) +static int camss_link_entities(struct camss *camss) { int i, j, k; int ret; - for (i = 0; i < camss->res->csiphy_num; i++) { - ret = msm_csiphy_register_entity(&camss->csiphy[i], - &camss->v4l2_dev); - if (ret < 0) { - dev_err(camss->dev, - "Failed to register csiphy%d entity: %d\n", - i, ret); - goto err_reg_csiphy; - } - } - - for (i = 0; i < camss->res->csid_num; i++) { - ret = msm_csid_register_entity(&camss->csid[i], - &camss->v4l2_dev); - if (ret < 0) { - dev_err(camss->dev, - "Failed to register csid%d entity: %d\n", - i, ret); - goto err_reg_csid; - } - } - - ret = msm_ispif_register_entities(camss->ispif, - &camss->v4l2_dev); - if (ret < 0) { - dev_err(camss->dev, "Failed to register ispif entities: %d\n", - ret); - goto err_reg_ispif; - } - - for (i = 0; i < camss->res->vfe_num; i++) { - ret = msm_vfe_register_entities(&camss->vfe[i], - &camss->v4l2_dev); - if (ret < 0) { - dev_err(camss->dev, - "Failed to register vfe%d entities: %d\n", - i, ret); - goto err_reg_vfe; - } - } - for (i = 0; i < camss->res->csiphy_num; i++) { for (j = 0; j < camss->res->csid_num; j++) { - ret = media_create_pad_link( - &camss->csiphy[i].subdev.entity, - MSM_CSIPHY_PAD_SRC, - &camss->csid[j].subdev.entity, - MSM_CSID_PAD_SINK, - 0); + ret = media_create_pad_link(&camss->csiphy[i].subdev.entity, + MSM_CSIPHY_PAD_SRC, + &camss->csid[j].subdev.entity, + MSM_CSID_PAD_SINK, + 0); if (ret < 0) { dev_err(camss->dev, "Failed to link %s->%s entities: %d\n", camss->csiphy[i].subdev.entity.name, camss->csid[j].subdev.entity.name, ret); - goto err_link; + return ret; } } } @@ -1829,19 +1787,18 @@ static int camss_register_entities(struct camss *camss) if (camss->ispif) { for (i = 0; i < camss->res->csid_num; i++) { for (j = 0; j < camss->ispif->line_num; j++) { - ret = media_create_pad_link( - &camss->csid[i].subdev.entity, - MSM_CSID_PAD_SRC, - &camss->ispif->line[j].subdev.entity, - MSM_ISPIF_PAD_SINK, - 0); + ret = media_create_pad_link(&camss->csid[i].subdev.entity, + MSM_CSID_PAD_SRC, + &camss->ispif->line[j].subdev.entity, + MSM_ISPIF_PAD_SINK, + 0); if (ret < 0) { dev_err(camss->dev, "Failed to link %s->%s entities: %d\n", camss->csid[i].subdev.entity.name, camss->ispif->line[j].subdev.entity.name, ret); - goto err_link; + return ret; } } } @@ -1863,7 +1820,7 @@ static int camss_register_entities(struct camss *camss) ispif->entity.name, vfe->entity.name, ret); - goto err_link; + return ret; } } } else { @@ -1884,15 +1841,67 @@ static int camss_register_entities(struct camss *camss) csid->entity.name, vfe->entity.name, ret); - goto err_link; + return ret; } } } + return 0; +} + +/* + * camss_register_entities - Register subdev nodes and create links + * @camss: CAMSS device + * + * Return 0 on success or a negative error code on failure + */ +static int camss_register_entities(struct camss *camss) +{ + int i; + int ret; + + for (i = 0; i < camss->res->csiphy_num; i++) { + ret = msm_csiphy_register_entity(&camss->csiphy[i], + &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, + "Failed to register csiphy%d entity: %d\n", + i, ret); + goto err_reg_csiphy; + } + } + + for (i = 0; i < camss->res->csid_num; i++) { + ret = msm_csid_register_entity(&camss->csid[i], + &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, + "Failed to register csid%d entity: %d\n", + i, ret); + goto err_reg_csid; + } + } + + ret = msm_ispif_register_entities(camss->ispif, + &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, "Failed to register ispif entities: %d\n", ret); + goto err_reg_ispif; + } + + for (i = 0; i < camss->res->vfe_num; i++) { + ret = msm_vfe_register_entities(&camss->vfe[i], + &camss->v4l2_dev); + if (ret < 0) { + dev_err(camss->dev, + "Failed to register vfe%d entities: %d\n", + i, ret); + goto err_reg_vfe; + } + } + return 0; -err_link: - i = camss->res->vfe_num; err_reg_vfe: for (i--; i >= 0; i--) msm_vfe_unregister_entities(&camss->vfe[i]); @@ -2212,6 +2221,10 @@ static int camss_probe(struct platform_device *pdev) if (ret < 0) goto err_v4l2_device_unregister; + ret = camss->res->link_entities(camss); + if (ret < 0) + goto err_register_subdevs; + if (num_subdevs) { camss->notifier.ops = &camss_subdev_notifier_ops; @@ -2291,6 +2304,7 @@ static const struct camss_resources msm8916_resources = { .csiphy_num = ARRAY_SIZE(csiphy_res_8x16), .csid_num = ARRAY_SIZE(csid_res_8x16), .vfe_num = ARRAY_SIZE(vfe_res_8x16), + .link_entities = camss_link_entities }; static const struct camss_resources msm8996_resources = { @@ -2302,6 +2316,7 @@ static const struct camss_resources msm8996_resources = { .csiphy_num = ARRAY_SIZE(csiphy_res_8x96), .csid_num = ARRAY_SIZE(csid_res_8x96), .vfe_num = ARRAY_SIZE(vfe_res_8x96), + .link_entities = camss_link_entities }; static const struct camss_resources sdm660_resources = { @@ -2313,6 +2328,7 @@ static const struct camss_resources sdm660_resources = { .csiphy_num = ARRAY_SIZE(csiphy_res_660), .csid_num = ARRAY_SIZE(csid_res_660), .vfe_num = ARRAY_SIZE(vfe_res_660), + .link_entities = camss_link_entities }; static const struct camss_resources sdm845_resources = { @@ -2323,6 +2339,7 @@ static const struct camss_resources sdm845_resources = { .csiphy_num = ARRAY_SIZE(csiphy_res_845), .csid_num = ARRAY_SIZE(csid_res_845), .vfe_num = ARRAY_SIZE(vfe_res_845), + .link_entities = camss_link_entities }; static const struct camss_resources sm8250_resources = { @@ -2336,6 +2353,7 @@ static const struct camss_resources sm8250_resources = { .csiphy_num = ARRAY_SIZE(csiphy_res_8250), .csid_num = ARRAY_SIZE(csid_res_8250), .vfe_num = ARRAY_SIZE(vfe_res_8250), + .link_entities = camss_link_entities }; static const struct camss_resources sc8280xp_resources = { @@ -2350,6 +2368,7 @@ static const struct camss_resources sc8280xp_resources = { .csiphy_num = ARRAY_SIZE(csiphy_res_sc8280xp), .csid_num = ARRAY_SIZE(csid_res_sc8280xp), .vfe_num = ARRAY_SIZE(vfe_res_sc8280xp), + .link_entities = camss_link_entities }; static const struct of_device_id camss_dt_match[] = { diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index 9e73a55a257b..588a52eb2d43 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -98,6 +98,7 @@ struct camss_resources { const unsigned int csiphy_num; const unsigned int csid_num; const unsigned int vfe_num; + int (*link_entities)(struct camss *camss); }; struct camss { -- cgit v1.2.3 From b1e6eef535dfc4ad32332e227c5757ded4512aa2 Mon Sep 17 00:00:00 2001 From: Milen Mitkov Date: Wed, 22 May 2024 18:46:59 +0300 Subject: media: qcom: camss: Decouple VFE from CSID Decouple the direct calls to VFE's vfe_get/put in the CSID subdev in order to prepare for the introduction of IFE subdev. Also decouple CSID base address from VFE since on the Titan platform CSID register base address resides within VFE's base address. Signed-off-by: Milen Mitkov Signed-off-by: Gjorgji Rosikopulos Reviewed-by: Bryan O'Donoghue Tested-by: Bryan O'Donoghue # sc8280xp/sm8250/sdm845/apq8016 Acked-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/camss/camss-csid.c | 16 ++++-- drivers/media/platform/qcom/camss/camss-csid.h | 1 + drivers/media/platform/qcom/camss/camss.c | 69 ++++++++++++++++++++++++++ drivers/media/platform/qcom/camss/camss.h | 8 +++ 4 files changed, 89 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index 5b23f5b8746d..858db5d4ca75 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -602,7 +602,6 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) struct csid_device *csid = v4l2_get_subdevdata(sd); struct camss *camss = csid->camss; struct device *dev = camss->dev; - struct vfe_device *vfe = &camss->vfe[csid->id]; int ret = 0; if (on) { @@ -611,7 +610,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) * switching on the CSID. Do so unconditionally, as there is no * drawback in following the same powering order on older SoCs. */ - ret = vfe_get(vfe); + ret = csid->res->parent_dev_ops->get(camss, csid->id); if (ret < 0) return ret; @@ -663,7 +662,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) regulator_bulk_disable(csid->num_supplies, csid->supplies); pm_runtime_put_sync(dev); - vfe_put(vfe); + csid->res->parent_dev_ops->put(camss, csid->id); } return ret; @@ -1021,6 +1020,11 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, csid->id = id; csid->res = &res->csid; + if (dev_WARN_ONCE(dev, !csid->res->parent_dev_ops, + "Error: CSID depends on VFE/IFE device ops!\n")) { + return -EINVAL; + } + csid->res->hw_ops->subdev_init(csid); /* Memory */ @@ -1031,9 +1035,11 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, * VFE to be initialized before CSID */ if (id >= 2) /* VFE/CSID lite */ - csid->base = camss->vfe[id].base + VFE_480_LITE_CSID_OFFSET; + csid->base = csid->res->parent_dev_ops->get_base_address(camss, id) + + VFE_480_LITE_CSID_OFFSET; else - csid->base = camss->vfe[id].base + VFE_480_CSID_OFFSET; + csid->base = csid->res->parent_dev_ops->get_base_address(camss, id) + + VFE_480_CSID_OFFSET; } else { csid->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]); if (IS_ERR(csid->base)) diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h index 0e385d17c250..8cdae98e4dca 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.h +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -157,6 +157,7 @@ struct csid_hw_ops { struct csid_subdev_resources { bool is_lite; const struct csid_hw_ops *hw_ops; + const struct parent_dev_ops *parent_dev_ops; const struct csid_formats *formats; }; diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 8a3968d1cd83..1f1f44f6fbb2 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -32,6 +32,8 @@ #define CAMSS_CLOCK_MARGIN_NUMERATOR 105 #define CAMSS_CLOCK_MARGIN_DENOMINATOR 100 +static const struct parent_dev_ops vfe_parent_dev_ops; + static const struct camss_subdev_resources csiphy_res_8x16[] = { /* CSIPHY0 */ { @@ -84,6 +86,7 @@ static const struct camss_subdev_resources csid_res_8x16[] = { .interrupt = { "csid0" }, .csid = { .hw_ops = &csid_ops_4_1, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_4_1 } }, @@ -105,6 +108,7 @@ static const struct camss_subdev_resources csid_res_8x16[] = { .interrupt = { "csid1" }, .csid = { .hw_ops = &csid_ops_4_1, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_4_1 } }, @@ -216,6 +220,7 @@ static const struct camss_subdev_resources csid_res_8x96[] = { .interrupt = { "csid0" }, .csid = { .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_4_7 } }, @@ -237,6 +242,7 @@ static const struct camss_subdev_resources csid_res_8x96[] = { .interrupt = { "csid1" }, .csid = { .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_4_7 } }, @@ -258,6 +264,7 @@ static const struct camss_subdev_resources csid_res_8x96[] = { .interrupt = { "csid2" }, .csid = { .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_4_7 } }, @@ -279,6 +286,7 @@ static const struct camss_subdev_resources csid_res_8x96[] = { .interrupt = { "csid3" }, .csid = { .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_4_7 } } @@ -425,6 +433,7 @@ static const struct camss_subdev_resources csid_res_660[] = { .interrupt = { "csid0" }, .csid = { .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_4_7 } }, @@ -449,6 +458,7 @@ static const struct camss_subdev_resources csid_res_660[] = { .interrupt = { "csid1" }, .csid = { .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_4_7 } }, @@ -473,6 +483,7 @@ static const struct camss_subdev_resources csid_res_660[] = { .interrupt = { "csid2" }, .csid = { .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_4_7 } }, @@ -497,6 +508,7 @@ static const struct camss_subdev_resources csid_res_660[] = { .interrupt = { "csid3" }, .csid = { .hw_ops = &csid_ops_4_7, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_4_7 } } @@ -683,6 +695,7 @@ static const struct camss_subdev_resources csid_res_845[] = { .interrupt = { "csid0" }, .csid = { .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_gen2 } }, @@ -707,6 +720,7 @@ static const struct camss_subdev_resources csid_res_845[] = { .interrupt = { "csid1" }, .csid = { .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_gen2 } }, @@ -732,6 +746,7 @@ static const struct camss_subdev_resources csid_res_845[] = { .csid = { .is_lite = true, .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_gen2 } } @@ -914,6 +929,7 @@ static const struct camss_subdev_resources csid_res_8250[] = { .interrupt = { "csid0" }, .csid = { .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_gen2 } }, @@ -930,6 +946,7 @@ static const struct camss_subdev_resources csid_res_8250[] = { .interrupt = { "csid1" }, .csid = { .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_gen2 } }, @@ -946,6 +963,7 @@ static const struct camss_subdev_resources csid_res_8250[] = { .csid = { .is_lite = true, .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_gen2 } }, @@ -962,6 +980,7 @@ static const struct camss_subdev_resources csid_res_8250[] = { .csid = { .is_lite = true, .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_gen2 } } @@ -1161,6 +1180,7 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { .interrupt = { "csid0" }, .csid = { .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_gen2 } }, @@ -1176,6 +1196,7 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { .interrupt = { "csid1" }, .csid = { .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_gen2 } }, @@ -1191,6 +1212,7 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { .interrupt = { "csid2" }, .csid = { .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_gen2 } }, @@ -1206,6 +1228,7 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { .interrupt = { "csid3" }, .csid = { .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_gen2 } }, @@ -1221,6 +1244,7 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { .csid = { .is_lite = true, .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_gen2 } }, @@ -1236,6 +1260,7 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { .csid = { .is_lite = true, .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_gen2 } }, @@ -1251,6 +1276,7 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { .csid = { .is_lite = true, .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_gen2 } }, @@ -1266,6 +1292,7 @@ static const struct camss_subdev_resources csid_res_sc8280xp[] = { .csid = { .is_lite = true, .hw_ops = &csid_ops_gen2, + .parent_dev_ops = &vfe_parent_dev_ops, .formats = &csid_formats_gen2 } } @@ -1610,6 +1637,48 @@ void camss_pm_domain_off(struct camss *camss, int id) } } +static int vfe_parent_dev_ops_get(struct camss *camss, int id) +{ + int ret = -EINVAL; + + if (id < camss->res->vfe_num) { + struct vfe_device *vfe = &camss->vfe[id]; + + ret = vfe_get(vfe); + } + + return ret; +} + +static int vfe_parent_dev_ops_put(struct camss *camss, int id) +{ + if (id < camss->res->vfe_num) { + struct vfe_device *vfe = &camss->vfe[id]; + + vfe_put(vfe); + } + + return 0; +} + +static void __iomem +*vfe_parent_dev_ops_get_base_address(struct camss *camss, int id) +{ + if (id < camss->res->vfe_num) { + struct vfe_device *vfe = &camss->vfe[id]; + + return vfe->base; + } + + return NULL; +} + +static const struct parent_dev_ops vfe_parent_dev_ops = { + .get = vfe_parent_dev_ops_get, + .put = vfe_parent_dev_ops_put, + .get_base_address = vfe_parent_dev_ops_get_base_address +}; + /* * camss_of_parse_endpoint_node - Parse port endpoint node * @dev: Device diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index 588a52eb2d43..73c47c07fc30 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -135,6 +135,12 @@ struct camss_clock { u32 nfreqs; }; +struct parent_dev_ops { + int (*get)(struct camss *camss, int id); + int (*put)(struct camss *camss, int id); + void __iomem *(*get_base_address)(struct camss *camss, int id); +}; + void camss_add_clock_margin(u64 *rate); int camss_enable_clocks(int nclocks, struct camss_clock *clock, struct device *dev); @@ -145,6 +151,8 @@ s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp, int camss_get_pixel_clock(struct media_entity *entity, u64 *pixel_clock); int camss_pm_domain_on(struct camss *camss, int id); void camss_pm_domain_off(struct camss *camss, int id); +int camss_vfe_get(struct camss *camss, int id); +void camss_vfe_put(struct camss *camss, int id); void camss_delete(struct camss *camss); #endif /* QC_MSM_CAMSS_H */ -- cgit v1.2.3 From e28bfde3f0cd6d59b2b1b7b3b443848d75b7dcad Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Sun, 9 Jun 2024 14:03:08 -0700 Subject: media: exynos4-is: add missing MODULE_DESCRIPTION() macros make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/platform/samsung/exynos4-is/exynos-fimc-lite.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/platform/samsung/exynos4-is/exynos-fimc-is.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/platform/samsung/exynos4-is/exynos4-is-common.o Add the missing invocations of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Signed-off-by: Hans Verkuil --- drivers/media/platform/samsung/exynos4-is/common.c | 1 + drivers/media/platform/samsung/exynos4-is/fimc-is.c | 1 + drivers/media/platform/samsung/exynos4-is/fimc-lite.c | 1 + 3 files changed, 3 insertions(+) diff --git a/drivers/media/platform/samsung/exynos4-is/common.c b/drivers/media/platform/samsung/exynos4-is/common.c index e41333535eac..77007f1a909b 100644 --- a/drivers/media/platform/samsung/exynos4-is/common.c +++ b/drivers/media/platform/samsung/exynos4-is/common.c @@ -44,4 +44,5 @@ void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap) } EXPORT_SYMBOL(__fimc_vidioc_querycap); +MODULE_DESCRIPTION("Samsung S5P/EXYNOS4 SoC Camera Subsystem driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is.c b/drivers/media/platform/samsung/exynos4-is/fimc-is.c index 39aab667910d..0a4b58daf924 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-is.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-is.c @@ -999,4 +999,5 @@ module_exit(fimc_is_module_exit); MODULE_ALIAS("platform:" FIMC_IS_DRV_NAME); MODULE_AUTHOR("Younghwan Joo "); MODULE_AUTHOR("Sylwester Nawrocki "); +MODULE_DESCRIPTION("Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c index d1d860fa3454..1a4d75443215 100644 --- a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c @@ -1662,5 +1662,6 @@ static struct platform_driver fimc_lite_driver = { } }; module_platform_driver(fimc_lite_driver); +MODULE_DESCRIPTION("Samsung EXYNOS FIMC-LITE (camera host interface) driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" FIMC_LITE_DRV_NAME); -- cgit v1.2.3 From 9d7d3a3b00ff2068ef73d6de904f08e331d07e00 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Sun, 9 Jun 2024 14:32:49 -0700 Subject: media: ti: add missing MODULE_DESCRIPTION() macros make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/platform/ti/vpe/ti-vpdma.o Add the missing invocations of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/vpe/vpdma.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/ti/vpe/vpdma.c b/drivers/media/platform/ti/vpe/vpdma.c index f8998a8ad371..da90d7f03f82 100644 --- a/drivers/media/platform/ti/vpe/vpdma.c +++ b/drivers/media/platform/ti/vpe/vpdma.c @@ -1173,4 +1173,5 @@ EXPORT_SYMBOL(vpdma_create); MODULE_AUTHOR("Texas Instruments Inc."); MODULE_FIRMWARE(VPDMA_FIRMWARE); +MODULE_DESCRIPTION("TI VPDMA helper library"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 427527e701e28ebeddba70f08b4f576e42ae5a27 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Mon, 10 Jun 2024 17:34:04 -0700 Subject: media: atomisp: add missing MODULE_DESCRIPTION() macros make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/staging/media/atomisp/i2c/atomisp-mt9m114.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/staging/media/atomisp/i2c/atomisp-libmsrlisthelper.o Add the missing invocations of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Signed-off-by: Hans Verkuil --- drivers/staging/media/atomisp/i2c/atomisp-libmsrlisthelper.c | 1 + drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/staging/media/atomisp/i2c/atomisp-libmsrlisthelper.c b/drivers/staging/media/atomisp/i2c/atomisp-libmsrlisthelper.c index 7a20d918a9d5..3499353f8ea5 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-libmsrlisthelper.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-libmsrlisthelper.c @@ -207,4 +207,5 @@ module_init(init_msrlisthelper); module_exit(exit_msrlisthelper); MODULE_AUTHOR("Jukka Kaartinen "); +MODULE_DESCRIPTION("Helper library to load, parse and apply large register lists"); MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c index 23b1001c2a55..918ea4fa9f6b 100644 --- a/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c +++ b/drivers/staging/media/atomisp/i2c/atomisp-mt9m114.c @@ -1614,4 +1614,5 @@ static struct i2c_driver mt9m114_driver = { module_i2c_driver(mt9m114_driver); MODULE_AUTHOR("Shuguang Gong "); +MODULE_DESCRIPTION("Aptina mt9m114 sensor support module"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From ddaa23afcb2e1a8e1b9b212c96d5af5b40a8f078 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Tue, 11 Jun 2024 20:21:34 -0700 Subject: media: dvb-frontends: add missing MODULE_DESCRIPTION() macros With ARCH=x86, make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/dvb-frontends/au8522_decoder.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/dvb-frontends/mb86a16.o Add the missing invocations of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Signed-off-by: Hans Verkuil --- drivers/media/dvb-frontends/au8522_decoder.c | 1 + drivers/media/dvb-frontends/mb86a16.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c index acc27376c246..d02a92a81c60 100644 --- a/drivers/media/dvb-frontends/au8522_decoder.c +++ b/drivers/media/dvb-frontends/au8522_decoder.c @@ -25,6 +25,7 @@ #include "au8522_priv.h" MODULE_AUTHOR("Devin Heitmueller"); +MODULE_DESCRIPTION("Auvitek AU8522 QAM/8VSB demodulator driver and video decoder"); MODULE_LICENSE("GPL"); static int au8522_analog_debug; diff --git a/drivers/media/dvb-frontends/mb86a16.c b/drivers/media/dvb-frontends/mb86a16.c index 0fc45896e7b8..9033e39d75f4 100644 --- a/drivers/media/dvb-frontends/mb86a16.c +++ b/drivers/media/dvb-frontends/mb86a16.c @@ -1854,5 +1854,6 @@ error: return NULL; } EXPORT_SYMBOL_GPL(mb86a16_attach); +MODULE_DESCRIPTION("Fujitsu MB86A16 DVB-S/DSS DC Receiver driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Manu Abraham"); -- cgit v1.2.3 From bec6bec031943c3a6b797d3af99dade3586e3b91 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Wed, 12 Jun 2024 11:19:49 -0700 Subject: media: uda1342: add missing MODULE_DESCRIPTION() macro With ARCH=x86, make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/i2c/uda1342.o Add the missing invocation of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Signed-off-by: Hans Verkuil --- drivers/media/i2c/uda1342.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/i2c/uda1342.c b/drivers/media/i2c/uda1342.c index da7bc4700bed..abd052a44bd7 100644 --- a/drivers/media/i2c/uda1342.c +++ b/drivers/media/i2c/uda1342.c @@ -95,4 +95,5 @@ static struct i2c_driver uda1342_driver = { module_i2c_driver(uda1342_driver); +MODULE_DESCRIPTION("Philips UDA1342 audio codec driver"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 33a1ec476d6fb889e26781052c59f00259ec15a4 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Wed, 12 Jun 2024 16:58:59 -0700 Subject: usb: uvc: add missing MODULE_DESCRIPTION() macro With ARCH=arm64, make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/common/uvc.o Add the missing invocation of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Reviewed-by: Kieran Bingham Signed-off-by: Hans Verkuil --- drivers/media/common/uvc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/common/uvc.c b/drivers/media/common/uvc.c index 9c0ba7a6c185..c54c2268fee6 100644 --- a/drivers/media/common/uvc.c +++ b/drivers/media/common/uvc.c @@ -180,4 +180,5 @@ const struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16]) } EXPORT_SYMBOL_GPL(uvc_format_by_guid); +MODULE_DESCRIPTION("USB Video Class common code"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 4f0200f0c373231d4a03dcbb1a834aca72cd8cd8 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Wed, 12 Jun 2024 21:04:30 -0700 Subject: media: dvb-usb: add missing MODULE_DESCRIPTION() macros With ARCH=x86, make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/usb/dvb-usb/dvb-usb-dibusb-common.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/usb/dvb-usb/dvb-usb-dibusb-mc-common.o Add the missing invocations of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Signed-off-by: Hans Verkuil --- drivers/media/usb/dvb-usb/dibusb-common.c | 1 + drivers/media/usb/dvb-usb/dibusb-mc-common.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/media/usb/dvb-usb/dibusb-common.c b/drivers/media/usb/dvb-usb/dibusb-common.c index aff60c10cb0b..20f1ef3393a5 100644 --- a/drivers/media/usb/dvb-usb/dibusb-common.c +++ b/drivers/media/usb/dvb-usb/dibusb-common.c @@ -14,6 +14,7 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level (1=info (|-able))." DVB_USB_DEBUG_STATUS); +MODULE_DESCRIPTION("Common methods for dibusb-based receivers"); MODULE_LICENSE("GPL"); #define deb_info(args...) dprintk(debug,0x01,args) diff --git a/drivers/media/usb/dvb-usb/dibusb-mc-common.c b/drivers/media/usb/dvb-usb/dibusb-mc-common.c index b8cde4cded33..36bc7762acf4 100644 --- a/drivers/media/usb/dvb-usb/dibusb-mc-common.c +++ b/drivers/media/usb/dvb-usb/dibusb-mc-common.c @@ -8,6 +8,7 @@ #include "dibusb.h" +MODULE_DESCRIPTION("Common methods for DIB3000MC"); MODULE_LICENSE("GPL"); /* 3000MC/P stuff */ -- cgit v1.2.3 From 4caf6d93d9f2c11d6441c64e1c549c445fa322ed Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Fri, 21 Jun 2024 09:35:22 +0800 Subject: media: qcom: camss: Add check for v4l2_fwnode_endpoint_parse Add check for the return value of v4l2_fwnode_endpoint_parse() and return the error if it fails in order to catch the error. Signed-off-by: Chen Ni Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/camss/camss.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 1f1f44f6fbb2..51b1d3550421 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -1695,8 +1695,11 @@ static int camss_of_parse_endpoint_node(struct device *dev, struct v4l2_mbus_config_mipi_csi2 *mipi_csi2; struct v4l2_fwnode_endpoint vep = { { 0 } }; unsigned int i; + int ret; - v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep); + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep); + if (ret) + return ret; csd->interface.csiphy_id = vep.base.port; -- cgit v1.2.3 From 32c1935280f11ef03efdb069aa46c0bc631eab7d Mon Sep 17 00:00:00 2001 From: Nils Rothaug Date: Sun, 23 Jun 2024 12:46:43 +0200 Subject: media: tuner-simple: Add support for Tena TNF931D-DFDR1 Tuner ranges were determined by USB capturing the vendor driver of a MyGica UTV3 video capture card. Signed-off-by: Nils Rothaug Signed-off-by: Sean Young Signed-off-by: Hans Verkuil --- Documentation/admin-guide/media/tuner-cardlist.rst | 2 ++ drivers/media/tuners/tuner-types.c | 21 +++++++++++++++++++++ include/media/tuner.h | 1 + 3 files changed, 24 insertions(+) diff --git a/Documentation/admin-guide/media/tuner-cardlist.rst b/Documentation/admin-guide/media/tuner-cardlist.rst index 362617c59c5d..65ecf48ddf24 100644 --- a/Documentation/admin-guide/media/tuner-cardlist.rst +++ b/Documentation/admin-guide/media/tuner-cardlist.rst @@ -97,4 +97,6 @@ Tuner number Card name 89 Sony BTF-PG472Z PAL/SECAM 90 Sony BTF-PK467Z NTSC-M-JP 91 Sony BTF-PB463Z NTSC-M +92 Silicon Labs Si2157 tuner +93 Tena TNF931D-DFDR1 ============ ===================================================== diff --git a/drivers/media/tuners/tuner-types.c b/drivers/media/tuners/tuner-types.c index ff5a6c0acdd4..c26f1296e18f 100644 --- a/drivers/media/tuners/tuner-types.c +++ b/drivers/media/tuners/tuner-types.c @@ -1434,6 +1434,22 @@ static struct tuner_params tuner_sony_btf_pg463z_params[] = { }, }; +/* ------------- TUNER_TENA_TNF_931D_DFDR1 - NXP TDA6509A ------------- */ + +static struct tuner_range tuner_tena_tnf_931d_dfdr1_ranges[] = { + { 16 * 161.15 /*MHz*/, 0x8e, 0x01, }, + { 16 * 463.15 /*MHz*/, 0x8e, 0x02, }, + { 16 * 999.99 , 0x8e, 0x08, }, +}; + +static struct tuner_params tuner_tena_tnf_931d_dfdr1_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_tena_tnf_931d_dfdr1_ranges, + .count = ARRAY_SIZE(tuner_tena_tnf_931d_dfdr1_ranges), + }, +}; + /* --------------------------------------------------------------------- */ struct tunertype tuners[] = { @@ -1946,6 +1962,11 @@ struct tunertype tuners[] = { .name = "Silicon Labs Si2157 tuner", /* see si2157.c for details */ }, + [TUNER_TENA_TNF_931D_DFDR1] = { + .name = "Tena TNF931D-DFDR1", + .params = tuner_tena_tnf_931d_dfdr1_params, + .count = ARRAY_SIZE(tuner_tena_tnf_931d_dfdr1_params), + } }; EXPORT_SYMBOL(tuners); diff --git a/include/media/tuner.h b/include/media/tuner.h index a7796e0a3659..c5fd6faabfd6 100644 --- a/include/media/tuner.h +++ b/include/media/tuner.h @@ -133,6 +133,7 @@ #define TUNER_SONY_BTF_PK467Z 90 /* NTSC_JP */ #define TUNER_SONY_BTF_PB463Z 91 /* NTSC */ #define TUNER_SI2157 92 +#define TUNER_TENA_TNF_931D_DFDR1 93 /* tv card specific */ #define TDA9887_PRESENT (1<<0) -- cgit v1.2.3 From e31604d5922ef76a58bd3f9ae719486887eb266e Mon Sep 17 00:00:00 2001 From: Nils Rothaug Date: Sun, 23 Jun 2024 12:46:44 +0200 Subject: media: rc: add keymap for MyGica UTV3 remote Add keymap for the simple IR (RC-5) remote that comes with the MyGica UTV3 Analog USB2.0 TV Box video capture card. Signed-off-by: Nils Rothaug Signed-off-by: Sean Young Signed-off-by: Hans Verkuil --- drivers/media/rc/keymaps/Makefile | 1 + drivers/media/rc/keymaps/rc-mygica-utv3.c | 69 +++++++++++++++++++++++++++++++ include/media/rc-map.h | 1 + 3 files changed, 71 insertions(+) create mode 100644 drivers/media/rc/keymaps/rc-mygica-utv3.c diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index f19558fdab0c..7fdf0d9edbfd 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -84,6 +84,7 @@ obj-$(CONFIG_RC_MAP) += \ rc-msi-digivox-ii.o \ rc-msi-tvanywhere.o \ rc-msi-tvanywhere-plus.o \ + rc-mygica-utv3.o \ rc-nebula.o \ rc-nec-terratec-cinergy-xs.o \ rc-norwood.o \ diff --git a/drivers/media/rc/keymaps/rc-mygica-utv3.c b/drivers/media/rc/keymaps/rc-mygica-utv3.c new file mode 100644 index 000000000000..f32b8281459b --- /dev/null +++ b/drivers/media/rc/keymaps/rc-mygica-utv3.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* rc-mygica-utv3.c - Keytable for the MyGica UTV3 Analog USB2.0 TV Box + * + * Copyright (c) 2024 by Nils Rothaug + */ + +#include +#include + +static struct rc_map_table mygica_utv3[] = { + { 0x0d, KEY_MUTE }, + { 0x38, KEY_VIDEO }, /* Source */ + { 0x14, KEY_RADIO }, /* FM Radio */ + { 0x0c, KEY_POWER2 }, + + { 0x01, KEY_NUMERIC_1}, + { 0x02, KEY_NUMERIC_2}, + { 0x03, KEY_NUMERIC_3}, + { 0x04, KEY_NUMERIC_4}, + { 0x05, KEY_NUMERIC_5}, + { 0x06, KEY_NUMERIC_6}, + { 0x07, KEY_NUMERIC_7}, + { 0x08, KEY_NUMERIC_8}, + { 0x09, KEY_NUMERIC_9}, + { 0x00, KEY_NUMERIC_0}, + + { 0x0a, KEY_DIGITS }, /* Single/double/triple digit */ + { 0x0e, KEY_CAMERA }, /* Snapshot */ + { 0x0f, KEY_ZOOM }, /* Full Screen */ + { 0x29, KEY_LAST }, /* Recall (return to previous channel) */ + + { 0x17, KEY_PLAY }, + { 0x1f, KEY_RECORD }, + { 0x0b, KEY_STOP }, + { 0x16, KEY_PAUSE }, + + { 0x20, KEY_CHANNELUP }, + { 0x21, KEY_CHANNELDOWN }, + { 0x10, KEY_VOLUMEUP }, + { 0x11, KEY_VOLUMEDOWN }, + { 0x26, KEY_REWIND }, + { 0x27, KEY_FASTFORWARD }, +}; + +static struct rc_map_list mygica_utv3_map = { + .map = { + .scan = mygica_utv3, + .size = ARRAY_SIZE(mygica_utv3), + .rc_proto = RC_PROTO_RC5, + .name = RC_MAP_MYGICA_UTV3, + } +}; + +static int __init init_rc_map_mygica_utv3(void) +{ + return rc_map_register(&mygica_utv3_map); +} + +static void __exit exit_rc_map_mygica_utv3(void) +{ + rc_map_unregister(&mygica_utv3_map); +} + +module_init(init_rc_map_mygica_utv3) +module_exit(exit_rc_map_mygica_utv3) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Nils Rothaug"); +MODULE_DESCRIPTION("MyGica UTV3 Analog USB2.0 TV Box remote keytable"); diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 4676545ffd8f..4867eb2f931e 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -290,6 +290,7 @@ struct rc_map *rc_map_get(const char *name); #define RC_MAP_MSI_DIGIVOX_III "rc-msi-digivox-iii" #define RC_MAP_MSI_TVANYWHERE "rc-msi-tvanywhere" #define RC_MAP_MSI_TVANYWHERE_PLUS "rc-msi-tvanywhere-plus" +#define RC_MAP_MYGICA_UTV3 "rc-mygica-utv3" #define RC_MAP_NEBULA "rc-nebula" #define RC_MAP_NEC_TERRATEC_CINERGY_XS "rc-nec-terratec-cinergy-xs" #define RC_MAP_NORWOOD "rc-norwood" -- cgit v1.2.3 From fbf657d5fde81413e19938a629ad3018ee82c6ca Mon Sep 17 00:00:00 2001 From: Nils Rothaug Date: Sun, 23 Jun 2024 12:46:45 +0200 Subject: media: dt-bindings: rc: add rc-mygica-utv3 Add a binding for the rc-mygica-utv3 keymap Signed-off-by: Nils Rothaug Acked-by: Rob Herring (Arm) Signed-off-by: Sean Young Signed-off-by: Hans Verkuil --- Documentation/devicetree/bindings/media/rc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/media/rc.yaml b/Documentation/devicetree/bindings/media/rc.yaml index 7bbe580c80f7..dedc5a4b81ec 100644 --- a/Documentation/devicetree/bindings/media/rc.yaml +++ b/Documentation/devicetree/bindings/media/rc.yaml @@ -103,6 +103,7 @@ properties: - rc-msi-digivox-iii - rc-msi-tvanywhere - rc-msi-tvanywhere-plus + - rc-mygica-utv3 - rc-nebula - rc-nec-terratec-cinergy-xs - rc-norwood -- cgit v1.2.3 From ceac017e1292913a256f1d90ff367e9e868f1c86 Mon Sep 17 00:00:00 2001 From: Nils Rothaug Date: Sun, 23 Jun 2024 12:46:46 +0200 Subject: media: em28xx: Add support for MyGica UTV3 The MyGica UTV3 Analog USB2.0 TV Box is a USB video capture card that has analog TV, composite video, and FM radio inputs, an IR remote, and provides audio only as Line Out, but not over USB. Mine is prepared for an FM tuner, but not equipped with one. Support for FM radio is therefore missing. The device contains: - Empia EM2860 USB bridge - Philips SAA7113 video decoder - NXP TDA9801T demodulator - Tena TNF931D-DFDR1 tuner - ST HCF4052 demux, switches input audio to Line Out Signed-off-by: Nils Rothaug Signed-off-by: Sean Young Signed-off-by: Hans Verkuil --- .../admin-guide/media/em28xx-cardlist.rst | 8 ++++ drivers/media/usb/em28xx/em28xx-cards.c | 52 ++++++++++++++++++++++ drivers/media/usb/em28xx/em28xx.h | 1 + 3 files changed, 61 insertions(+) diff --git a/Documentation/admin-guide/media/em28xx-cardlist.rst b/Documentation/admin-guide/media/em28xx-cardlist.rst index ace65718ea22..7dac07986d91 100644 --- a/Documentation/admin-guide/media/em28xx-cardlist.rst +++ b/Documentation/admin-guide/media/em28xx-cardlist.rst @@ -438,3 +438,11 @@ EM28xx cards list - MyGica iGrabber - em2860 - 1f4d:1abe + * - 106 + - Hauppauge USB QuadHD ATSC + - em28274 + - 2040:846d + * - 107 + - MyGica UTV3 Analog USB2.0 TV Box + - em2860 + - eb1a:2860 diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index bae76023cf71..a51cbcf429e1 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -554,6 +554,30 @@ static struct em28xx_reg_seq hauppauge_usb_quadhd_atsc_reg_seq[] = { {-1, -1, -1, -1}, }; +/* + * MyGica USB TV Box + * GPIO_1,0: 00=Composite audio + * 01=Tuner audio + * 10=Mute audio + * 11=FM radio? (if equipped) + * GPIO_2-6: Unused + * GPIO_7: ?? + */ +static const struct em28xx_reg_seq mygica_utv3_composite_audio_gpio[] = { + {EM2820_R08_GPIO_CTRL, 0xfc, 0xff, 0}, + { -1, -1, -1, -1}, +}; + +static const struct em28xx_reg_seq mygica_utv3_tuner_audio_gpio[] = { + {EM2820_R08_GPIO_CTRL, 0xfd, 0xff, 0}, + { -1, -1, -1, -1}, +}; + +static const struct em28xx_reg_seq mygica_utv3_suspend_gpio[] = { + {EM2820_R08_GPIO_CTRL, 0xfe, 0xff, 0}, + { -1, -1, -1, -1}, +}; + /* * Button definitions */ @@ -2578,6 +2602,32 @@ const struct em28xx_board em28xx_boards[] = { .tuner_gpio = hauppauge_usb_quadhd_atsc_reg_seq, .leds = hauppauge_usb_quadhd_leds, }, + /* + * eb1a:2860 MyGica UTV3 Analog USB2.0 TV Box + * Empia EM2860, Philips SAA7113, NXP TDA9801T demod, + * Tena TNF931D-DFDR1 tuner (contains NXP TDA6509A), + * ST HCF4052 demux (switches audio to line out), + * no audio over USB + */ + [EM2860_BOARD_MYGICA_UTV3] = { + .name = "MyGica UTV3 Analog USB2.0 TV Box", + .xclk = EM28XX_XCLK_IR_RC5_MODE | EM28XX_XCLK_FREQUENCY_12MHZ, + .tuner_type = TUNER_TENA_TNF_931D_DFDR1, + .ir_codes = RC_MAP_MYGICA_UTV3, + .decoder = EM28XX_SAA711X, + .suspend_gpio = mygica_utv3_suspend_gpio, + .input = { { + .type = EM28XX_VMUX_COMPOSITE, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = mygica_utv3_composite_audio_gpio, + }, { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_VIDEO, + .gpio = mygica_utv3_tuner_audio_gpio, + } }, + }, }; EXPORT_SYMBOL_GPL(em28xx_boards); @@ -2819,6 +2869,7 @@ static const struct em28xx_hash_table em28xx_eeprom_hash[] = { {0x63f653bd, EM2870_BOARD_REDDO_DVB_C_USB_BOX, TUNER_ABSENT}, {0x4e913442, EM2882_BOARD_DIKOM_DK300, TUNER_XC2028}, {0x85dd871e, EM2882_BOARD_ZOLID_HYBRID_TV_STICK, TUNER_XC2028}, + {0x8f597549, EM2860_BOARD_MYGICA_UTV3, TUNER_TENA_TNF_931D_DFDR1}, }; /* I2C devicelist hash table for devices with generic USB IDs */ @@ -2831,6 +2882,7 @@ static const struct em28xx_hash_table em28xx_i2c_hash[] = { {0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF}, {0x6b800080, EM2874_BOARD_LEADERSHIP_ISDBT, TUNER_ABSENT}, {0x27e10080, EM2882_BOARD_ZOLID_HYBRID_TV_STICK, TUNER_XC2028}, + {0x840d0484, EM2860_BOARD_MYGICA_UTV3, TUNER_TENA_TNF_931D_DFDR1}, }; /* NOTE: introduce a separate hash table for devices with 16 bit eeproms */ diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index db18dd814a67..f3449c240d21 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -143,6 +143,7 @@ #define EM28178_BOARD_PCTV_461E_V2 104 #define EM2860_BOARD_MYGICA_IGRABBER 105 #define EM2874_BOARD_HAUPPAUGE_USB_QUADHD 106 +#define EM2860_BOARD_MYGICA_UTV3 107 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 -- cgit v1.2.3 From 361e2ff5cefe10da266ced01845ab0b08112a1fd Mon Sep 17 00:00:00 2001 From: Nils Rothaug Date: Sun, 23 Jun 2024 12:46:47 +0200 Subject: media: em28xx: Set GPIOs for non-audio boards when switching input Fixes changing the Line Out audio source with the video input on MyGica UTV3 board. Previously, GPIOs were only set in em28xx_set_audio_source(), which only boards with USB audio support reach. Signed-off-by: Nils Rothaug Signed-off-by: Sean Young Signed-off-by: Hans Verkuil --- drivers/media/usb/em28xx/em28xx-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 61d7bf701d57..29a7f3f19b56 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -416,8 +416,9 @@ int em28xx_audio_analog_set(struct em28xx *dev) int ret, i; u8 xclk; + /* Set GPIOs here for boards without audio */ if (dev->int_audio_type == EM28XX_INT_AUDIO_NONE) - return 0; + return em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio); /* * It is assumed that all devices use master volume for output. -- cgit v1.2.3 From 8e04a24ba5ddfb43fa29028ee31bb35c133e665c Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Wed, 26 Jun 2024 10:15:43 -0700 Subject: media: videobuf2: add missing MODULE_DESCRIPTION() macro With ARCH=x86, make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/media/common/videobuf2/videobuf2-dvb.o Add the missing invocation of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Acked-by: Tomasz Figa Signed-off-by: Hans Verkuil [hverkuil: helpers library -> helper library] --- drivers/media/common/videobuf2/videobuf2-dvb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/common/videobuf2/videobuf2-dvb.c b/drivers/media/common/videobuf2/videobuf2-dvb.c index 8c15bcd07eef..3746bf6d1c15 100644 --- a/drivers/media/common/videobuf2/videobuf2-dvb.c +++ b/drivers/media/common/videobuf2/videobuf2-dvb.c @@ -19,6 +19,7 @@ /* ------------------------------------------------------------------ */ MODULE_AUTHOR("Gerd Knorr [SuSE Labs]"); +MODULE_DESCRIPTION("Videobuf2 helper library for simple DVB cards"); MODULE_LICENSE("GPL"); /* ------------------------------------------------------------------ */ -- cgit v1.2.3 From e750a4b1224142bd8dd057b0d5adf8a5608b7e77 Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Wed, 10 Jan 2024 11:42:14 +0530 Subject: media: venus: flush all buffers in output plane streamoff For scenarios, when source change is followed by VIDIOC_STREAMOFF on output plane, driver should discard any queued OUTPUT buffers, which are not decoded or dequeued. Flush with HFI_FLUSH_INPUT does not have any actual impact. So, fix it, by invoking HFI_FLUSH_ALL, which will flush all queued buffers. Fixes: 85872f861d4c ("media: venus: Mark last capture buffer") Signed-off-by: Dikshita Agarwal Tested-by: Nathan Hebert Reviewed-by: Bryan O'Donoghue Signed-off-by: Stanimir Varbanov Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/venus/vdec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 29130a9441e7..0d2ab95bec0f 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -1255,7 +1255,7 @@ static int vdec_stop_output(struct venus_inst *inst) break; case VENUS_DEC_STATE_INIT: case VENUS_DEC_STATE_CAPTURE_SETUP: - ret = hfi_session_flush(inst, HFI_FLUSH_INPUT, true); + ret = hfi_session_flush(inst, HFI_FLUSH_ALL, true); break; default: break; -- cgit v1.2.3 From 02e92ea83b9166b87771c90c76754bd661c5645f Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Wed, 6 Mar 2024 11:23:26 +0530 Subject: media: venus: avoid multiple core dumps Core dump is generated whenever there is system error reported by firmware. Right now, multiple such dumps are generated if recovery fails in first attempt, since the sys error handler is invoked again for every failed recovery. To avoid it, add conditional check to generate core dump only once during every system error notification from firmware. Signed-off-by: Dikshita Agarwal Reviewed-by: Bryan O'Donoghue Signed-off-by: Stanimir Varbanov Signed-off-by: Hans Verkuil [hverkuil: document new dump_core field] --- drivers/media/platform/qcom/venus/core.c | 6 +++++- drivers/media/platform/qcom/venus/core.h | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index ce206b709754..af8890fd6ec2 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -68,6 +68,7 @@ static void venus_event_notify(struct venus_core *core, u32 event) mutex_lock(&core->lock); set_bit(0, &core->sys_error); + set_bit(0, &core->dump_core); list_for_each_entry(inst, &core->instances, list) inst->ops->event_notify(inst, EVT_SESSION_ERROR, NULL); mutex_unlock(&core->lock); @@ -110,7 +111,10 @@ static void venus_sys_error_handler(struct work_struct *work) venus_shutdown(core); - venus_coredump(core); + if (test_bit(0, &core->dump_core)) { + venus_coredump(core); + clear_bit(0, &core->dump_core); + } pm_runtime_put_sync(core->dev); diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 6a77de374454..55202b89e1b9 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -169,6 +169,7 @@ struct venus_format { * @core1_usage_count: usage counter for core1 * @root: debugfs root directory * @venus_ver: the venus firmware version + * @dump_core: a flag indicating that a core dump is required */ struct venus_core { void __iomem *base; @@ -232,6 +233,7 @@ struct venus_core { u32 minor; u32 rev; } venus_ver; + unsigned long dump_core; }; struct vdec_controls { -- cgit v1.2.3 From 599a0bc7f1eddf9311678b8b6c1e0969c868681f Mon Sep 17 00:00:00 2001 From: Marc Gonzalez Date: Tue, 4 Jun 2024 18:42:04 +0200 Subject: dt-bindings: media: add qcom,msm8998-venus msm8998 has the same video encode/decode accelerator as msm8996. Signed-off-by: Marc Gonzalez Reviewed-by: Bryan O'Donoghue Reviewed-by: Krzysztof Kozlowski Signed-off-by: Stanimir Varbanov Signed-off-by: Hans Verkuil --- Documentation/devicetree/bindings/media/qcom,msm8996-venus.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/qcom,msm8996-venus.yaml b/Documentation/devicetree/bindings/media/qcom,msm8996-venus.yaml index 3a4d817e544e..56c16458e3bb 100644 --- a/Documentation/devicetree/bindings/media/qcom,msm8996-venus.yaml +++ b/Documentation/devicetree/bindings/media/qcom,msm8996-venus.yaml @@ -18,7 +18,9 @@ allOf: properties: compatible: - const: qcom,msm8996-venus + enum: + - qcom,msm8996-venus + - qcom,msm8998-venus power-domains: maxItems: 1 -- cgit v1.2.3 From 193b3dac29a441fb72d2bbc3b4c4ebf2961e3c27 Mon Sep 17 00:00:00 2001 From: Pierre-Hugues Husson Date: Tue, 4 Jun 2024 18:43:07 +0200 Subject: media: venus: add msm8998 support Add the missing bits for msm8998 support. Downstream vendor code for reference: https://git.codelinaro.org/clo/la/kernel/msm-4.4/-/blob/caf_migration/ kernel.lnx.4.4.r38-rel/arch/arm/boot/dts/qcom/msm8998-vidc.dtsi#L42-53 qcom,load-freq-tbl = /* Encoders */ <972000 465000000 0x55555555>, /* 4k UHD @ 30 */ <489600 360000000 0x55555555>, /* 1080p @ 60 */ <244800 186000000 0x55555555>, /* 1080p @ 30 */ <108000 100000000 0x55555555>, /* 720p @ 30 */ /* Decoders */ <1944000 465000000 0xffffffff>, /* 4k UHD @ 60 */ < 972000 360000000 0xffffffff>, /* 4k UHD @ 30 */ < 489600 186000000 0xffffffff>, /* 1080p @ 60 */ < 244800 100000000 0xffffffff>; /* 1080p @ 30 */ Signed-off-by: Pierre-Hugues Husson Signed-off-by: Marc Gonzalez Reviewed-by: Bryan O'Donoghue Acked-by: Vikash Garodia Signed-off-by: Stanimir Varbanov Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/venus/core.c | 39 ++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index af8890fd6ec2..165c947a6703 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -591,6 +591,44 @@ static const struct venus_resources msm8996_res = { .fwname = "qcom/venus-4.2/venus.mbn", }; +static const struct freq_tbl msm8998_freq_table[] = { + { 1944000, 465000000 }, /* 4k UHD @ 60 (decode only) */ + { 972000, 465000000 }, /* 4k UHD @ 30 */ + { 489600, 360000000 }, /* 1080p @ 60 */ + { 244800, 186000000 }, /* 1080p @ 30 */ + { 108000, 100000000 }, /* 720p @ 30 */ +}; + +static const struct reg_val msm8998_reg_preset[] = { + { 0x80124, 0x00000003 }, + { 0x80550, 0x01111111 }, + { 0x80560, 0x01111111 }, + { 0x80568, 0x01111111 }, + { 0x80570, 0x01111111 }, + { 0x80580, 0x01111111 }, + { 0x80588, 0x01111111 }, + { 0xe2010, 0x00000000 }, +}; + +static const struct venus_resources msm8998_res = { + .freq_tbl = msm8998_freq_table, + .freq_tbl_size = ARRAY_SIZE(msm8998_freq_table), + .reg_tbl = msm8998_reg_preset, + .reg_tbl_size = ARRAY_SIZE(msm8998_reg_preset), + .clks = { "core", "iface", "bus", "mbus" }, + .clks_num = 4, + .vcodec0_clks = { "core" }, + .vcodec1_clks = { "core" }, + .vcodec_clks_num = 1, + .max_load = 2563200, + .hfi_version = HFI_VERSION_3XX, + .vmem_id = VIDC_RESOURCE_NONE, + .vmem_size = 0, + .vmem_addr = 0, + .dma_mask = 0xddc00000 - 1, + .fwname = "qcom/venus-4.4/venus.mbn", +}; + static const struct freq_tbl sdm660_freq_table[] = { { 979200, 518400000 }, { 489600, 441600000 }, @@ -897,6 +935,7 @@ static const struct venus_resources sc7280_res = { static const struct of_device_id venus_dt_match[] = { { .compatible = "qcom,msm8916-venus", .data = &msm8916_res, }, { .compatible = "qcom,msm8996-venus", .data = &msm8996_res, }, + { .compatible = "qcom,msm8998-venus", .data = &msm8998_res, }, { .compatible = "qcom,sdm660-venus", .data = &sdm660_res, }, { .compatible = "qcom,sdm845-venus", .data = &sdm845_res, }, { .compatible = "qcom,sdm845-venus-v2", .data = &sdm845_res_v2, }, -- cgit v1.2.3 From a0157b5aa34eb43ec4c5510f9c260bbb03be937e Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Thu, 9 May 2024 10:44:29 +0530 Subject: media: venus: fix use after free in vdec_close There appears to be a possible use after free with vdec_close(). The firmware will add buffer release work to the work queue through HFI callbacks as a normal part of decoding. Randomly closing the decoder device from userspace during normal decoding can incur a read after free for inst. Fix it by cancelling the work in vdec_close. Cc: stable@vger.kernel.org Fixes: af2c3834c8ca ("[media] media: venus: adding core part and helper functions") Signed-off-by: Dikshita Agarwal Acked-by: Vikash Garodia Signed-off-by: Stanimir Varbanov Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/venus/vdec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index 0d2ab95bec0f..d12089370d91 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -1747,6 +1747,7 @@ static int vdec_close(struct file *file) vdec_pm_get(inst); + cancel_work_sync(&inst->delayed_process_work); v4l2_m2m_ctx_release(inst->m2m_ctx); v4l2_m2m_release(inst->m2m_dev); vdec_ctrl_deinit(inst); -- cgit v1.2.3 From 9d0643da1162f1ee1f49420789f4203aaaae0f7e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 3 Jun 2024 15:20:53 +0200 Subject: media: atomisp: Remove unused mipicsi_flag module parameter The mipicsi_flag int is completely unused, remove it. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240603132057.255917-1-hdegoede@redhat.com Signed-off-by: Hans Verkuil --- drivers/staging/media/atomisp/pci/atomisp_common.h | 1 - drivers/staging/media/atomisp/pci/atomisp_v4l2.c | 4 ---- 2 files changed, 5 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_common.h b/drivers/staging/media/atomisp/pci/atomisp_common.h index 9d23a6ccfc33..2d0a77df6c88 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_common.h +++ b/drivers/staging/media/atomisp/pci/atomisp_common.h @@ -33,7 +33,6 @@ extern int dbg_level; extern int dbg_func; -extern int mipicsi_flag; extern int pad_w; extern int pad_h; diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c index 9df0eb7044b7..8b522baf2e66 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c +++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c @@ -66,10 +66,6 @@ module_param(dbg_func, int, 0644); MODULE_PARM_DESC(dbg_func, "log function switch non/printk (default:printk)"); -int mipicsi_flag; -module_param(mipicsi_flag, int, 0644); -MODULE_PARM_DESC(mipicsi_flag, "mipi csi compression predictor algorithm"); - static char firmware_name[256]; module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0); MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the default firmware name."); -- cgit v1.2.3 From 19d933cf065229356c40753c836605ce8e1c59dc Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 3 Jun 2024 15:20:54 +0200 Subject: media: atomisp: Remove firmware_name module parameter There is no need for the user to be able to specify a different name for the firmware being loaded. Most other kernel drivers do not have this and work fine without it. Drop this unnecessary module parameter. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240603132057.255917-2-hdegoede@redhat.com Signed-off-by: Hans Verkuil --- drivers/staging/media/atomisp/pci/atomisp_v4l2.c | 34 +++++++++--------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c index 8b522baf2e66..cfdfbf96c3fe 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c +++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c @@ -66,10 +66,6 @@ module_param(dbg_func, int, 0644); MODULE_PARM_DESC(dbg_func, "log function switch non/printk (default:printk)"); -static char firmware_name[256]; -module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0); -MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the default firmware name."); - /* * Set to 16x16 since this is the amount of lines and pixels the sensor * exports extra. If these are kept at the 10x8 that they were on, in yuv @@ -1101,23 +1097,19 @@ atomisp_load_firmware(struct atomisp_device *isp) int rc; char *fw_path = NULL; - if (firmware_name[0] != '\0') { - fw_path = firmware_name; - } else { - if ((isp->media_dev.hw_revision >> ATOMISP_HW_REVISION_SHIFT) - == ATOMISP_HW_REVISION_ISP2401) - fw_path = "shisp_2401a0_v21.bin"; - - if (isp->media_dev.hw_revision == - ((ATOMISP_HW_REVISION_ISP2401_LEGACY << ATOMISP_HW_REVISION_SHIFT) - | ATOMISP_HW_STEPPING_A0)) - fw_path = "shisp_2401a0_legacy_v21.bin"; - - if (isp->media_dev.hw_revision == - ((ATOMISP_HW_REVISION_ISP2400 << ATOMISP_HW_REVISION_SHIFT) - | ATOMISP_HW_STEPPING_B0)) - fw_path = "shisp_2400b0_v21.bin"; - } + if ((isp->media_dev.hw_revision >> ATOMISP_HW_REVISION_SHIFT) == + ATOMISP_HW_REVISION_ISP2401) + fw_path = "shisp_2401a0_v21.bin"; + + if (isp->media_dev.hw_revision == + ((ATOMISP_HW_REVISION_ISP2401_LEGACY << ATOMISP_HW_REVISION_SHIFT) | + ATOMISP_HW_STEPPING_A0)) + fw_path = "shisp_2401a0_legacy_v21.bin"; + + if (isp->media_dev.hw_revision == + ((ATOMISP_HW_REVISION_ISP2400 << ATOMISP_HW_REVISION_SHIFT) | + ATOMISP_HW_STEPPING_B0)) + fw_path = "shisp_2400b0_v21.bin"; if (!fw_path) { dev_err(isp->dev, "Unsupported hw_revision 0x%x\n", -- cgit v1.2.3 From f99d675766bd7ed5e085249a617851548e05c629 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 3 Jun 2024 15:20:55 +0200 Subject: media: atomisp: Prefix firmware paths with "intel/ipu/" The atomisp firmwares have been added to upstream linux-firmware under intel/ipu/ add this prefix to the firmware name passed to request_firmware(). Fall back to the old location if this fails to avoid breaking existing setups. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240603132057.255917-3-hdegoede@redhat.com Signed-off-by: Hans Verkuil --- drivers/staging/media/atomisp/pci/atomisp_v4l2.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c index cfdfbf96c3fe..c9984f1557b0 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c +++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c @@ -1099,17 +1099,17 @@ atomisp_load_firmware(struct atomisp_device *isp) if ((isp->media_dev.hw_revision >> ATOMISP_HW_REVISION_SHIFT) == ATOMISP_HW_REVISION_ISP2401) - fw_path = "shisp_2401a0_v21.bin"; + fw_path = "intel/ipu/shisp_2401a0_v21.bin"; if (isp->media_dev.hw_revision == ((ATOMISP_HW_REVISION_ISP2401_LEGACY << ATOMISP_HW_REVISION_SHIFT) | ATOMISP_HW_STEPPING_A0)) - fw_path = "shisp_2401a0_legacy_v21.bin"; + fw_path = "intel/ipu/shisp_2401a0_legacy_v21.bin"; if (isp->media_dev.hw_revision == ((ATOMISP_HW_REVISION_ISP2400 << ATOMISP_HW_REVISION_SHIFT) | ATOMISP_HW_STEPPING_B0)) - fw_path = "shisp_2400b0_v21.bin"; + fw_path = "intel/ipu/shisp_2400b0_v21.bin"; if (!fw_path) { dev_err(isp->dev, "Unsupported hw_revision 0x%x\n", @@ -1118,6 +1118,9 @@ atomisp_load_firmware(struct atomisp_device *isp) } rc = request_firmware(&fw, fw_path, isp->dev); + /* Fallback to old fw_path without "intel/ipu/" prefix */ + if (rc) + rc = request_firmware(&fw, kbasename(fw_path), isp->dev); if (rc) { dev_err(isp->dev, "atomisp: Error %d while requesting firmware %s\n", -- cgit v1.2.3 From aa7a02089fb505a1658e968fb8ad4be643c5903f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 3 Jun 2024 15:20:56 +0200 Subject: media: atomisp: Update TODO Update the TODO list: - Remove comment about adding firmware to linux-firmware this has been done - Add a comment about removing unnecessary / unwanted module parameters Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240603132057.255917-4-hdegoede@redhat.com Signed-off-by: Hans Verkuil --- drivers/staging/media/atomisp/TODO | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/drivers/staging/media/atomisp/TODO b/drivers/staging/media/atomisp/TODO index bfef99997a1d..27cbbde93b1e 100644 --- a/drivers/staging/media/atomisp/TODO +++ b/drivers/staging/media/atomisp/TODO @@ -1,29 +1,3 @@ -Required firmware -================= - -The atomisp driver requires the following firmware: - -- for BYT: /lib/firmware/shisp_2400b0_v21.bin - - With a version of "irci_stable_candrpv_0415_20150423_1753" to check - the version run: "strings shisp_2400b0_v21.bin | head -n1", sha256sum: - - 3847b95fb9f1f8352c595ba7394d55b33176751372baae17f89aa483ec02a21b shisp_2400b0_v21.bin - - The shisp_2400b0_v21.bin file with this version can be found on - the Android factory images of various X86 Android tablets such as - e.g. the Chuwi Hi8 Pro. - -- for CHT: /lib/firmware/shisp_2401a0_v21.bin - - With a version of "irci_stable_candrpv_0415_20150521_0458", sha256sum: - - e89359f4e4934c410c83d525e283f34c5fcce9cb5caa75ad8a32d66d3842d95c shisp_2401a0_v21.bin - - This can be found here: - https://github.com/intel-aero/meta-intel-aero-base/blob/master/recipes-kernel/linux/linux-yocto/shisp_2401a0_v21.bin - - TODO ==== @@ -35,6 +9,8 @@ TODO * Remove custom sysfs files created by atomisp_drvfs.c +* Remove unnecessary/unwanted module parameters + * Remove abuse of priv field in various v4l2 userspace API structs * Without a 3A library the capture behaviour is not very good. To take a good @@ -61,9 +37,6 @@ TODO * Fix not all v4l2 apps working, e.g. cheese does not work -* Get manufacturer's authorization to redistribute the binaries for - the firmware files - * The atomisp code still has a lot of cruft which needs cleaning up -- cgit v1.2.3 From e323de473d292a5c7f21e6c6c493bc94aab62739 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 3 Jun 2024 15:20:57 +0200 Subject: media: atomisp: csi2-bridge: Add DMI quirk for OV5693 on Xiaomi Mipad2 The OV5693 the Xiaomi Mipad2 is used as a front facing sensor and as such is connected to CsiPort 0, but the _DSM has CsiPort 1, add a DMI quirk to override the wrong _DSM setting. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240603132057.255917-5-hdegoede@redhat.com Signed-off-by: Hans Verkuil --- drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c index 2483eaeeac73..d789d38ef689 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c +++ b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c @@ -106,6 +106,12 @@ static struct gmin_cfg_var lenovo_ideapad_miix_310_vars[] = { {} }; +static struct gmin_cfg_var xiaomi_mipad2_vars[] = { + /* _DSM contains the wrong CsiPort for the front facing OV5693 sensor */ + { "INT33BE:00", "CsiPort", "0" }, + {} +}; + static const struct dmi_system_id gmin_cfg_dmi_overrides[] = { { /* Lenovo Ideapad Miix 310 */ @@ -115,6 +121,14 @@ static const struct dmi_system_id gmin_cfg_dmi_overrides[] = { }, .driver_data = lenovo_ideapad_miix_310_vars, }, + { + /* Xiaomi Mipad2 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Xiaomi Inc"), + DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"), + }, + .driver_data = xiaomi_mipad2_vars, + }, {} }; -- cgit v1.2.3 From 143fd8feb8eaf3f14eb42d330fb46152ff291693 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 24 Apr 2024 21:43:31 +0300 Subject: media: atomisp: Clean up unused macros from math_support.h Clean up unused macros from math_support.h and replace rarely used by generic ones from Linux kernel headers. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240424184421.1737776-2-andriy.shevchenko@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- .../pci/camera/util/interface/ia_css_util.h | 11 --- .../media/atomisp/pci/camera/util/src/util.c | 25 +++-- .../pci/hive_isp_css_include/math_support.h | 110 +-------------------- drivers/staging/media/atomisp/pci/ia_css_3a.h | 1 + .../pci/isp/kernels/xnr/xnr_3.0/ia_css_xnr3.host.c | 6 +- .../media/atomisp/pci/runtime/binary/src/binary.c | 2 - drivers/staging/media/atomisp/pci/sh_css_frac.h | 4 +- 7 files changed, 20 insertions(+), 139 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/camera/util/interface/ia_css_util.h b/drivers/staging/media/atomisp/pci/camera/util/interface/ia_css_util.h index 59df44d696a0..d4de1e9293a1 100644 --- a/drivers/staging/media/atomisp/pci/camera/util/interface/ia_css_util.h +++ b/drivers/staging/media/atomisp/pci/camera/util/interface/ia_css_util.h @@ -100,17 +100,6 @@ bool ia_css_util_res_leq( bool ia_css_util_resolution_is_zero( const struct ia_css_resolution resolution); -/* ISP2401 */ -/** - * @brief Check if resolution is even - * - * @param[in] resolution The resolution to check - * - * @returns true if resolution is even - */ -bool ia_css_util_resolution_is_even( - const struct ia_css_resolution resolution); - /* @brief check width and height * * @param[in] stream_format diff --git a/drivers/staging/media/atomisp/pci/camera/util/src/util.c b/drivers/staging/media/atomisp/pci/camera/util/src/util.c index 40a71e37cc4e..9d7025a00beb 100644 --- a/drivers/staging/media/atomisp/pci/camera/util/src/util.c +++ b/drivers/staging/media/atomisp/pci/camera/util/src/util.c @@ -119,17 +119,6 @@ int ia_css_util_check_vf_out_info( return 0; } -int ia_css_util_check_res(unsigned int width, unsigned int height) -{ - /* height can be odd number for jpeg/embedded data from ISYS2401 */ - if (((width == 0) || - (height == 0) || - IS_ODD(width))) { - return -EINVAL; - } - return 0; -} - /* ISP2401 */ bool ia_css_util_res_leq(struct ia_css_resolution a, struct ia_css_resolution b) { @@ -142,10 +131,18 @@ bool ia_css_util_resolution_is_zero(const struct ia_css_resolution resolution) return (resolution.width == 0) || (resolution.height == 0); } -/* ISP2401 */ -bool ia_css_util_resolution_is_even(const struct ia_css_resolution resolution) +int ia_css_util_check_res(unsigned int width, unsigned int height) { - return IS_EVEN(resolution.height) && IS_EVEN(resolution.width); + const struct ia_css_resolution resolution = { .width = width, .height = height }; + + if (ia_css_util_resolution_is_zero(resolution)) + return -EINVAL; + + /* height can be odd number for jpeg/embedded data from ISYS2401 */ + if (width & 1) + return -EINVAL; + + return 0; } bool ia_css_util_is_input_format_raw(enum atomisp_input_format format) diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h index a444ec14ff9d..7349943bba2b 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h @@ -16,133 +16,27 @@ #ifndef __MATH_SUPPORT_H #define __MATH_SUPPORT_H -#include /* Override the definition of max/min from linux kernel*/ - -#define IS_ODD(a) ((a) & 0x1) -#define IS_EVEN(a) (!IS_ODD(a)) +/* Override the definition of max/min from Linux kernel */ +#include /* force a value to a lower even value */ #define EVEN_FLOOR(x) ((x) & ~1) -/* ISP2401 */ -/* If the number is odd, find the next even number */ -#define EVEN_CEIL(x) ((IS_ODD(x)) ? ((x) + 1) : (x)) - -/* A => B */ -#define IMPLIES(a, b) (!(a) || (b)) - /* for preprocessor and array sizing use MIN and MAX otherwise use min and max */ #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define ROUND_DIV(a, b) (((b) != 0) ? ((a) + ((b) >> 1)) / (b) : 0) #define CEIL_DIV(a, b) (((b) != 0) ? ((a) + (b) - 1) / (b) : 0) #define CEIL_MUL(a, b) (CEIL_DIV(a, b) * (b)) #define CEIL_MUL2(a, b) (((a) + (b) - 1) & ~((b) - 1)) #define CEIL_SHIFT(a, b) (((a) + (1 << (b)) - 1) >> (b)) #define CEIL_SHIFT_MUL(a, b) (CEIL_SHIFT(a, b) << (b)) -#define ROUND_HALF_DOWN_DIV(a, b) (((b) != 0) ? ((a) + (b / 2) - 1) / (b) : 0) -#define ROUND_HALF_DOWN_MUL(a, b) (ROUND_HALF_DOWN_DIV(a, b) * (b)) - -/*To Find next power of 2 number from x */ -#define bit2(x) ((x) | ((x) >> 1)) -#define bit4(x) (bit2(x) | (bit2(x) >> 2)) -#define bit8(x) (bit4(x) | (bit4(x) >> 4)) -#define bit16(x) (bit8(x) | (bit8(x) >> 8)) -#define bit32(x) (bit16(x) | (bit16(x) >> 16)) -#define NEXT_POWER_OF_2(x) (bit32(x - 1) + 1) - -/* min and max should not be macros as they will evaluate their arguments twice. - if you really need a macro (e.g. for CPP or for initializing an array) - use MIN() and MAX(), otherwise use min() and max(). - -*/ #if !defined(PIPE_GENERATION) -/* -This macro versions are added back as we are mixing types in usage of inline. -This causes corner cases of calculations to be incorrect due to conversions -between signed and unsigned variables or overflows. -Before the addition of the inline functions, max, min and ceil_div were macros -and therefore adding them back. - -Leaving out the other math utility functions as they are newly added -*/ - #define ceil_div(a, b) (CEIL_DIV(a, b)) -static inline unsigned int ceil_mul(unsigned int a, unsigned int b) -{ - return CEIL_MUL(a, b); -} - -static inline unsigned int ceil_mul2(unsigned int a, unsigned int b) -{ - return CEIL_MUL2(a, b); -} - -static inline unsigned int ceil_shift(unsigned int a, unsigned int b) -{ - return CEIL_SHIFT(a, b); -} - -static inline unsigned int ceil_shift_mul(unsigned int a, unsigned int b) -{ - return CEIL_SHIFT_MUL(a, b); -} - -/* ISP2401 */ -static inline unsigned int round_half_down_div(unsigned int a, unsigned int b) -{ - return ROUND_HALF_DOWN_DIV(a, b); -} - -/* ISP2401 */ -static inline unsigned int round_half_down_mul(unsigned int a, unsigned int b) -{ - return ROUND_HALF_DOWN_MUL(a, b); -} - -/* @brief Next Power of Two - * - * @param[in] unsigned number - * - * @return next power of two - * - * This function rounds input to the nearest power of 2 (2^x) - * towards infinity - * - * Input Range: 0 .. 2^(8*sizeof(int)-1) - * - * IF input is a power of 2 - * out = in - * OTHERWISE - * out = 2^(ceil(log2(in)) - * - */ - -static inline unsigned int ceil_pow2(unsigned int a) -{ - if (a == 0) { - return 1; - } - /* IF input is already a power of two*/ - else if ((!((a) & ((a) - 1)))) { - return a; - } else { - unsigned int v = a; - - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - return (v + 1); - } -} - #endif /* !defined(PIPE_GENERATION) */ /* diff --git a/drivers/staging/media/atomisp/pci/ia_css_3a.h b/drivers/staging/media/atomisp/pci/ia_css_3a.h index 70cfc915cc56..506910dd5c18 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_3a.h +++ b/drivers/staging/media/atomisp/pci/ia_css_3a.h @@ -20,6 +20,7 @@ * This file contains types used for 3A statistics */ +#include #include #include "ia_css_types.h" #include "ia_css_err.h" diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_3.0/ia_css_xnr3.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_3.0/ia_css_xnr3.host.c index 9c9d9b9a453e..70132d955e9b 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_3.0/ia_css_xnr3.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_3.0/ia_css_xnr3.host.c @@ -13,6 +13,8 @@ * more details. */ +#include + #include "type_support.h" #include "math_support.h" #include "sh_css_defs.h" @@ -137,9 +139,7 @@ ia_css_xnr3_encode( unsigned int size) { int kernel_size = XNR_FILTER_SIZE; - /* The adjust factor is the next power of 2 - w.r.t. the kernel size*/ - int adjust_factor = ceil_pow2(kernel_size); + int adjust_factor = roundup_pow_of_two(kernel_size); s32 max_diff = (1 << (ISP_VEC_ELEMBITS - 1)) - 1; s32 min_diff = -(1 << (ISP_VEC_ELEMBITS - 1)); diff --git a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c index 130662f8e768..b0f904a5e442 100644 --- a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c +++ b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c @@ -43,8 +43,6 @@ #include "assert_support.h" -#define IMPLIES(a, b) (!(a) || (b)) /* A => B */ - static struct ia_css_binary_xinfo *all_binaries; /* ISP binaries only (no SP) */ static struct ia_css_binary_xinfo *binary_infos[IA_CSS_BINARY_NUM_MODES] = { NULL, }; diff --git a/drivers/staging/media/atomisp/pci/sh_css_frac.h b/drivers/staging/media/atomisp/pci/sh_css_frac.h index 8f08df5c88cc..b90b5b330dfa 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_frac.h +++ b/drivers/staging/media/atomisp/pci/sh_css_frac.h @@ -16,7 +16,9 @@ #ifndef __SH_CSS_FRAC_H #define __SH_CSS_FRAC_H -#include +#include + +#include "mamoiada_params.h" #define sISP_REG_BIT ISP_VEC_ELEMBITS #define uISP_REG_BIT ((unsigned int)(sISP_REG_BIT - 1)) -- cgit v1.2.3 From 92a643eec731771d85a33280280d249dea3bcda7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 24 Apr 2024 21:43:32 +0300 Subject: media: atomisp: Replace COMPILATION_ERROR_IF() by static_assert() Replace COMPILATION_ERROR_IF() by static_assert(). Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240424184421.1737776-3-andriy.shevchenko@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- .../base/circbuf/interface/ia_css_circbuf_comm.h | 6 ++++ .../pci/hive_isp_css_include/assert_support.h | 23 ------------- .../pci/hive_isp_css_include/type_support.h | 5 +-- drivers/staging/media/atomisp/pci/ia_css_3a.h | 4 +++ drivers/staging/media/atomisp/pci/ia_css_dvs.h | 4 +++ .../staging/media/atomisp/pci/ia_css_metadata.h | 4 +++ drivers/staging/media/atomisp/pci/ia_css_types.h | 2 ++ .../runtime/spctrl/interface/ia_css_spctrl_comm.h | 4 +++ drivers/staging/media/atomisp/pci/sh_css.c | 38 ---------------------- .../staging/media/atomisp/pci/sh_css_internal.h | 15 +++++++-- 10 files changed, 40 insertions(+), 65 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_comm.h b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_comm.h index 6fa6da859158..b0f20563c3a3 100644 --- a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_comm.h +++ b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_comm.h @@ -16,6 +16,8 @@ #ifndef _IA_CSS_CIRCBUF_COMM_H #define _IA_CSS_CIRCBUF_COMM_H +#include + #include /* uint8_t, uint32_t */ #define IA_CSS_CIRCBUF_PADDING 1 /* The circular buffer is implemented in lock-less manner, wherein @@ -45,6 +47,8 @@ struct ia_css_circbuf_desc_s { #define SIZE_OF_IA_CSS_CIRCBUF_DESC_S_STRUCT \ (4 * sizeof(uint8_t)) +static_assert(sizeof(struct ia_css_circbuf_desc_s) == SIZE_OF_IA_CSS_CIRCBUF_DESC_S_STRUCT); + /** * @brief Data structure for the circular buffer element. */ @@ -56,4 +60,6 @@ struct ia_css_circbuf_elem_s { #define SIZE_OF_IA_CSS_CIRCBUF_ELEM_S_STRUCT \ (sizeof(uint32_t)) +static_assert(sizeof(struct ia_css_circbuf_elem_s) == SIZE_OF_IA_CSS_CIRCBUF_ELEM_S_STRUCT); + #endif /*_IA_CSS_CIRCBUF_COMM_H*/ diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/assert_support.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/assert_support.h index 7382c0bbf7cb..d294ac402de8 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/assert_support.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/assert_support.h @@ -16,29 +16,6 @@ #ifndef __ASSERT_SUPPORT_H_INCLUDED__ #define __ASSERT_SUPPORT_H_INCLUDED__ -/** - * The following macro can help to test the size of a struct at compile - * time rather than at run-time. It does not work for all compilers; see - * below. - * - * Depending on the value of 'condition', the following macro is expanded to: - * - condition==true: - * an expression containing an array declaration with negative size, - * usually resulting in a compilation error - * - condition==false: - * (void) 1; // C statement with no effect - * - * example: - * COMPILATION_ERROR_IF( sizeof(struct host_sp_queues) != SIZE_OF_HOST_SP_QUEUES_STRUCT); - * - * verify that the macro indeed triggers a compilation error with your compiler: - * COMPILATION_ERROR_IF( sizeof(struct host_sp_queues) != (sizeof(struct host_sp_queues)+1) ); - * - * Not all compilers will trigger an error with this macro; use a search engine to search for - * BUILD_BUG_ON to find other methods. - */ -#define COMPILATION_ERROR_IF(condition) ((void)sizeof(char[1 - 2 * !!(condition)])) - /* Compile time assertion */ #ifndef CT_ASSERT #define CT_ASSERT(cnd) ((void)sizeof(char[(cnd) ? 1 : -1])) diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/type_support.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/type_support.h index b996ee54d4a5..9a640f18eed9 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/type_support.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/type_support.h @@ -33,9 +33,10 @@ #define CHAR_BIT (8) -#include -#include #include +#include +#include + #define HOST_ADDRESS(x) (unsigned long)(x) #endif /* __TYPE_SUPPORT_H_INCLUDED__ */ diff --git a/drivers/staging/media/atomisp/pci/ia_css_3a.h b/drivers/staging/media/atomisp/pci/ia_css_3a.h index 506910dd5c18..fc2075c7bd01 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_3a.h +++ b/drivers/staging/media/atomisp/pci/ia_css_3a.h @@ -20,6 +20,8 @@ * This file contains types used for 3A statistics */ +#include + #include #include #include "ia_css_types.h" @@ -80,6 +82,8 @@ struct ia_css_isp_3a_statistics { SIZE_OF_IA_CSS_PTR + \ 4 * sizeof(uint32_t)) +static_assert(sizeof(struct ia_css_isp_3a_statistics) == SIZE_OF_IA_CSS_ISP_3A_STATISTICS_STRUCT); + /* Map with host-side pointers to ISP-format statistics. * These pointers can either be copies of ISP data or memory mapped * ISP pointers. diff --git a/drivers/staging/media/atomisp/pci/ia_css_dvs.h b/drivers/staging/media/atomisp/pci/ia_css_dvs.h index 3367dfd64050..41a81561bbef 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_dvs.h +++ b/drivers/staging/media/atomisp/pci/ia_css_dvs.h @@ -20,6 +20,8 @@ * This file contains types for DVS statistics */ +#include + #include #include "ia_css_types.h" #include "ia_css_err.h" @@ -55,6 +57,8 @@ struct ia_css_isp_skc_dvs_statistics; ((3 * SIZE_OF_IA_CSS_PTR) + \ (4 * sizeof(uint32_t))) +static_assert(sizeof(struct ia_css_isp_dvs_statistics) == SIZE_OF_IA_CSS_ISP_DVS_STATISTICS_STRUCT); + /* Map with host-side pointers to ISP-format statistics. * These pointers can either be copies of ISP data or memory mapped * ISP pointers. diff --git a/drivers/staging/media/atomisp/pci/ia_css_metadata.h b/drivers/staging/media/atomisp/pci/ia_css_metadata.h index 9eb1b76a3b2a..a3e759a3eee7 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_metadata.h +++ b/drivers/staging/media/atomisp/pci/ia_css_metadata.h @@ -20,6 +20,8 @@ * This file contains structure for processing sensor metadata. */ +#include + #include #include "ia_css_types.h" #include "ia_css_stream_format.h" @@ -50,6 +52,8 @@ struct ia_css_metadata { #define SIZE_OF_IA_CSS_METADATA_STRUCT sizeof(struct ia_css_metadata) +static_assert(sizeof(struct ia_css_metadata) == SIZE_OF_IA_CSS_METADATA_STRUCT); + /* @brief Allocate a metadata buffer. * @param[in] metadata_info Metadata info struct, contains details on metadata buffers. * @return Pointer of metadata buffer or NULL (if error) diff --git a/drivers/staging/media/atomisp/pci/ia_css_types.h b/drivers/staging/media/atomisp/pci/ia_css_types.h index 6e34d401f9df..f5df564c86e8 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_types.h +++ b/drivers/staging/media/atomisp/pci/ia_css_types.h @@ -84,6 +84,8 @@ struct ia_css_state_memory_offsets; /* Virtual address within the CSS address space. */ typedef u32 ia_css_ptr; +#define SIZE_OF_IA_CSS_PTR sizeof(uint32_t) + /* Generic resolution structure. */ struct ia_css_resolution { diff --git a/drivers/staging/media/atomisp/pci/runtime/spctrl/interface/ia_css_spctrl_comm.h b/drivers/staging/media/atomisp/pci/runtime/spctrl/interface/ia_css_spctrl_comm.h index 78e0f3096f60..de68616482f0 100644 --- a/drivers/staging/media/atomisp/pci/runtime/spctrl/interface/ia_css_spctrl_comm.h +++ b/drivers/staging/media/atomisp/pci/runtime/spctrl/interface/ia_css_spctrl_comm.h @@ -16,6 +16,8 @@ #ifndef __IA_CSS_SPCTRL_COMM_H__ #define __IA_CSS_SPCTRL_COMM_H__ +#include + #include /* state of SP */ @@ -43,4 +45,6 @@ struct ia_css_sp_init_dmem_cfg { (4 * sizeof(uint32_t)) + \ (1 * sizeof(sp_ID_t)) +static_assert(sizeof(struct ia_css_sp_init_dmem_cfg) == SIZE_OF_IA_CSS_SP_INIT_DMEM_CFG_STRUCT); + #endif /* __IA_CSS_SPCTRL_COMM_H__ */ diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index 42a69b26db01..e2497fc4dfc9 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -1345,47 +1345,9 @@ ia_css_init(struct device *dev, const struct ia_css_env *env, { int err; ia_css_spctrl_cfg spctrl_cfg; - void (*flush_func)(struct ia_css_acc_fw *fw); hrt_data select, enable; - /* - * The C99 standard does not specify the exact object representation of structs; - * the representation is compiler dependent. - * - * The structs that are communicated between host and SP/ISP should have the - * exact same object representation. The compiler that is used to compile the - * firmware is hivecc. - * - * To check if a different compiler, used to compile a host application, uses - * another object representation, macros are defined specifying the size of - * the structs as expected by the firmware. - * - * A host application shall verify that a sizeof( ) of the struct is equal to - * the SIZE_OF_XXX macro of the corresponding struct. If they are not - * equal, functionality will break. - */ - - /* Check struct sh_css_ddr_address_map */ - COMPILATION_ERROR_IF(sizeof(struct sh_css_ddr_address_map) != SIZE_OF_SH_CSS_DDR_ADDRESS_MAP_STRUCT); - /* Check struct host_sp_queues */ - COMPILATION_ERROR_IF(sizeof(struct host_sp_queues) != SIZE_OF_HOST_SP_QUEUES_STRUCT); - COMPILATION_ERROR_IF(sizeof(struct ia_css_circbuf_desc_s) != SIZE_OF_IA_CSS_CIRCBUF_DESC_S_STRUCT); - COMPILATION_ERROR_IF(sizeof(struct ia_css_circbuf_elem_s) != SIZE_OF_IA_CSS_CIRCBUF_ELEM_S_STRUCT); - - /* Check struct host_sp_communication */ - COMPILATION_ERROR_IF(sizeof(struct host_sp_communication) != SIZE_OF_HOST_SP_COMMUNICATION_STRUCT); - COMPILATION_ERROR_IF(sizeof(struct sh_css_event_irq_mask) != SIZE_OF_SH_CSS_EVENT_IRQ_MASK_STRUCT); - - /* Check struct sh_css_hmm_buffer */ - COMPILATION_ERROR_IF(sizeof(struct sh_css_hmm_buffer) != SIZE_OF_SH_CSS_HMM_BUFFER_STRUCT); - COMPILATION_ERROR_IF(sizeof(struct ia_css_isp_3a_statistics) != SIZE_OF_IA_CSS_ISP_3A_STATISTICS_STRUCT); - COMPILATION_ERROR_IF(sizeof(struct ia_css_isp_dvs_statistics) != SIZE_OF_IA_CSS_ISP_DVS_STATISTICS_STRUCT); - COMPILATION_ERROR_IF(sizeof(struct ia_css_metadata) != SIZE_OF_IA_CSS_METADATA_STRUCT); - - /* Check struct ia_css_init_dmem_cfg */ - COMPILATION_ERROR_IF(sizeof(struct ia_css_sp_init_dmem_cfg) != SIZE_OF_IA_CSS_SP_INIT_DMEM_CFG_STRUCT); - if (!env) return -EINVAL; diff --git a/drivers/staging/media/atomisp/pci/sh_css_internal.h b/drivers/staging/media/atomisp/pci/sh_css_internal.h index bef2b8c5132b..1501046cebb9 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_internal.h +++ b/drivers/staging/media/atomisp/pci/sh_css_internal.h @@ -16,11 +16,13 @@ #ifndef _SH_CSS_INTERNAL_H_ #define _SH_CSS_INTERNAL_H_ +#include +#include + #include #include #include #include -#include #include "input_formatter.h" #include "input_system.h" @@ -104,7 +106,6 @@ */ #define CALC_ALIGNMENT_MEMBER(x, y) (CEIL_MUL(x, y) - x) #define SIZE_OF_HRT_VADDRESS sizeof(hive_uint32) -#define SIZE_OF_IA_CSS_PTR sizeof(uint32_t) /* Number of SP's */ #define NUM_OF_SPS 1 @@ -202,6 +203,8 @@ struct sh_css_ddr_address_map { (SH_CSS_MAX_STAGES * IA_CSS_NUM_MEMORIES * SIZE_OF_HRT_VADDRESS) + \ (16 * SIZE_OF_HRT_VADDRESS)) +static_assert(sizeof(struct sh_css_ddr_address_map) == SIZE_OF_SH_CSS_DDR_ADDRESS_MAP_STRUCT); + /* xmem address map allocation per pipeline */ struct sh_css_ddr_address_map_size { size_t isp_param; @@ -705,6 +708,8 @@ struct sh_css_hmm_buffer { SIZE_OF_IA_CSS_CLOCK_TICK_STRUCT + \ CALC_ALIGNMENT_MEMBER(SIZE_OF_IA_CSS_CLOCK_TICK_STRUCT, 8)) +static_assert(sizeof(struct sh_css_hmm_buffer) == SIZE_OF_SH_CSS_HMM_BUFFER_STRUCT); + enum sh_css_queue_type { sh_css_invalid_queue_type = -1, sh_css_host2sp_buffer_queue, @@ -724,6 +729,8 @@ struct sh_css_event_irq_mask { #define SIZE_OF_SH_CSS_EVENT_IRQ_MASK_STRUCT \ (2 * sizeof(uint16_t)) +static_assert(sizeof(struct sh_css_event_irq_mask) == SIZE_OF_SH_CSS_EVENT_IRQ_MASK_STRUCT); + struct host_sp_communication { /* * Don't use enum host2sp_commands, because the sizeof an enum is @@ -761,6 +768,8 @@ struct host_sp_communication { ((3 + N_CSI_PORTS) * sizeof(uint32_t)) + \ (NR_OF_PIPELINES * SIZE_OF_SH_CSS_EVENT_IRQ_MASK_STRUCT)) +static_assert(sizeof(struct host_sp_communication) == SIZE_OF_HOST_SP_COMMUNICATION_STRUCT); + struct host_sp_queues { /* * Queues for the dynamic frame information, @@ -831,6 +840,8 @@ struct host_sp_queues { #define SIZE_OF_HOST_SP_QUEUES_STRUCT \ (SIZE_OF_QUEUES_ELEMS + SIZE_OF_QUEUES_DESC) +static_assert(sizeof(struct host_sp_queues) == SIZE_OF_HOST_SP_QUEUES_STRUCT); + extern int __printf(1, 0) (*sh_css_printf)(const char *fmt, va_list args); static inline void __printf(1, 2) sh_css_print(const char *fmt, ...) -- cgit v1.2.3 From a7547337b878c310e137ad366b28d70a243b7140 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 25 Apr 2024 00:08:00 +0300 Subject: media: atomisp: Remove unused GPIO related defines and APIs Remove unused GPIO related defines and APIs. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20240424210800.1776038-1-andriy.shevchenko@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- .../staging/media/atomisp/pci/gpio_block_defs.h | 17 -------- .../atomisp/pci/hive_isp_css_common/gpio_global.h | 23 ----------- .../pci/hive_isp_css_common/host/gpio_local.h | 21 ---------- .../pci/hive_isp_css_common/host/gpio_private.h | 9 ++--- .../media/atomisp/pci/hive_isp_css_include/gpio.h | 46 ---------------------- .../pci/hive_isp_css_include/host/gpio_public.h | 46 ---------------------- drivers/staging/media/atomisp/pci/sh_css.c | 10 ++--- 7 files changed, 7 insertions(+), 165 deletions(-) delete mode 100644 drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gpio_local.h delete mode 100644 drivers/staging/media/atomisp/pci/hive_isp_css_include/gpio.h delete mode 100644 drivers/staging/media/atomisp/pci/hive_isp_css_include/host/gpio_public.h diff --git a/drivers/staging/media/atomisp/pci/gpio_block_defs.h b/drivers/staging/media/atomisp/pci/gpio_block_defs.h index e1bd638d344a..55c39067a9bf 100644 --- a/drivers/staging/media/atomisp/pci/gpio_block_defs.h +++ b/drivers/staging/media/atomisp/pci/gpio_block_defs.h @@ -16,27 +16,10 @@ #ifndef _gpio_block_defs_h_ #define _gpio_block_defs_h_ -#define _HRT_GPIO_BLOCK_REG_ALIGN 4 - /* R/W registers */ #define _gpio_block_reg_do_e 0 #define _gpio_block_reg_do_select 1 #define _gpio_block_reg_do_0 2 #define _gpio_block_reg_do_1 3 -#define _gpio_block_reg_do_pwm_cnt_0 4 -#define _gpio_block_reg_do_pwm_cnt_1 5 -#define _gpio_block_reg_do_pwm_cnt_2 6 -#define _gpio_block_reg_do_pwm_cnt_3 7 -#define _gpio_block_reg_do_pwm_main_cnt 8 -#define _gpio_block_reg_do_pwm_enable 9 -#define _gpio_block_reg_di_debounce_sel 10 -#define _gpio_block_reg_di_debounce_cnt_0 11 -#define _gpio_block_reg_di_debounce_cnt_1 12 -#define _gpio_block_reg_di_debounce_cnt_2 13 -#define _gpio_block_reg_di_debounce_cnt_3 14 -#define _gpio_block_reg_di_active_level 15 - -/* read-only registers */ -#define _gpio_block_reg_di 16 #endif /* _gpio_block_defs_h_ */ diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/gpio_global.h b/drivers/staging/media/atomisp/pci/hive_isp_css_common/gpio_global.h index b5f017482f89..06b6cb3842f4 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/gpio_global.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/gpio_global.h @@ -16,31 +16,8 @@ #ifndef __GPIO_GLOBAL_H_INCLUDED__ #define __GPIO_GLOBAL_H_INCLUDED__ -#define IS_GPIO_VERSION_1 - #include -/* pqiao: following part only defines in hive_isp_css_defs.h in fpga system. - port it here -*/ - -/* GPIO pin defines */ -/*#define HIVE_GPIO_CAMERA_BOARD_RESET_PIN_NR 0 -#define HIVE_GPIO_LCD_CLOCK_SELECT_PIN_NR 7 -#define HIVE_GPIO_HDMI_CLOCK_SELECT_PIN_NR 8 -#define HIVE_GPIO_LCD_VERT_FLIP_PIN_NR 8 -#define HIVE_GPIO_LCD_HOR_FLIP_PIN_NR 9 -#define HIVE_GPIO_AS3683_GPIO_P0_PIN_NR 1 -#define HIVE_GPIO_AS3683_DATA_P1_PIN_NR 2 -#define HIVE_GPIO_AS3683_CLK_P2_PIN_NR 3 -#define HIVE_GPIO_AS3683_T1_F0_PIN_NR 4 -#define HIVE_GPIO_AS3683_SFL_F1_PIN_NR 5 -#define HIVE_GPIO_AS3683_STROBE_F2_PIN_NR 6 -#define HIVE_GPIO_MAX1577_EN1_PIN_NR 1 -#define HIVE_GPIO_MAX1577_EN2_PIN_NR 2 -#define HIVE_GPIO_MAX8685A_EN_PIN_NR 3 -#define HIVE_GPIO_MAX8685A_TRIG_PIN_NR 4*/ - #define HIVE_GPIO_STROBE_TRIGGER_PIN 2 #endif /* __GPIO_GLOBAL_H_INCLUDED__ */ diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gpio_local.h b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gpio_local.h deleted file mode 100644 index 14013733f826..000000000000 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gpio_local.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Support for Intel Camera Imaging ISP subsystem. - * Copyright (c) 2010-2015, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ - -#ifndef __GPIO_LOCAL_H_INCLUDED__ -#define __GPIO_LOCAL_H_INCLUDED__ - -#include "gpio_global.h" - -#endif /* __GPIO_LOCAL_H_INCLUDED__ */ diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gpio_private.h b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gpio_private.h index cc60bed71ddb..85fcde0b8615 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gpio_private.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gpio_private.h @@ -16,13 +16,10 @@ #ifndef __GPIO_PRIVATE_H_INCLUDED__ #define __GPIO_PRIVATE_H_INCLUDED__ -#include "gpio_public.h" - -#include "device_access.h" - #include "assert_support.h" +#include "device_access.h" -STORAGE_CLASS_GPIO_C void gpio_reg_store( +static inline void gpio_reg_store( const gpio_ID_t ID, const unsigned int reg, const hrt_data value) @@ -33,7 +30,7 @@ STORAGE_CLASS_GPIO_C void gpio_reg_store( return; } -STORAGE_CLASS_GPIO_C hrt_data gpio_reg_load( +static inline hrt_data gpio_reg_load( const gpio_ID_t ID, const unsigned int reg) { diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/gpio.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/gpio.h deleted file mode 100644 index 6f16ca77cf75..000000000000 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/gpio.h +++ /dev/null @@ -1,46 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Support for Intel Camera Imaging ISP subsystem. - * Copyright (c) 2015, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ - -#ifndef __GPIO_H_INCLUDED__ -#define __GPIO_H_INCLUDED__ - -/* - * This file is included on every cell {SP,ISP,host} and on every system - * that uses the input system device(s). It defines the API to DLI bridge - * - * System and cell specific interfaces and inline code are included - * conditionally through Makefile path settings. - * - * - . system and cell agnostic interfaces, constants and identifiers - * - public: system agnostic, cell specific interfaces - * - private: system dependent, cell specific interfaces & inline implementations - * - global: system specific constants and identifiers - * - local: system and cell specific constants and identifiers - */ - -#include "system_local.h" -#include "gpio_local.h" - -#ifndef __INLINE_GPIO__ -#define STORAGE_CLASS_GPIO_H extern -#define STORAGE_CLASS_GPIO_C -#include "gpio_public.h" -#else /* __INLINE_GPIO__ */ -#define STORAGE_CLASS_GPIO_H static inline -#define STORAGE_CLASS_GPIO_C static inline -#include "gpio_private.h" -#endif /* __INLINE_GPIO__ */ - -#endif /* __GPIO_H_INCLUDED__ */ diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/gpio_public.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/gpio_public.h deleted file mode 100644 index 13df9b57a5fb..000000000000 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/gpio_public.h +++ /dev/null @@ -1,46 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Support for Intel Camera Imaging ISP subsystem. - * Copyright (c) 2015, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ - -#ifndef __GPIO_PUBLIC_H_INCLUDED__ -#define __GPIO_PUBLIC_H_INCLUDED__ - -#include "system_local.h" - -/*! Write to a control register of GPIO[ID] - - \param ID[in] GPIO identifier - \param reg_addr[in] register byte address - \param value[in] The data to be written - - \return none, GPIO[ID].ctrl[reg] = value - */ -STORAGE_CLASS_GPIO_H void gpio_reg_store( - const gpio_ID_t ID, - const unsigned int reg_addr, - const hrt_data value); - -/*! Read from a control register of GPIO[ID] - - \param ID[in] GPIO identifier - \param reg_addr[in] register byte address - \param value[in] The data to be written - - \return GPIO[ID].ctrl[reg] - */ -STORAGE_CLASS_GPIO_H hrt_data gpio_reg_load( - const gpio_ID_t ID, - const unsigned int reg_addr); - -#endif /* __GPIO_PUBLIC_H_INCLUDED__ */ diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c index e2497fc4dfc9..01f0b8a33c99 100644 --- a/drivers/staging/media/atomisp/pci/sh_css.c +++ b/drivers/staging/media/atomisp/pci/sh_css.c @@ -66,8 +66,8 @@ #include "sp.h" /* cnd_sp_irq_enable() */ #include "isp.h" /* cnd_isp_irq_enable, ISP_VEC_NELEMS */ #include "gp_device.h" /* gp_device_reg_store() */ -#define __INLINE_GPIO__ -#include "gpio.h" +#include +#include #include "timed_ctrl.h" #include "ia_css_inputfifo.h" #define WITH_PC_MONITORING 0 @@ -1363,10 +1363,8 @@ ia_css_init(struct device *dev, const struct ia_css_env *env, ia_css_device_access_init(&env->hw_access_env); - select = gpio_reg_load(GPIO0_ID, _gpio_block_reg_do_select) - & (~GPIO_FLASH_PIN_MASK); - enable = gpio_reg_load(GPIO0_ID, _gpio_block_reg_do_e) - | GPIO_FLASH_PIN_MASK; + select = gpio_reg_load(GPIO0_ID, _gpio_block_reg_do_select) & ~GPIO_FLASH_PIN_MASK; + enable = gpio_reg_load(GPIO0_ID, _gpio_block_reg_do_e) | GPIO_FLASH_PIN_MASK; sh_css_mmu_set_page_table_base_index(mmu_l1_base); my_css_save.mmu_base = mmu_l1_base; -- cgit v1.2.3 From 06617337bf1ee819d022a13b4e7022fc9da283c4 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 29 Apr 2024 08:51:46 +0100 Subject: media: atomisp: Fix spelling mistake "pipline" -> "pipeline" There is a spelling mistake in a dev_err() message. Fix it. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20240429075146.1073869-1-colin.i.king@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/staging/media/atomisp/pci/atomisp_ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c index effc71b5a439..d7e8a9871522 100644 --- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c +++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c @@ -894,7 +894,7 @@ int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count) ret = __media_pipeline_start(&asd->video_out.vdev.entity.pads[0], &asd->video_out.pipe); mutex_unlock(&isp->media_dev.graph_mutex); if (ret) { - dev_err(isp->dev, "Error starting mc pipline: %d\n", ret); + dev_err(isp->dev, "Error starting mc pipeline: %d\n", ret); goto out_unlock; } -- cgit v1.2.3 From 4306942df0f5f37c2fa4e55658e93d69d508c6ab Mon Sep 17 00:00:00 2001 From: Roshan Khatri Date: Wed, 1 May 2024 22:11:20 +0545 Subject: media: atomisp: Fix spelling mistake in sh_css_internal.h codespell reported misspelled words still and member. This patch fixes misspellings. Signed-off-by: Roshan Khatri Link: https://lore.kernel.org/r/20240501162620.86865-1-topofeverest8848@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/staging/media/atomisp/pci/sh_css_internal.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css_internal.h b/drivers/staging/media/atomisp/pci/sh_css_internal.h index 1501046cebb9..a2d972ea3fa0 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_internal.h +++ b/drivers/staging/media/atomisp/pci/sh_css_internal.h @@ -511,7 +511,7 @@ struct sh_css_sp_pipeline { * of the associated pipe. Dynamic means that the data address can * change with every (frame) iteration of the associated pipe * - * s3a and dis are now also dynamic but (stil) handled separately + * s3a and dis are now also dynamic but (still) handled separately */ #define SH_CSS_NUM_DYNAMIC_FRAME_IDS (3) @@ -599,7 +599,7 @@ struct sh_css_sp_stage { /* * Time: 2012-07-19, 17:40. - * Note: Add a new data memeber "debug" in "sh_css_sp_group". This + * Note: Add a new data member "debug" in "sh_css_sp_group". This * data member is used to pass the debugging command from the * Host to the SP. * -- cgit v1.2.3 From 5b11fe4daf6878b99526899f261f7700b09b0e5b Mon Sep 17 00:00:00 2001 From: Roshan Khatri Date: Sun, 5 May 2024 09:14:30 +0545 Subject: media: atomisp: Fix spelling mistake in ia_css_eed1_8.host.c codespell reported misspelled calculating in ia_css_eed1_8.host.c. This patch fixes the misspellings. Signed-off-by: Roshan Khatri Link: https://lore.kernel.org/r/20240505032931.133879-1-topofeverest8848@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- .../staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c index bfea78171f7c..e4fc90f88e24 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c +++ b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c @@ -161,7 +161,7 @@ ia_css_eed1_8_vmem_encode( assert(fcinv_x[j] > fcinv_x[j - 1]); } - /* The implementation of the calulating 1/x is based on the availability + /* The implementation of the calculating 1/x is based on the availability * of the OP_vec_shuffle16 operation. * A 64 element vector is split up in 4 blocks of 16 element. Each array is copied to * a vector 4 times, (starting at 0, 16, 32 and 48). All array elements are copied or -- cgit v1.2.3 From 0d0e892cd1be027ca75f355a8c68a0f98902c0e6 Mon Sep 17 00:00:00 2001 From: Roshan Khatri Date: Sun, 5 May 2024 19:08:44 +0545 Subject: media: atomisp: Fix spelling mistake in hmm_bo.c codespell reported misspelled unchanged in hmm_bo.c at two places. This patch fixes the misspellings. Signed-off-by: Roshan Khatri Link: https://lore.kernel.org/r/20240505132345.135528-1-topofeverest8848@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/staging/media/atomisp/pci/hmm/hmm_bo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c index 095cd0ba8c21..b90efac771e2 100644 --- a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c +++ b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c @@ -288,7 +288,7 @@ static void __bo_take_off_handling(struct hmm_buffer_object *bo) /* 3. when bo->prev != NULL && bo->next == NULL, bo is not a rbtree * node, bo is the last element of the linked list after rbtree * node, to take off this bo, we just need set the "prev/next" - * pointers to NULL, the free rbtree stays unchaged + * pointers to NULL, the free rbtree stays unchanged */ } else if (bo->prev && !bo->next) { bo->prev->next = NULL; @@ -296,7 +296,7 @@ static void __bo_take_off_handling(struct hmm_buffer_object *bo) /* 4. when bo->prev != NULL && bo->next != NULL ,bo is not a rbtree * node, bo is in the middle of the linked list after rbtree node, * to take off this bo, we just set take the "prev/next" pointers - * to NULL, the free rbtree stays unchaged + * to NULL, the free rbtree stays unchanged */ } else if (bo->prev && bo->next) { bo->next->prev = bo->prev; -- cgit v1.2.3 From 41edaef5942b34f05e9be35679a57adc4662bfd5 Mon Sep 17 00:00:00 2001 From: Roshan Khatri Date: Mon, 6 May 2024 11:43:18 +0545 Subject: media: atomisp: Fix spelling mistake in ia_css_debug.c codespell reported misspelled suppress and implicit on ia_css_debug.c. This patch fixes the misspellings. Signed-off-by: Roshan Khatri Link: https://lore.kernel.org/r/20240506055818.2532-1-topofeverest8848@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c index 9982e77716a7..35c98fb8d6e8 100644 --- a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c +++ b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c @@ -848,7 +848,7 @@ void ia_css_debug_enable_sp_sleep_mode(enum ia_css_sp_sleep_mode mode) fw = &sh_css_sp_fw; HIVE_ADDR_sp_sleep_mode = fw->info.sp.sleep_mode; - (void)HIVE_ADDR_sp_sleep_mode; /* Suppres warnings in CRUN */ + (void)HIVE_ADDR_sp_sleep_mode; /* Suppress warnings in CRUN */ sp_dmem_store_uint32(SP0_ID, (unsigned int)sp_address_of(sp_sleep_mode), @@ -1334,7 +1334,7 @@ ia_css_debug_pipe_graph_dump_stage( if (stage->stage_num == 0) { /* - * There are some implicite assumptions about which bin is the + * There are some implicit assumptions about which bin is the * input binary e.g. which one is connected to the input system * Priority: * 1) sp_raw_copy bin has highest priority -- cgit v1.2.3 From 5d5f67b3b23f42eafdba508d6075fa714cbe2ed2 Mon Sep 17 00:00:00 2001 From: Roshan Khatri Date: Tue, 7 May 2024 11:10:12 +0545 Subject: media: atomisp: Fix spelling mistakes in sh_css_sp.c Codespell reported misspelled fields,suppress,separately and offset in file sh_css_sp.c. This patch fixes the misspellings. Signed-off-by: Roshan Khatri Link: https://lore.kernel.org/r/20240507052512.7296-1-topofeverest8848@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede Signed-off-by: Hans Verkuil --- drivers/staging/media/atomisp/pci/sh_css_sp.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/sh_css_sp.c b/drivers/staging/media/atomisp/pci/sh_css_sp.c index 29e5bee78c20..c2ab70f8fafe 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_sp.c +++ b/drivers/staging/media/atomisp/pci/sh_css_sp.c @@ -1022,8 +1022,10 @@ sh_css_sp_init_stage(struct ia_css_binary *binary, */ if (binary->info->sp.pipeline.mode == IA_CSS_BINARY_MODE_PREVIEW && (binary->vf_downscale_log2 > 0)) { - /* TODO: Remove this after preview output decimation is fixed - * by configuring out&vf info fiels properly */ + /* + * TODO: Remove this after preview output decimation is fixed + * by configuring out&vf info fields properly. + */ sh_css_sp_stage.frames.out[0].info.padded_width <<= binary->vf_downscale_log2; sh_css_sp_stage.frames.out[0].info.res.width @@ -1325,7 +1327,7 @@ bool sh_css_write_host2sp_command(enum host2sp_commands host2sp_command) host2sp_command) / sizeof(int); enum host2sp_commands last_cmd = host2sp_cmd_error; - (void)HIVE_ADDR_host_sp_com; /* Suppres warnings in CRUN */ + (void)HIVE_ADDR_host_sp_com; /* Suppress warnings in CRUN */ /* Previous command must be handled by SP (by design) */ last_cmd = load_sp_array_uint(host_sp_com, offset); @@ -1343,7 +1345,7 @@ sh_css_read_host2sp_command(void) unsigned int HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com; unsigned int offset = (unsigned int)offsetof(struct host_sp_communication, host2sp_command) / sizeof(int); - (void)HIVE_ADDR_host_sp_com; /* Suppres warnings in CRUN */ + (void)HIVE_ADDR_host_sp_com; /* Suppress warnings in CRUN */ return (enum host2sp_commands)load_sp_array_uint(host_sp_com, offset); } @@ -1351,7 +1353,7 @@ sh_css_read_host2sp_command(void) * Frame data is no longer part of the sp_stage structure but part of a * separate structure. The aim is to make the sp_data struct static * (it defines a pipeline) and that the dynamic (per frame) data is stored - * separetly. + * separately. * * This function must be called first every where were you start constructing * a new pipeline by defining one or more stages with use of variable @@ -1364,7 +1366,7 @@ sh_css_init_host2sp_frame_data(void) /* Clean table */ unsigned int HIVE_ADDR_host_sp_com = sh_css_sp_fw.info.sp.host_sp_com; - (void)HIVE_ADDR_host_sp_com; /* Suppres warnings in CRUN */ + (void)HIVE_ADDR_host_sp_com; /* Suppress warnings in CRUN */ /* * rvanimme: don't clean it to save static frame info line ref_in * ref_out, and tnr_frames. Once this static data is in a @@ -1544,7 +1546,7 @@ ia_css_pipe_set_irq_mask(struct ia_css_pipe *pipe, * - different assert for Linux and Windows */ - (void)HIVE_ADDR_host_sp_com; /* Suppres warnings in CRUN */ + (void)HIVE_ADDR_host_sp_com; /* Suppress warnings in CRUN */ IA_CSS_LOG("or_mask=%x, and_mask=%x", or_mask, and_mask); event_irq_mask.or_mask = (uint16_t)or_mask; @@ -1573,7 +1575,7 @@ ia_css_event_get_irq_mask(const struct ia_css_pipe *pipe, struct sh_css_event_irq_mask event_irq_mask; unsigned int pipe_num; - (void)HIVE_ADDR_host_sp_com; /* Suppres warnings in CRUN */ + (void)HIVE_ADDR_host_sp_com; /* Suppress warnings in CRUN */ IA_CSS_ENTER_LEAVE(""); @@ -1623,7 +1625,7 @@ sh_css_sp_start_isp(void) if (sp_running) return; - (void)HIVE_ADDR_sp_sw_state; /* Suppres warnings in CRUN */ + (void)HIVE_ADDR_sp_sw_state; /* Suppress warnings in CRUN */ /* no longer here, sp started immediately */ /*ia_css_debug_pipe_graph_dump_epilogue();*/ @@ -1664,7 +1666,7 @@ ia_css_isp_has_started(void) { const struct ia_css_fw_info *fw = &sh_css_sp_fw; unsigned int HIVE_ADDR_ia_css_ispctrl_sp_isp_started = fw->info.sp.isp_started; - (void)HIVE_ADDR_ia_css_ispctrl_sp_isp_started; /* Suppres warnings in CRUN */ + (void)HIVE_ADDR_ia_css_ispctrl_sp_isp_started; /* Suppress warnings in CRUN */ return (bool)load_sp_uint(ia_css_ispctrl_sp_isp_started); } @@ -1719,7 +1721,7 @@ sh_css_sp_set_dma_sw_reg(int dma_id, sw_reg = sh_css_sp_group.debug.dma_sw_reg; - /* get the offest of the target bit */ + /* get the offset of the target bit */ bit_offset = (8 * request_type) + channel_id; /* clear the value of the target bit */ -- cgit v1.2.3 From a7351f0d3668b449fdc2cfd90403b1cb1f03ed6d Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Wed, 3 Jul 2024 13:59:16 +0200 Subject: media: stm32: dcmipp: correct error handling in dcmipp_create_subdevs Correct error handling within the dcmipp_create_subdevs by properly decrementing the i counter when releasing the subdevs. Fixes: 28e0f3772296 ("media: stm32-dcmipp: STM32 DCMIPP camera interface driver") Cc: stable@vger.kernel.org Signed-off-by: Alain Volmat Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus [hverkuil: correct the indices: it's [i], not [i - 1].] --- drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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..7f771ea49b78 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c @@ -202,8 +202,8 @@ 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; } -- cgit v1.2.3 From 79cf9c6ee44e0af7e1383365d29c64eece6665bb Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Fri, 28 Jun 2024 15:29:42 +0200 Subject: media: uapi: pisp_be_config: Drop BIT() from uAPI The pisp_be_config.h uAPI header file contains a bit-field definition that uses the BIT() helper macro. As the BIT() identifier is not defined in userspace, drop it from the uAPI header. Fixes: c6c49bac8770 ("media: uapi: Add Raspberry Pi PiSP Back End uAPI") Signed-off-by: Jacopo Mondi Reviewed-by: Tomi Valkeinen Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- include/uapi/linux/media/raspberrypi/pisp_be_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/linux/media/raspberrypi/pisp_be_config.h b/include/uapi/linux/media/raspberrypi/pisp_be_config.h index 1684ae068d4f..27d0cc417d6b 100644 --- a/include/uapi/linux/media/raspberrypi/pisp_be_config.h +++ b/include/uapi/linux/media/raspberrypi/pisp_be_config.h @@ -146,7 +146,7 @@ struct pisp_be_dpc_config { */ struct pisp_be_geq_config { __u16 offset; -#define PISP_BE_GEQ_SHARPER BIT(15) +#define PISP_BE_GEQ_SHARPER (1U << 15) #define PISP_BE_GEQ_SLOPE ((1 << 10) - 1) /* top bit is the "sharper" flag, slope value is bottom 10 bits */ __u16 slope_sharper; -- cgit v1.2.3 From 1991a09e6d7c1dff5efea65b5b31082332f2e64e Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Fri, 28 Jun 2024 15:29:43 +0200 Subject: media: uapi: pisp_common: Add 32 bpp format test Add definition and test for 32-bits image formats to the pisp_common.h uAPI header. Fixes: c6c49bac8770 ("media: uapi: Add Raspberry Pi PiSP Back End uAPI") Signed-off-by: Jacopo Mondi Acked-by: David Plowman Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- include/uapi/linux/media/raspberrypi/pisp_common.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/uapi/linux/media/raspberrypi/pisp_common.h b/include/uapi/linux/media/raspberrypi/pisp_common.h index b2522e29c976..74d096188233 100644 --- a/include/uapi/linux/media/raspberrypi/pisp_common.h +++ b/include/uapi/linux/media/raspberrypi/pisp_common.h @@ -72,6 +72,8 @@ enum pisp_image_format { PISP_IMAGE_FORMAT_SHIFT_8 = 0x00080000, PISP_IMAGE_FORMAT_SHIFT_MASK = 0x000f0000, + PISP_IMAGE_FORMAT_BPP_32 = 0x00100000, + PISP_IMAGE_FORMAT_UNCOMPRESSED = 0x00000000, PISP_IMAGE_FORMAT_COMPRESSION_MODE_1 = 0x01000000, PISP_IMAGE_FORMAT_COMPRESSION_MODE_2 = 0x02000000, @@ -134,6 +136,7 @@ enum pisp_image_format { PISP_IMAGE_FORMAT_PLANARITY_PLANAR) #define PISP_IMAGE_FORMAT_wallpaper(fmt) \ ((fmt) & PISP_IMAGE_FORMAT_WALLPAPER_ROLL) +#define PISP_IMAGE_FORMAT_bpp_32(fmt) ((fmt) & PISP_IMAGE_FORMAT_BPP_32) #define PISP_IMAGE_FORMAT_HOG(fmt) \ ((fmt) & \ (PISP_IMAGE_FORMAT_HOG_SIGNED | PISP_IMAGE_FORMAT_HOG_UNSIGNED)) -- cgit v1.2.3 From f5cee94f2dfe5b7625452b831f7629369ce68a7b Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Fri, 28 Jun 2024 15:29:44 +0200 Subject: media: uapi: pisp_common: Capitalize all macros The macro used to inspect an image format characteristic use a mixture of capitalized and non-capitalized letters, which is rather unusual for the Linux kernel style. Capitalize all identifiers. Fixes: c6c49bac8770 ("media: uapi: Add Raspberry Pi PiSP Back End uAPI") Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- include/uapi/linux/media/raspberrypi/pisp_common.h | 38 +++++++++++----------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/include/uapi/linux/media/raspberrypi/pisp_common.h b/include/uapi/linux/media/raspberrypi/pisp_common.h index 74d096188233..cbdccfed1261 100644 --- a/include/uapi/linux/media/raspberrypi/pisp_common.h +++ b/include/uapi/linux/media/raspberrypi/pisp_common.h @@ -92,51 +92,51 @@ enum pisp_image_format { PISP_IMAGE_FORMAT_THREE_CHANNEL }; -#define PISP_IMAGE_FORMAT_bps_8(fmt) \ +#define PISP_IMAGE_FORMAT_BPS_8(fmt) \ (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_8) -#define PISP_IMAGE_FORMAT_bps_10(fmt) \ +#define PISP_IMAGE_FORMAT_BPS_10(fmt) \ (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_10) -#define PISP_IMAGE_FORMAT_bps_12(fmt) \ +#define PISP_IMAGE_FORMAT_BPS_12(fmt) \ (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_12) -#define PISP_IMAGE_FORMAT_bps_16(fmt) \ +#define PISP_IMAGE_FORMAT_BPS_16(fmt) \ (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_16) -#define PISP_IMAGE_FORMAT_bps(fmt) \ +#define PISP_IMAGE_FORMAT_BPS(fmt) \ (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) ? \ 8 + (2 << (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) - 1)) : 8) -#define PISP_IMAGE_FORMAT_shift(fmt) \ +#define PISP_IMAGE_FORMAT_SHIFT(fmt) \ (((fmt) & PISP_IMAGE_FORMAT_SHIFT_MASK) / PISP_IMAGE_FORMAT_SHIFT_1) -#define PISP_IMAGE_FORMAT_three_channel(fmt) \ +#define PISP_IMAGE_FORMAT_THREE_CHANNEL(fmt) \ ((fmt) & PISP_IMAGE_FORMAT_THREE_CHANNEL) -#define PISP_IMAGE_FORMAT_single_channel(fmt) \ +#define PISP_IMAGE_FORMAT_SINGLE_CHANNEL(fmt) \ (!((fmt) & PISP_IMAGE_FORMAT_THREE_CHANNEL)) -#define PISP_IMAGE_FORMAT_compressed(fmt) \ +#define PISP_IMAGE_FORMAT_COMPRESSED(fmt) \ (((fmt) & PISP_IMAGE_FORMAT_COMPRESSION_MASK) != \ PISP_IMAGE_FORMAT_UNCOMPRESSED) -#define PISP_IMAGE_FORMAT_sampling_444(fmt) \ +#define PISP_IMAGE_FORMAT_SAMPLING_444(fmt) \ (((fmt) & PISP_IMAGE_FORMAT_SAMPLING_MASK) == \ PISP_IMAGE_FORMAT_SAMPLING_444) -#define PISP_IMAGE_FORMAT_sampling_422(fmt) \ +#define PISP_IMAGE_FORMAT_SAMPLING_422(fmt) \ (((fmt) & PISP_IMAGE_FORMAT_SAMPLING_MASK) == \ PISP_IMAGE_FORMAT_SAMPLING_422) -#define PISP_IMAGE_FORMAT_sampling_420(fmt) \ +#define PISP_IMAGE_FORMAT_SAMPLING_420(fmt) \ (((fmt) & PISP_IMAGE_FORMAT_SAMPLING_MASK) == \ PISP_IMAGE_FORMAT_SAMPLING_420) -#define PISP_IMAGE_FORMAT_order_normal(fmt) \ +#define PISP_IMAGE_FORMAT_ORDER_NORMAL(fmt) \ (!((fmt) & PISP_IMAGE_FORMAT_ORDER_SWAPPED)) -#define PISP_IMAGE_FORMAT_order_swapped(fmt) \ +#define PISP_IMAGE_FORMAT_ORDER_SWAPPED(fmt) \ ((fmt) & PISP_IMAGE_FORMAT_ORDER_SWAPPED) -#define PISP_IMAGE_FORMAT_interleaved(fmt) \ +#define PISP_IMAGE_FORMAT_INTERLEAVED(fmt) \ (((fmt) & PISP_IMAGE_FORMAT_PLANARITY_MASK) == \ PISP_IMAGE_FORMAT_PLANARITY_INTERLEAVED) -#define PISP_IMAGE_FORMAT_semiplanar(fmt) \ +#define PISP_IMAGE_FORMAT_SEMIPLANAR(fmt) \ (((fmt) & PISP_IMAGE_FORMAT_PLANARITY_MASK) == \ PISP_IMAGE_FORMAT_PLANARITY_SEMI_PLANAR) -#define PISP_IMAGE_FORMAT_planar(fmt) \ +#define PISP_IMAGE_FORMAT_PLANAR(fmt) \ (((fmt) & PISP_IMAGE_FORMAT_PLANARITY_MASK) == \ PISP_IMAGE_FORMAT_PLANARITY_PLANAR) -#define PISP_IMAGE_FORMAT_wallpaper(fmt) \ +#define PISP_IMAGE_FORMAT_WALLPAPER(fmt) \ ((fmt) & PISP_IMAGE_FORMAT_WALLPAPER_ROLL) -#define PISP_IMAGE_FORMAT_bpp_32(fmt) ((fmt) & PISP_IMAGE_FORMAT_BPP_32) +#define PISP_IMAGE_FORMAT_BPP_32(fmt) ((fmt) & PISP_IMAGE_FORMAT_BPP_32) #define PISP_IMAGE_FORMAT_HOG(fmt) \ ((fmt) & \ (PISP_IMAGE_FORMAT_HOG_SIGNED | PISP_IMAGE_FORMAT_HOG_UNSIGNED)) -- cgit v1.2.3 From 639065c621df9bad9d94373084e1c568f81d34e0 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Fri, 28 Jun 2024 15:29:45 +0200 Subject: media: uapi: pisp_be_config: Re-sort pisp_be_tiles_config The order of the members of pisp_be_tiles_config is relevant as the driver logic assumes 'config' to be at offset 0. Re-sort the member to match the driver's expectations. Fixes: c6c49bac8770 ("media: uapi: Add Raspberry Pi PiSP Back End uAPI") Signed-off-by: Jacopo Mondi Acked-by: Naushir Patuck Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- include/uapi/linux/media/raspberrypi/pisp_be_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/linux/media/raspberrypi/pisp_be_config.h b/include/uapi/linux/media/raspberrypi/pisp_be_config.h index 27d0cc417d6b..f8650ca92bf8 100644 --- a/include/uapi/linux/media/raspberrypi/pisp_be_config.h +++ b/include/uapi/linux/media/raspberrypi/pisp_be_config.h @@ -919,9 +919,9 @@ struct pisp_tile { * @config: PiSP Back End configuration */ struct pisp_be_tiles_config { + struct pisp_be_config config; struct pisp_tile tiles[PISP_BACK_END_NUM_TILES]; __u32 num_tiles; - struct pisp_be_config config; } __attribute__((packed)); #endif /* _UAPI_PISP_BE_CONFIG_H_ */ -- cgit v1.2.3 From 1c2c57bd439ecaffc728139a0a00701bee886d0a Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Fri, 28 Jun 2024 15:29:46 +0200 Subject: media: uapi: pisp_be_config: Add extra config fields Complete the pisp_be_config strcture by adding fields that even if not written to the HW are relevant to complete the uAPI and put it in par with the BSP driver. Fixes: c6c49bac8770 ("media: uapi: Add Raspberry Pi PiSP Back End uAPI") Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil --- .../uapi/linux/media/raspberrypi/pisp_be_config.h | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/include/uapi/linux/media/raspberrypi/pisp_be_config.h b/include/uapi/linux/media/raspberrypi/pisp_be_config.h index f8650ca92bf8..cbeb714f4d61 100644 --- a/include/uapi/linux/media/raspberrypi/pisp_be_config.h +++ b/include/uapi/linux/media/raspberrypi/pisp_be_config.h @@ -716,6 +716,13 @@ struct pisp_be_hog_buffer_config { /** * struct pisp_be_config - RaspberryPi PiSP Back End Processing configuration * + * @input_buffer: Input buffer addresses + * @tdn_input_buffer: TDN input buffer addresses + * @stitch_input_buffer: Stitch input buffer addresses + * @tdn_output_buffer: TDN output buffer addresses + * @stitch_output_buffer: Stitch output buffer addresses + * @output_buffer: Output buffers addresses + * @hog_buffer: HOG buffer addresses * @global: Global PiSP configuration * @input_format: Input image format * @decompress: Decompress configuration @@ -753,8 +760,30 @@ struct pisp_be_hog_buffer_config { * @resample: Resampling configuration * @output_format: Output format configuration * @hog: HOG configuration + * @axi: AXI bus configuration + * @lsc_extra: LSC extra info + * @cac_extra: CAC extra info + * @downscale_extra: Downscaler extra info + * @resample_extra: Resample extra info + * @crop: Crop configuration + * @hog_format: HOG format info + * @dirty_flags_bayer: Bayer enable dirty flags + * (:c:type:`pisp_be_bayer_enable`) + * @dirty_flags_rgb: RGB enable dirty flags + * (:c:type:`pisp_be_rgb_enable`) + * @dirty_flags_extra: Extra dirty flags */ struct pisp_be_config { + /* I/O configuration: */ + struct pisp_be_input_buffer_config input_buffer; + struct pisp_be_tdn_input_buffer_config tdn_input_buffer; + struct pisp_be_stitch_input_buffer_config stitch_input_buffer; + struct pisp_be_tdn_output_buffer_config tdn_output_buffer; + struct pisp_be_stitch_output_buffer_config stitch_output_buffer; + struct pisp_be_output_buffer_config + output_buffer[PISP_BACK_END_NUM_OUTPUTS]; + struct pisp_be_hog_buffer_config hog_buffer; + /* Processing configuration: */ struct pisp_be_global_config global; struct pisp_image_format_config input_format; struct pisp_decompress_config decompress; @@ -793,6 +822,18 @@ struct pisp_be_config { struct pisp_be_output_format_config output_format[PISP_BACK_END_NUM_OUTPUTS]; struct pisp_be_hog_config hog; + struct pisp_be_axi_config axi; + /* Non-register fields: */ + struct pisp_be_lsc_extra lsc_extra; + struct pisp_be_cac_extra cac_extra; + struct pisp_be_downscale_extra + downscale_extra[PISP_BACK_END_NUM_OUTPUTS]; + struct pisp_be_resample_extra resample_extra[PISP_BACK_END_NUM_OUTPUTS]; + struct pisp_be_crop_config crop; + struct pisp_image_format_config hog_format; + __u32 dirty_flags_bayer; /* these use pisp_be_bayer_enable */ + __u32 dirty_flags_rgb; /* use pisp_be_rgb_enable */ + __u32 dirty_flags_extra; /* these use pisp_be_dirty_t */ } __attribute__((packed)); /** -- cgit v1.2.3 From 68a72104cbcf38ad16500216e213fa4eb21c4be2 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 9 Jul 2024 16:37:15 +1000 Subject: media: raspberrypi: Switch to remove_new MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The remove callback's return value is about to change from int to void, this is done by commit 0edb555a65d1 ("platform: Make platform_driver::remove() return void"). Prepare for merging the patch by switching the PiSP driver from remove to remove_new callback. Fixes: 12187bd5d4f8 ("media: raspberrypi: Add support for PiSP BE") Signed-off-by: Stephen Rothwell Acked-by: Naushir Patuck Acked-by: Jacopo Mondi Signed-off-by: Sakari Ailus Reviewed-by: Uwe Kleine-König Signed-off-by: Hans Verkuil --- drivers/media/platform/raspberrypi/pisp_be/pisp_be.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c index e74df5b116dc..65ff2382cffe 100644 --- a/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c +++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c @@ -1756,7 +1756,7 @@ pm_runtime_disable_err: return ret; } -static int pispbe_remove(struct platform_device *pdev) +static void pispbe_remove(struct platform_device *pdev) { struct pispbe_dev *pispbe = platform_get_drvdata(pdev); @@ -1765,8 +1765,6 @@ static int pispbe_remove(struct platform_device *pdev) pispbe_runtime_suspend(pispbe->dev); pm_runtime_dont_use_autosuspend(pispbe->dev); pm_runtime_disable(pispbe->dev); - - return 0; } static const struct dev_pm_ops pispbe_pm_ops = { @@ -1783,7 +1781,7 @@ MODULE_DEVICE_TABLE(of, pispbe_of_match); static struct platform_driver pispbe_pdrv = { .probe = pispbe_probe, - .remove = pispbe_remove, + .remove_new = pispbe_remove, .driver = { .name = PISPBE_NAME, .of_match_table = pispbe_of_match, -- cgit v1.2.3