From 6d9f0f69f7c3a359b9f80c2052164784a36dff14 Mon Sep 17 00:00:00 2001 From: Hao Yao Date: Thu, 18 Jul 2024 14:30:04 +0800 Subject: [PATCH] ARL platform Beta release on 2024-07-18 Signed-off-by: Hao Yao --- Makefile | 6 + dkms.conf | 7 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ov05c10.c | 1001 +++++++++++++++++ drivers/media/i2c/ov08a10.c | 1 + drivers/media/pci/intel/ipu-buttress.c | 34 +- drivers/media/pci/intel/ipu-buttress.h | 1 - drivers/media/pci/intel/ipu-dma.c | 28 + drivers/media/pci/intel/ipu-fw-isys.c | 4 +- .../media/pci/intel/ipu-isys-csi2-be-soc.c | 7 +- drivers/media/pci/intel/ipu-isys-csi2-be.c | 7 +- drivers/media/pci/intel/ipu-isys-csi2.c | 37 +- drivers/media/pci/intel/ipu-isys-queue.c | 18 +- drivers/media/pci/intel/ipu-isys-subdev.c | 8 +- drivers/media/pci/intel/ipu-isys-video.c | 44 +- drivers/media/pci/intel/ipu-isys.c | 23 +- drivers/media/pci/intel/ipu-mmu.c | 56 +- drivers/media/pci/intel/ipu-mmu.h | 4 +- drivers/media/pci/intel/ipu-psys.c | 453 +++++--- drivers/media/pci/intel/ipu-psys.h | 27 +- drivers/media/pci/intel/ipu.c | 10 +- drivers/media/pci/intel/ipu6/ipu-platform.h | 4 +- drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c | 17 +- drivers/media/pci/intel/ipu6/ipu6-isys-gpc.c | 1 - drivers/media/pci/intel/ipu6/ipu6-isys.c | 1 - drivers/media/pci/intel/ipu6/ipu6-psys-gpc.c | 1 - drivers/media/pci/intel/ipu6/ipu6-psys.c | 24 +- drivers/media/pci/intel/ipu6/ipu6.c | 23 - ...d-Tline-calculation-and-handshake-pi.patch | 6 +- ...dd-IPU6-and-supported-sensors-config.patch | 6 +- ...edia-ipu-bridge-Support-more-sensors.patch | 6 +- ...enumeration-of-devices-with-a-_DEP-p.patch | 10 +- ...er-interrupt-handler-for-system-susp.patch | 34 +- ...6-mei-vsc-reset-ivsc-during-shutdown.patch | 16 +- ...op-restart-mei-device-during-system-.patch | 10 +- ...timeout-error-with-added-delay-post-.patch | 13 +- ...-csi-don-t-count-privacy-on-as-error.patch | 10 +- ...dd-separate-lock-for-v4l2-control-ha.patch | 16 +- ...emove-privacy-status-in-struct-mei_c.patch | 13 +- ...-media-Support-ov05c10-camera-sensor.patch | 62 + 40 files changed, 1671 insertions(+), 379 deletions(-) create mode 100644 drivers/media/i2c/ov05c10.c create mode 100644 patch/v6.8/0012-media-Support-ov05c10-camera-sensor.patch diff --git a/Makefile b/Makefile index 6286beade951..b268b643cce9 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,7 @@ version_lt = $(shell \ KV_IVSC := 6.6.0 KV_IPU_BRIDGE := 6.6.0 KV_OV2740 := 6.8.0 +KV_OV05C10 := 6.8.0 KERNEL_SRC ?= /lib/modules/$(KERNELRELEASE)/build MODSRC := $(shell pwd) @@ -88,6 +89,11 @@ ifeq ($(call version_lt,$(KERNEL_VERSION),$(KV_OV2740)),true) export CONFIG_VIDEO_OV2740 = m export CONFIG_VIDEO_GC5035 = m endif + +ifeq ($(call version_lt,$(KERNEL_VERSION),$(KV_OV05C10)),false) +export CONFIG_VIDEO_OV05C10 = m +endif + obj-y += drivers/media/i2c/ ifeq ($(call version_lt,$(KERNEL_VERSION),$(KV_IVSC)),true) diff --git a/dkms.conf b/dkms.conf index 708e1298b196..635f518d4eb9 100644 --- a/dkms.conf +++ b/dkms.conf @@ -32,6 +32,7 @@ version_lt() { KERNEL_VERSION=$(echo ${kernelver} | sed 's/[^0-9.]*\([0-9.]*\).*/\1/') KV_IVSC=6.6.0 KV_OV2740=6.8.0 +KV_OV05C10=6.8.0 BUILT_MODULE_NAME[0]="intel-ipu6" BUILT_MODULE_LOCATION[0]="drivers/media/pci/intel/ipu6" @@ -78,6 +79,12 @@ BUILT_MODULE_LOCATION[10]="drivers/media/i2c" DEST_MODULE_LOCATION[10]="/updates" +if ! version_lt ${KERNEL_VERSION} ${KV_OV05C10}; then + BUILT_MODULE_NAME[11]="ov05c10" + BUILT_MODULE_LOCATION[11]="drivers/media/i2c" + DEST_MODULE_LOCATION[11]="/updates" +fi + if version_lt ${KERNEL_VERSION} ${KV_OV2740}; then BUILT_MODULE_NAME[11]="ov2740" BUILT_MODULE_LOCATION[11]="drivers/media/i2c" diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 1f395448bc7a..b4fa25598fbf 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_VIDEO_OV01A1S) += ov01a1s.o obj-$(CONFIG_VIDEO_OV01A10) += ov01a10.o obj-$(CONFIG_VIDEO_OV02C10) += ov02c10.o obj-$(CONFIG_VIDEO_OV02E10) += ov02e10.o +obj-$(CONFIG_VIDEO_OV05C10) += ov05c10.o obj-$(CONFIG_VIDEO_OV2740) += ov2740.o obj-$(CONFIG_VIDEO_HM2170) += hm2170.o obj-$(CONFIG_VIDEO_HM2170) += hm2172.o diff --git a/drivers/media/i2c/ov05c10.c b/drivers/media/i2c/ov05c10.c new file mode 100644 index 000000000000..bce60998cd41 --- /dev/null +++ b/drivers/media/i2c/ov05c10.c @@ -0,0 +1,1001 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2024 Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OV05C10_REG_CHIP_ID_H CCI_REG16(0x00) //P0:0x00[7:0],0x01[7:0] +#define OV05C10_REG_CHIP_ID_L CCI_REG16(0x02) //P0:0x02[7:0],0x03[7:0] +#define OV05C10_CHIP_ID 0x43055610 + +#define REG_TIMING_HTS CCI_REG16(0x37) //P1:0x37[4:0],0x38[7:0] +#define REG_TIMING_VTS CCI_REG16(0x35) //P1:0x35[7:0],0x36[7:0] +#define REG_DUMMY_LINE CCI_REG16(0x05) //P1:0x05[7:0],0x06[7:0] +#define REG_EXPOSURE CCI_REG24(0x02) //P1:0x02[7:0]--0x04[7:0] +#define REG_ANALOG_GAIN CCI_REG8(0x24) +#define REG_DIGITAL_GAIN CCI_REG16(0x21) //P1:0x21[2:0],0x22[7:0] + +#define REG_PAGE_FLAG CCI_REG8(0xfd) +#define PAGE_0 0x0 +#define PAGE_1 0x1 + +#define OV05C10_EXPOSURE_MARGIN 33 +#define OV05C10_EXPOSURE_MIN 0x6 +#define OV05C10_EXPOSURE_STEP 0x1 + +#define MAX_ANA_GAIN 0xf8 // 15.5x +#define MIN_ANA_GAIN 0x10 // 1x +#define OV05C10_ANAL_GAIN_STEP 0x01 +#define OV05C10_ANAL_GAIN_DEFAULT 0x10 + +#define MAX_DIG_GAIN 0x100 // 4x +#define MIN_DIG_GAIN 0x40 // 1x +#define OV05C10_DGTL_GAIN_STEP 0x01 +#define OV05C10_DGTL_GAIN_DEFAULT 0x40 + +#define OV05C10_VTS_MAX 0xffff +#define OV05C10_PPL 3234 +#define OV05C10_DEFAULT_VTS 1860 +#define PIXEL_RATE 192000000ULL +#define OV05C10_NATIVE_WIDTH 2888 +#define OV05C10_NATIVE_HEIGHT 1808 + +#define to_ov05c10(_sd) container_of(_sd, struct ov05c10, sd) + +static const char *const ov05c10_test_pattern_menu[] = { + "No Pattern", + "Color Bar", +}; +static const s64 ov05c10_link_freq_menu_items[] = { + 480000000ULL, +}; + +struct ov05c10_reg_list { + u32 num_of_regs; + const struct cci_reg_sequence *regs; +}; + +struct ov05c10_mode { + u32 width; + u32 height; + u32 hts; + u32 vts_def; + u32 vts_min; + u32 link_freq_index; + u32 code; + u32 fps; + s32 lanes; + /* Sensor register settings for this mode */ + const struct ov05c10_reg_list reg_list; +}; + +static const struct cci_reg_sequence ov05c10_soft_standby[] = { + { REG_PAGE_FLAG, PAGE_0 }, + { CCI_REG8(0xa0), 0x00 }, + { REG_PAGE_FLAG, PAGE_1 }, + { CCI_REG8(0x01), 0x02 }, +}; + +static const struct cci_reg_sequence ov05c10_streaming[] = { + { REG_PAGE_FLAG, PAGE_0 }, + { CCI_REG8(0xa0), 0x01 }, + { REG_PAGE_FLAG, PAGE_1 }, + { CCI_REG8(0x01), 0x02 }, +}; + +static const struct cci_reg_sequence ov05c10_test_enable[] = { + { CCI_REG8(0xf3), 0x02 }, + { CCI_REG8(0x12), 0x01 }, +}; + +static const struct cci_reg_sequence ov05c10_test_disable[] = { + { CCI_REG8(0xf3), 0x00 }, + { CCI_REG8(0x12), 0x00 }, +}; + +static const struct cci_reg_sequence ov05c10_trigger[] = { + { REG_PAGE_FLAG, PAGE_1 }, + { CCI_REG8(0x01), 0x01 }, +}; + +//2800X1576_2lane_raw10_Mclk19.2M_pclk96M_30fps +static const struct cci_reg_sequence mode_2800_1576_30fps[] = { + { CCI_REG8(0xfd), 0x00 }, + { CCI_REG8(0x20), 0x00 }, + { CCI_REG8(0xfd), 0x00 }, + { CCI_REG8(0x20), 0x0b }, + { CCI_REG8(0xc1), 0x09 }, + { CCI_REG8(0x21), 0x06 }, + { CCI_REG8(0x11), 0x4e }, + { CCI_REG8(0x12), 0x13 }, + { CCI_REG8(0x14), 0x96 }, + { CCI_REG8(0x1b), 0x64 }, + { CCI_REG8(0x1d), 0x02 }, + { CCI_REG8(0x1e), 0x40 }, + { CCI_REG8(0xe7), 0x03 }, + { CCI_REG8(0xe7), 0x00 }, + { CCI_REG8(0x21), 0x00 }, + { CCI_REG8(0xfd), 0x01 }, + { CCI_REG8(0x03), 0x00 }, + { CCI_REG8(0x04), 0x06 }, + { CCI_REG8(0x06), 0x76 }, + { CCI_REG8(0x07), 0x08 }, + { CCI_REG8(0x1b), 0x01 }, + { CCI_REG8(0x24), 0xff }, + { CCI_REG8(0x42), 0x5d }, + { CCI_REG8(0x43), 0x08 }, + { CCI_REG8(0x44), 0x81 }, + { CCI_REG8(0x46), 0x5f }, + { CCI_REG8(0x48), 0x18 }, + { CCI_REG8(0x49), 0x04 }, + { CCI_REG8(0x5c), 0x18 }, + { CCI_REG8(0x5e), 0x13 }, + { CCI_REG8(0x70), 0x15 }, + { CCI_REG8(0x77), 0x35 }, + { CCI_REG8(0x79), 0xb2 }, + { CCI_REG8(0x7b), 0x08 }, + { CCI_REG8(0x7d), 0x08 }, + { CCI_REG8(0x7e), 0x08 }, + { CCI_REG8(0x7f), 0x08 }, + { CCI_REG8(0x90), 0x37 }, + { CCI_REG8(0x91), 0x05 }, + { CCI_REG8(0x92), 0x18 }, + { CCI_REG8(0x93), 0x27 }, + { CCI_REG8(0x94), 0x05 }, + { CCI_REG8(0x95), 0x38 }, + { CCI_REG8(0x9b), 0x00 }, + { CCI_REG8(0x9c), 0x06 }, + { CCI_REG8(0x9d), 0x28 }, + { CCI_REG8(0x9e), 0x06 }, + { CCI_REG8(0xb2), 0x0f }, + { CCI_REG8(0xb3), 0x29 }, + { CCI_REG8(0xbf), 0x3c }, + { CCI_REG8(0xc2), 0x04 }, + { CCI_REG8(0xc4), 0x00 }, + { CCI_REG8(0xca), 0x20 }, + { CCI_REG8(0xcb), 0x20 }, + { CCI_REG8(0xcc), 0x28 }, + { CCI_REG8(0xcd), 0x28 }, + { CCI_REG8(0xce), 0x20 }, + { CCI_REG8(0xcf), 0x20 }, + { CCI_REG8(0xd0), 0x2a }, + { CCI_REG8(0xd1), 0x2a }, + { CCI_REG8(0xfd), 0x0f }, + { CCI_REG8(0x00), 0x00 }, + { CCI_REG8(0x01), 0xa0 }, + { CCI_REG8(0x02), 0x48 }, + { CCI_REG8(0x07), 0x8e }, + { CCI_REG8(0x08), 0x70 }, + { CCI_REG8(0x09), 0x01 }, + { CCI_REG8(0x0b), 0x40 }, + { CCI_REG8(0x0d), 0x07 }, + { CCI_REG8(0x11), 0x33 }, + { CCI_REG8(0x12), 0x77 }, + { CCI_REG8(0x13), 0x66 }, + { CCI_REG8(0x14), 0x65 }, + { CCI_REG8(0x15), 0x37 }, + { CCI_REG8(0x16), 0xbf }, + { CCI_REG8(0x17), 0xff }, + { CCI_REG8(0x18), 0xff }, + { CCI_REG8(0x19), 0x12 }, + { CCI_REG8(0x1a), 0x10 }, + { CCI_REG8(0x1c), 0x77 }, + { CCI_REG8(0x1d), 0x77 }, + { CCI_REG8(0x20), 0x0f }, + { CCI_REG8(0x21), 0x0f }, + { CCI_REG8(0x22), 0x0f }, + { CCI_REG8(0x23), 0x0f }, + { CCI_REG8(0x2b), 0x20 }, + { CCI_REG8(0x2c), 0x20 }, + { CCI_REG8(0x2d), 0x04 }, + { CCI_REG8(0xfd), 0x03 }, + { CCI_REG8(0x9d), 0x0f }, + { CCI_REG8(0x9f), 0x40 }, + { CCI_REG8(0xfd), 0x00 }, + { CCI_REG8(0x20), 0x1b }, + { CCI_REG8(0xfd), 0x04 }, + { CCI_REG8(0x19), 0x60 }, + { CCI_REG8(0xfd), 0x02 }, + { CCI_REG8(0x75), 0x04 }, + { CCI_REG8(0x7f), 0x06 }, + { CCI_REG8(0x9a), 0x03 }, + { CCI_REG8(0xa0), 0x00 }, + { CCI_REG8(0xa1), 0x75 }, //;GRBG + { CCI_REG8(0xa2), 0x06 }, //;vsize[11:8] + { CCI_REG8(0xa3), 0x28 }, //;vsize[7:0] + { CCI_REG8(0xa4), 0x00 }, + { CCI_REG8(0xa5), 0x2e }, + { CCI_REG8(0xa6), 0x0a }, //;hsize[11:8] + { CCI_REG8(0xa7), 0xf0 }, //;hsize[7:0] + { CCI_REG8(0xfd), 0x00 }, //;mipi size + { CCI_REG8(0x8e), 0x0a }, //;hsize[11:8] + { CCI_REG8(0x8f), 0xf0 }, //;hsize[7:0] + { CCI_REG8(0x90), 0x06 }, //;vsize[11:8] + { CCI_REG8(0x91), 0x28 }, //;vsize[7:0] + { CCI_REG8(0xfd), 0x07 }, + { CCI_REG8(0x42), 0x00 }, + { CCI_REG8(0x43), 0x80 }, + { CCI_REG8(0x44), 0x00 }, + { CCI_REG8(0x45), 0x80 }, + { CCI_REG8(0x46), 0x00 }, + { CCI_REG8(0x47), 0x80 }, + { CCI_REG8(0x48), 0x00 }, + { CCI_REG8(0x49), 0x80 }, + { CCI_REG8(0x00), 0xf7 }, + { CCI_REG8(0xfd), 0x00 }, + { CCI_REG8(0xe7), 0x03 }, + { CCI_REG8(0xe7), 0x00 }, + { CCI_REG8(0xfd), 0x00 }, + { CCI_REG8(0x93), 0x18 }, + { CCI_REG8(0x94), 0xff }, + { CCI_REG8(0x95), 0xbd }, + { CCI_REG8(0x96), 0x1a }, + { CCI_REG8(0x98), 0x04 }, + { CCI_REG8(0x99), 0x08 }, + { CCI_REG8(0x9b), 0x10 }, + { CCI_REG8(0x9c), 0x3f }, + { CCI_REG8(0xa1), 0x05 }, + { CCI_REG8(0xa4), 0x2f }, + { CCI_REG8(0xc0), 0x0c }, + { CCI_REG8(0xc1), 0x08 }, + { CCI_REG8(0xc2), 0x00 }, + { CCI_REG8(0xb6), 0x20 }, + { CCI_REG8(0xbb), 0x80 }, + { CCI_REG8(0xfd), 0x00 }, + { CCI_REG8(0xa0), 0x00 }, //disable MIPI + { CCI_REG8(0xfd), 0x01 }, + { CCI_REG8(0x33), 0x03 }, + { CCI_REG8(0x01), 0x02 }, + { CCI_REG8(0xfd), 0x00 }, + { CCI_REG8(0x20), 0x1f }, + { CCI_REG8(0xfd), 0x01 }, +}; + +static const struct ov05c10_mode supported_modes[] = { + { + .width = 2800, + .height = 1576, + .hts = 758, + .vts_def = 1978, + .vts_min = 1978, + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .fps = 30, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_2800_1576_30fps), + .regs = mode_2800_1576_30fps, + }, + .link_freq_index = 0, + }, +}; + +struct ov05c10 { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *analogue_gain; + struct v4l2_ctrl *digital_gain; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + + struct regmap *regmap; + unsigned long link_freq_bitmap; + const struct ov05c10_mode *cur_mode; + + struct clk *img_clk; + struct regulator *avdd; + struct gpio_desc *reset; + struct gpio_desc *pwdn_gpio; +}; + +static int ov05c10_test_pattern(struct ov05c10 *ov05c10, u32 pattern_menu) +{ + int ret; + + ret = cci_write(ov05c10->regmap, REG_PAGE_FLAG, 0x04, NULL); + if (ret) + return ret; + if (pattern_menu) + return cci_multi_reg_write(ov05c10->regmap, + ov05c10_test_enable, + ARRAY_SIZE(ov05c10_test_enable), + NULL); + else + return cci_multi_reg_write(ov05c10->regmap, + ov05c10_test_disable, + ARRAY_SIZE(ov05c10_test_disable), + NULL); +} + +static int ov05c10_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov05c10 *ov05c10 = + container_of(ctrl->handler, struct ov05c10, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&ov05c10->sd); + struct v4l2_subdev_state *state; + const struct v4l2_mbus_framefmt *format; + s64 exposure_max; + int ret; + + state = v4l2_subdev_get_locked_active_state(&ov05c10->sd); + format = v4l2_subdev_state_get_format(state, 0); + + /* Propagate change of current control to all related controls */ + if (ctrl->id == V4L2_CID_VBLANK) { + /* Update max exposure while meeting expected vblanking */ + exposure_max = + format->height + ctrl->val - OV05C10_EXPOSURE_MARGIN; + __v4l2_ctrl_modify_range(ov05c10->exposure, + ov05c10->exposure->minimum, exposure_max, + ov05c10->exposure->step, + ov05c10->cur_mode->height - OV05C10_EXPOSURE_MARGIN); + } + /* V4L2 controls values will be applied only when power is already up */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + ret = cci_write(ov05c10->regmap, REG_PAGE_FLAG, PAGE_1, NULL); + if (ret) { + dev_err(&client->dev, "failed to set ctrl"); + goto err; + } + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = cci_write(ov05c10->regmap, REG_ANALOG_GAIN, + ctrl->val, NULL); + break; + case V4L2_CID_DIGITAL_GAIN: + ret = cci_write(ov05c10->regmap, REG_DIGITAL_GAIN, + ctrl->val, NULL); + break; + case V4L2_CID_EXPOSURE: + ret = cci_write(ov05c10->regmap, REG_EXPOSURE, + ctrl->val, NULL); + break; + case V4L2_CID_VBLANK: + ctrl->val = ctrl->val - + (OV05C10_DEFAULT_VTS - ov05c10->cur_mode->height); + ret = cci_write(ov05c10->regmap, REG_DUMMY_LINE, + ctrl->val, NULL); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov05c10_test_pattern(ov05c10, ctrl->val); + break; + default: + ret = -EINVAL; + break; + } + + ret = cci_multi_reg_write(ov05c10->regmap, ov05c10_trigger, + ARRAY_SIZE(ov05c10_trigger), NULL); + if (ret) { + dev_err(&client->dev, "failed to trigger write"); + goto err; + } + +err: + pm_runtime_put(&client->dev); + return ret; +} + +static const struct v4l2_ctrl_ops ov05c10_ctrl_ops = { + .s_ctrl = ov05c10_set_ctrl, +}; + +static int ov05c10_init_controls(struct ov05c10 *ov05c10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov05c10->sd); + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl_handler *ctrl_hdlr; + s64 exposure_max, vblank_max, vblank_min, vblank_def, hblank; + int ret; + + ctrl_hdlr = &ov05c10->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + if (ret) + return ret; + + ov05c10->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, + &ov05c10_ctrl_ops, V4L2_CID_LINK_FREQ, + ARRAY_SIZE(ov05c10_link_freq_menu_items) - 1, 0, + ov05c10_link_freq_menu_items); + if (ov05c10->link_freq) + ov05c10->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank_min = ov05c10->cur_mode->vts_min - ov05c10->cur_mode->height; + vblank_max = OV05C10_VTS_MAX - ov05c10->cur_mode->height; + vblank_def = ov05c10->cur_mode->vts_def - ov05c10->cur_mode->height; + ov05c10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops, + V4L2_CID_VBLANK, vblank_min, + vblank_max, 1, vblank_def); + + hblank = OV05C10_PPL - ov05c10->cur_mode->width; + ov05c10->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops, + V4L2_CID_HBLANK, hblank, hblank, 1, + hblank); + if (ov05c10->hblank) + ov05c10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + MIN_ANA_GAIN, MAX_ANA_GAIN, OV05C10_ANAL_GAIN_STEP, + OV05C10_ANAL_GAIN_DEFAULT); + v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + MIN_DIG_GAIN, MAX_DIG_GAIN, OV05C10_DGTL_GAIN_STEP, + OV05C10_DGTL_GAIN_DEFAULT); + + exposure_max = ov05c10->cur_mode->vts_def - OV05C10_EXPOSURE_MARGIN; + ov05c10->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops, + V4L2_CID_EXPOSURE, + OV05C10_EXPOSURE_MIN, + exposure_max, + OV05C10_EXPOSURE_STEP, + exposure_max); + + ov05c10->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov05c10_ctrl_ops, + V4L2_CID_PIXEL_RATE, + PIXEL_RATE, + PIXEL_RATE, 1, + PIXEL_RATE); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov05c10_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov05c10_test_pattern_menu) - 1, + 0, 0, ov05c10_test_pattern_menu); + + if (ctrl_hdlr->error) + return ctrl_hdlr->error; + + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + return ret; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &ov05c10_ctrl_ops, + &props); + if (ret) + return ret; + + ov05c10->sd.ctrl_handler = ctrl_hdlr; + return 0; +} + +static int ov05c10_start_streaming(struct ov05c10 *ov05c10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov05c10->sd); + const struct ov05c10_reg_list *reg_list; + int ret; + + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + return ret; + + reg_list = &ov05c10->cur_mode->reg_list; + ret = cci_multi_reg_write(ov05c10->regmap, reg_list->regs, + reg_list->num_of_regs, NULL); + if (ret) { + dev_err(&client->dev, "failed to set mode"); + goto err_rpm_put; + } + + ret = __v4l2_ctrl_handler_setup(ov05c10->sd.ctrl_handler); + if (ret) + goto err_rpm_put; + + ret = cci_multi_reg_write(ov05c10->regmap, ov05c10_streaming, + ARRAY_SIZE(ov05c10_streaming), NULL); + if (ret) { + dev_err(&client->dev, "failed to start stream"); + goto err_rpm_put; + } + + return 0; + +err_rpm_put: + pm_runtime_put(&client->dev); + return ret; +} + +static int ov05c10_stop_streaming(struct ov05c10 *ov05c10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov05c10->sd); + int ret; + + ret = cci_multi_reg_write(ov05c10->regmap, ov05c10_soft_standby, + ARRAY_SIZE(ov05c10_soft_standby), NULL); + if (ret < 0) + dev_err(&client->dev, "failed to stop stream"); + + pm_runtime_put(&client->dev); + return ret; +} + +static int ov05c10_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct ov05c10 *ov05c10 = to_ov05c10(sd); + struct v4l2_subdev_state *state; + int ret; + + state = v4l2_subdev_lock_and_get_active_state(sd); + if (enable) + ret = ov05c10_start_streaming(ov05c10); + else + ret = ov05c10_stop_streaming(ov05c10); + + v4l2_subdev_unlock_state(state); + return ret; +} + +static void ov05c10_update_pad_format(const struct ov05c10_mode *mode, + struct v4l2_mbus_framefmt *fmt) +{ + fmt->width = mode->width; + fmt->height = mode->height; + fmt->code = mode->code; + fmt->field = V4L2_FIELD_NONE; +} + +static int ov05c10_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct ov05c10 *ov05c10 = to_ov05c10(sd); + struct i2c_client *client = v4l2_get_subdevdata(&ov05c10->sd); + const struct ov05c10_mode *mode; + s64 hblank, exposure_max; + int ret; + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, + fmt->format.height); + + ov05c10_update_pad_format(mode, &fmt->format); + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + ov05c10->cur_mode = mode; + ret = __v4l2_ctrl_s_ctrl(ov05c10->link_freq, mode->link_freq_index); + if (ret) { + dev_err(&client->dev, "Link Freq ctrl set failed\n"); + return ret; + } + + hblank = OV05C10_PPL - ov05c10->cur_mode->width; + ret = __v4l2_ctrl_modify_range(ov05c10->hblank, hblank, hblank, + 1, hblank); + if (ret) { + dev_err(&client->dev, "HB ctrl range update failed\n"); + return ret; + } + + /* Update limits and set FPS to default */ + ret = __v4l2_ctrl_modify_range(ov05c10->vblank, + mode->vts_min - mode->height, + OV05C10_VTS_MAX - mode->height, 1, + mode->vts_def - mode->height); + if (ret) { + dev_err(&client->dev, "VB ctrl range update failed\n"); + return ret; + } + + ret = __v4l2_ctrl_s_ctrl(ov05c10->vblank, mode->vts_def - mode->height); + if (ret) { + dev_err(&client->dev, "VB ctrl set failed\n"); + return ret; + } + + exposure_max = mode->vts_def - OV05C10_EXPOSURE_MARGIN; + ret = __v4l2_ctrl_modify_range(ov05c10->exposure, OV05C10_EXPOSURE_MIN, + exposure_max, OV05C10_EXPOSURE_STEP, + exposure_max); + if (ret) { + dev_err(&client->dev, "exposure ctrl range update failed\n"); + return ret; + } + + return 0; +} + +static int ov05c10_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 = MEDIA_BUS_FMT_SGRBG10_1X10; + return 0; +} + +static int ov05c10_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static int ov05c10_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 = OV05C10_NATIVE_WIDTH; + sel->r.height = OV05C10_NATIVE_HEIGHT; + break; + default: + return -EINVAL; + } + return 0; +} + +static int ov05c10_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = 0, + .format = { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .width = 2800, + .height = 1576, + }, + }; + + ov05c10_set_format(sd, sd_state, &fmt); + return 0; +} + +static const struct v4l2_subdev_video_ops ov05c10_video_ops = { + .s_stream = ov05c10_s_stream, +}; +static const struct v4l2_subdev_pad_ops ov05c10_pad_ops = { + .set_fmt = ov05c10_set_format, + .get_fmt = v4l2_subdev_get_fmt, + .enum_mbus_code = ov05c10_enum_mbus_code, + .enum_frame_size = ov05c10_enum_frame_size, + .get_selection = ov05c10_get_selection, +}; +static const struct v4l2_subdev_core_ops ov05c10_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; +static const struct v4l2_subdev_ops ov05c10_subdev_ops = { + .core = &ov05c10_core_ops, + .video = &ov05c10_video_ops, + .pad = &ov05c10_pad_ops, +}; +static const struct media_entity_operations ov05c10_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; +static const struct v4l2_subdev_internal_ops ov05c10_internal_ops = { + .init_state = ov05c10_init_state, +}; + +static int ov05c10_parse_fwnode(struct ov05c10 *ov05c10, struct device *dev) +{ + struct fwnode_handle *endpoint; + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + int ret; + unsigned int i, j; + + if (!fwnode) + return -ENXIO; + + endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!endpoint) { + dev_err(dev, "endpoint node not found\n"); + return -EPROBE_DEFER; + } + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg); + fwnode_handle_put(endpoint); + if (ret) { + dev_err(dev, "parsing endpoint node failed\n"); + goto out_err; + } + + if (!bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequencies defined"); + ret = -EINVAL; + goto out_err; + } + + for (i = 0; i < ARRAY_SIZE(ov05c10_link_freq_menu_items); i++) { + for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { + if (ov05c10_link_freq_menu_items[i] == + bus_cfg.link_frequencies[j]) + break; + } + + if (j == bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "no link frequency %lld supported", + ov05c10_link_freq_menu_items[i]); + ret = -EINVAL; + goto out_err; + } + } + +out_err: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int ov05c10_identify_module(struct ov05c10 *ov05c10) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov05c10->sd); + int ret; + u64 val_h, val_l; + u32 val; + + ret = cci_write(ov05c10->regmap, REG_PAGE_FLAG, PAGE_0, NULL); + if (ret) { + dev_err(&client->dev, "chip page write err"); + return ret; + } + + ret = cci_read(ov05c10->regmap, OV05C10_REG_CHIP_ID_H, &val_h, NULL); + if (ret) + return ret; + ret = cci_read(ov05c10->regmap, OV05C10_REG_CHIP_ID_L, &val_l, NULL); + if (ret) + return ret; + + val = ((val_h << 16) | val_l); + if (val != OV05C10_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%u", + OV05C10_CHIP_ID, val); + return -ENXIO; + } + return 0; +} + +/* This function tries to get power control resources */ +static int ov05c10_get_pm_resources(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov05c10 *ov05c10 = to_ov05c10(sd); + int ret; + + ov05c10->img_clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(ov05c10->img_clk)) + return dev_err_probe(dev, PTR_ERR(ov05c10->img_clk), + "failed to get imaging clock\n"); + + ov05c10->avdd = devm_regulator_get_optional(dev, "avdd"); + if (IS_ERR(ov05c10->avdd)) { + ret = PTR_ERR(ov05c10->avdd); + ov05c10->avdd = NULL; + if (ret != -ENODEV) + return dev_err_probe(dev, ret, + "failed to get avdd regulator\n"); + } + + ov05c10->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ov05c10->reset)) + return dev_err_probe(dev, PTR_ERR(ov05c10->reset), + "failed to get reset gpio\n"); + + return 0; +} + +static int ov05c10_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov05c10 *ov05c10 = to_ov05c10(sd); + + if (ov05c10->reset) + gpiod_set_value_cansleep(ov05c10->reset, 1); + + if (ov05c10->avdd) + regulator_disable(ov05c10->avdd); + + clk_disable_unprepare(ov05c10->img_clk); + + return 0; +} + +static int ov05c10_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct ov05c10 *ov05c10 = to_ov05c10(sd); + int ret; + + ret = clk_prepare_enable(ov05c10->img_clk); + if (ret < 0) { + dev_err(dev, "failed to enable imaging clock: %d", ret); + return ret; + } + + if (ov05c10->avdd) { + ret = regulator_enable(ov05c10->avdd); + if (ret < 0) { + dev_err(dev, "failed to enable avdd: %d", ret); + clk_disable_unprepare(ov05c10->img_clk); + return ret; + } + } + + if (ov05c10->reset) { + gpiod_set_value_cansleep(ov05c10->reset, 0); + /* 5ms to wait ready after XSHUTDN assert */ + usleep_range(5000, 5500); + } + + return 0; +} + +static const struct dev_pm_ops ov05c10_pm_ops = { + SET_RUNTIME_PM_OPS(ov05c10_power_off, ov05c10_power_on, NULL) +}; + +static int ov05c10_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct ov05c10 *ov05c10; + bool full_power; + int ret; + + ov05c10 = devm_kzalloc(&client->dev, sizeof(*ov05c10), GFP_KERNEL); + if (!ov05c10) + return -ENOMEM; + ret = ov05c10_parse_fwnode(ov05c10, dev); + if (ret) + return ret; + + ov05c10->regmap = devm_cci_regmap_init_i2c(client, 8); + if (IS_ERR(ov05c10->regmap)) + return dev_err_probe(dev, PTR_ERR(ov05c10->regmap), + "failed to init CCI\n"); + v4l2_i2c_subdev_init(&ov05c10->sd, client, &ov05c10_subdev_ops); + + ret = ov05c10_get_pm_resources(dev); + if (ret) + return ret; + + full_power = acpi_dev_state_d0(dev); + if (full_power) { + ret = ov05c10_power_on(dev); + if (ret) { + dev_err(&client->dev, "failed to power on\n"); + return ret; + } + + /* Check module identity */ + ret = ov05c10_identify_module(ov05c10); + if (ret) { + dev_err(dev, "failed to find sensor: %d\n", ret); + goto error_power_off; + } + } + + ov05c10->cur_mode = &supported_modes[0]; + ret = ov05c10_init_controls(ov05c10); + if (ret) { + dev_err(&client->dev, "failed to init controls: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ov05c10->sd.internal_ops = &ov05c10_internal_ops; + ov05c10->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + ov05c10->sd.entity.ops = &ov05c10_subdev_entity_ops; + ov05c10->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ov05c10->pad.flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&ov05c10->sd.entity, 1, &ov05c10->pad); + if (ret) { + dev_err(&client->dev, "failed to init entity pads: %d", ret); + goto probe_error_v4l2_ctrl_handler_free; + } + + ov05c10->sd.state_lock = ov05c10->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&ov05c10->sd); + if (ret < 0) { + dev_err(dev, "v4l2 subdev init error: %d\n", ret); + goto probe_error_media_entity_cleanup; + } + /* + * Device is already turned on by i2c-core with ACPI domain PM. + * Enable runtime PM and turn off the device. + */ + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_idle(&client->dev); + ret = v4l2_async_register_subdev_sensor(&ov05c10->sd); + if (ret < 0) { + dev_err(&client->dev, "failed to register V4L2 subdev: %d", + ret); + goto probe_error_rpm; + } + return 0; + +probe_error_rpm: + pm_runtime_disable(&client->dev); + v4l2_subdev_cleanup(&ov05c10->sd); +probe_error_media_entity_cleanup: + media_entity_cleanup(&ov05c10->sd.entity); +probe_error_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(ov05c10->sd.ctrl_handler); +error_power_off: + ov05c10_power_off(&client->dev); + + return ret; +} + +static void ov05c10_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov05c10 *ov05c10 = to_ov05c10(sd); + + v4l2_async_unregister_subdev(&ov05c10->sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&ov05c10->sd.entity); + v4l2_ctrl_handler_free(&ov05c10->ctrl_handler); + pm_runtime_disable(&client->dev); + + if (!pm_runtime_status_suspended(&client->dev)) + ov05c10_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); +} + +static const struct acpi_device_id ov05c10_acpi_ids[] = { + { "OVTI05C1" }, + {} +}; + +MODULE_DEVICE_TABLE(acpi, ov05c10_acpi_ids); + +static struct i2c_driver ov05c10_i2c_driver = { + .driver = { + .name = "ov05c10", + .pm = pm_ptr(&ov05c10_pm_ops), + .acpi_match_table = ACPI_PTR(ov05c10_acpi_ids), + }, + .probe = ov05c10_probe, + .remove = ov05c10_remove, +}; + +module_i2c_driver(ov05c10_i2c_driver); +MODULE_DESCRIPTION("OmniVision ov05c10 camera driver"); +MODULE_AUTHOR("Dongcheng Yan "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov08a10.c b/drivers/media/i2c/ov08a10.c index 6a12f07b1fe7..7df05acd8f5d 100644 --- a/drivers/media/i2c/ov08a10.c +++ b/drivers/media/i2c/ov08a10.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/media/pci/intel/ipu-buttress.c b/drivers/media/pci/intel/ipu-buttress.c index 53c39bccc8b3..938ec3ac9312 100644 --- a/drivers/media/pci/intel/ipu-buttress.c +++ b/drivers/media/pci/intel/ipu-buttress.c @@ -1106,16 +1106,30 @@ static int ipu_buttress_tsc_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(ipu_buttress_tsc_fops, ipu_buttress_tsc_get, NULL, "%llu\n"); -static int ipu_buttress_psys_force_freq_get(void *data, u64 *val) +static int ipu_buttress_psys_freq_get(void *data, u64 *val) { struct ipu_device *isp = data; + u32 reg_val; + int rval; + + rval = pm_runtime_get_sync(&isp->psys->dev); + if (rval < 0) { + pm_runtime_put(&isp->psys->dev); + dev_err(&isp->pdev->dev, "Runtime PM failed (%d)\n", rval); + return rval; + } - *val = isp->buttress.psys_force_ratio * BUTTRESS_PS_FREQ_STEP; + reg_val = readl(isp->base + BUTTRESS_REG_PS_FREQ_CTL); + + pm_runtime_put(&isp->psys->dev); + + *val = IPU_PS_FREQ_RATIO_BASE * + (reg_val & IPU_BUTTRESS_PS_FREQ_CTL_DIVISOR_MASK); return 0; } -static int ipu_buttress_psys_force_freq_set(void *data, u64 val) +static int ipu_buttress_psys_freq_set(void *data, u64 val) { struct ipu_device *isp = data; @@ -1159,12 +1173,9 @@ static int ipu_buttress_isys_freq_get(void *data, u64 *val) return 0; } -DEFINE_SIMPLE_ATTRIBUTE(ipu_buttress_psys_force_freq_fops, - ipu_buttress_psys_force_freq_get, - ipu_buttress_psys_force_freq_set, "%llu\n"); - DEFINE_SIMPLE_ATTRIBUTE(ipu_buttress_psys_freq_fops, - ipu_buttress_psys_freq_get, NULL, "%llu\n"); + ipu_buttress_psys_freq_get, + ipu_buttress_psys_freq_set, "%llu\n"); DEFINE_SIMPLE_ATTRIBUTE(ipu_buttress_isys_freq_fops, ipu_buttress_isys_freq_get, @@ -1203,12 +1214,7 @@ int ipu_buttress_debugfs_init(struct ipu_device *isp) &ipu_buttress_tsc_fops); if (!file) goto err; - file = debugfs_create_file("psys_force_freq", 0700, dir, isp, - &ipu_buttress_psys_force_freq_fops); - if (!file) - goto err; - - file = debugfs_create_file("psys_freq", 0400, dir, isp, + file = debugfs_create_file("psys_freq", 0700, dir, isp, &ipu_buttress_psys_freq_fops); if (!file) goto err; diff --git a/drivers/media/pci/intel/ipu-buttress.h b/drivers/media/pci/intel/ipu-buttress.h index 520db8b01c3c..2c60641f696d 100644 --- a/drivers/media/pci/intel/ipu-buttress.h +++ b/drivers/media/pci/intel/ipu-buttress.h @@ -126,5 +126,4 @@ void ipu_buttress_csi_port_config(struct ipu_device *isp, int ipu_buttress_restore(struct ipu_device *isp); int ipu_buttress_isys_freq_set(void *data, u64 val); -int ipu_buttress_psys_freq_get(void *data, u64 *val); #endif /* IPU_BUTTRESS_H */ diff --git a/drivers/media/pci/intel/ipu-dma.c b/drivers/media/pci/intel/ipu-dma.c index f1dfd896b581..4aa97b8436ee 100644 --- a/drivers/media/pci/intel/ipu-dma.c +++ b/drivers/media/pci/intel/ipu-dma.c @@ -322,6 +322,7 @@ static void ipu_dma_free(struct device *dev, size_t size, void *vaddr, kfree(info); } +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) static int ipu_dma_mmap(struct device *dev, struct vm_area_struct *vma, void *addr, dma_addr_t iova, size_t size, @@ -359,6 +360,33 @@ static int ipu_dma_mmap(struct device *dev, struct vm_area_struct *vma, return 0; } +#else + +static int ipu_dma_mmap(struct device *dev, struct vm_area_struct *vma, + void *addr, dma_addr_t iova, size_t size, + unsigned long attrs) +{ + struct ipu_mmu *mmu = to_ipu_bus_device(dev)->mmu; + struct vm_info *info; + size_t count = PAGE_ALIGN(size) >> PAGE_SHIFT; + + info = get_vm_info(mmu, iova); + if (!info) + return -EFAULT; + + if (!info->vaddr) + return -EFAULT; + + if (vma->vm_start & ~PAGE_MASK) + return -EINVAL; + + if (size > info->size) + return -EFAULT; + + return vm_insert_pages(vma, vma->vm_start, info->pages, &count); +} + +#endif static void ipu_dma_unmap_sg(struct device *dev, struct scatterlist *sglist, diff --git a/drivers/media/pci/intel/ipu-fw-isys.c b/drivers/media/pci/intel/ipu-fw-isys.c index 7870fae557de..1aaa662fd040 100644 --- a/drivers/media/pci/intel/ipu-fw-isys.c +++ b/drivers/media/pci/intel/ipu-fw-isys.c @@ -173,8 +173,8 @@ ipu_fw_isys_complex_cmd(struct ipu_isys *isys, if (send_type >= N_IPU_FW_ISYS_SEND_TYPE) return -EINVAL; - dev_dbg(&isys->adev->dev, "send_token: %s\n", - send_msg_types[send_type]); + dev_dbg(&isys->adev->dev, "send_token: %s, stream_handle: %u\n", + send_msg_types[send_type], stream_handle); /* * Time to flush cache in case we have some payload. Not all messages diff --git a/drivers/media/pci/intel/ipu-isys-csi2-be-soc.c b/drivers/media/pci/intel/ipu-isys-csi2-be-soc.c index c7d630d294e6..83040911cf63 100644 --- a/drivers/media/pci/intel/ipu-isys-csi2-be-soc.c +++ b/drivers/media/pci/intel/ipu-isys-csi2-be-soc.c @@ -109,9 +109,10 @@ __subdev_link_validate(struct v4l2_subdev *sd, struct media_link *link, struct v4l2_subdev_format *source_fmt, struct v4l2_subdev_format *sink_fmt) { - struct ipu_isys_pipeline *ip = - container_of(media_entity_pipeline(&sd->entity), - struct ipu_isys_pipeline, pipe); + struct media_pipeline *mp = media_entity_pipeline(&sd->entity); + struct ipu_isys_pipeline *ip = container_of(mp, + struct ipu_isys_pipeline, + pipe); ip->csi2_be_soc = to_ipu_isys_csi2_be_soc(sd); return ipu_isys_subdev_link_validate(sd, link, source_fmt, sink_fmt); diff --git a/drivers/media/pci/intel/ipu-isys-csi2-be.c b/drivers/media/pci/intel/ipu-isys-csi2-be.c index dbbf4d889a98..d5d0b33efa7d 100644 --- a/drivers/media/pci/intel/ipu-isys-csi2-be.c +++ b/drivers/media/pci/intel/ipu-isys-csi2-be.c @@ -73,9 +73,10 @@ static int __subdev_link_validate(struct v4l2_subdev *sd, struct v4l2_subdev_format *source_fmt, struct v4l2_subdev_format *sink_fmt) { - struct ipu_isys_pipeline *ip = - container_of(media_entity_pipeline(&sd->entity), - struct ipu_isys_pipeline, pipe); + struct media_pipeline *mp = media_entity_pipeline(&sd->entity); + struct ipu_isys_pipeline *ip = container_of(mp, + struct ipu_isys_pipeline, + pipe); ip->csi2_be = to_ipu_isys_csi2_be(sd); return ipu_isys_subdev_link_validate(sd, link, source_fmt, sink_fmt); diff --git a/drivers/media/pci/intel/ipu-isys-csi2.c b/drivers/media/pci/intel/ipu-isys-csi2.c index 1530f50baf51..234cd8b4c5a9 100644 --- a/drivers/media/pci/intel/ipu-isys-csi2.c +++ b/drivers/media/pci/intel/ipu-isys-csi2.c @@ -84,9 +84,10 @@ static struct v4l2_subdev_internal_ops csi2_sd_internal_ops = { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 255) int ipu_isys_csi2_get_link_freq(struct ipu_isys_csi2 *csi2, s64 *link_freq) { - struct ipu_isys_pipeline *pipe = - container_of(media_entity_pipeline(&csi2->asd.sd.entity), - struct ipu_isys_pipeline, pipe); + struct media_pipeline *mp = media_entity_pipeline(&csi2->asd.sd.entity); + struct ipu_isys_pipeline *pipe = container_of(mp, + struct ipu_isys_pipeline, + pipe); struct v4l2_subdev *ext_sd = media_entity_to_v4l2_subdev(pipe->external->entity); struct device *dev = &csi2->isys->adev->dev; @@ -115,9 +116,10 @@ int ipu_isys_csi2_get_link_freq(struct ipu_isys_csi2 *csi2, s64 *link_freq) #else int ipu_isys_csi2_get_link_freq(struct ipu_isys_csi2 *csi2, __s64 *link_freq) { - struct ipu_isys_pipeline *pipe = - container_of(media_entity_pipeline(&csi2->asd.sd.entity), - struct ipu_isys_pipeline, pipe); + struct media_pipeline *mp = media_entity_pipeline(&csi2->asd.sd.entity); + struct ipu_isys_pipeline *pipe = container_of(mp, + struct ipu_isys_pipeline, + pipe); struct v4l2_subdev *ext_sd = media_entity_to_v4l2_subdev(pipe->external->entity); struct v4l2_ext_control c = {.id = V4L2_CID_LINK_FREQ, }; @@ -265,8 +267,10 @@ ipu_isys_csi2_calc_timing(struct ipu_isys_csi2 *csi2, static int set_stream(struct v4l2_subdev *sd, int enable) { struct ipu_isys_csi2 *csi2 = to_ipu_isys_csi2(sd); - struct ipu_isys_pipeline *ip = - to_ipu_isys_pipeline(media_entity_pipeline(&sd->entity)); + struct media_pipeline *mp = media_entity_pipeline(&sd->entity); + struct ipu_isys_pipeline *ip = container_of(mp, + struct ipu_isys_pipeline, + pipe); struct ipu_isys_csi2_config *cfg; struct v4l2_subdev *ext_sd; struct ipu_isys_csi2_timing timing = {0}; @@ -322,20 +326,17 @@ static void csi2_capture_done(struct ipu_isys_pipeline *ip, static int csi2_link_validate(struct media_link *link) { - struct media_pipeline *media_pipe; struct ipu_isys_csi2 *csi2; struct ipu_isys_pipeline *ip; + struct media_pipeline *mp; int rval; - if (!link->sink->entity || !link->source->entity) - return -EINVAL; - media_pipe = media_entity_pipeline(link->sink->entity); - if (!media_pipe) + mp = media_entity_pipeline(link->sink->entity); + if (!link->sink->entity || !link->source->entity || !mp) return -EINVAL; csi2 = to_ipu_isys_csi2(media_entity_to_v4l2_subdev(link->sink->entity)); - - ip = to_ipu_isys_pipeline(media_pipe); + ip = to_ipu_isys_pipeline(mp); csi2->receiver_errors = 0; ip->csi2 = csi2; ipu_isys_video_add_capture_done(ip, csi2_capture_done); @@ -404,8 +405,10 @@ static int __subdev_link_validate(struct v4l2_subdev *sd, struct v4l2_subdev_format *source_fmt, struct v4l2_subdev_format *sink_fmt) { - struct ipu_isys_pipeline *ip = - to_ipu_isys_pipeline(media_entity_pipeline(&sd->entity)); + struct media_pipeline *mp = media_entity_pipeline(&sd->entity); + struct ipu_isys_pipeline *ip = container_of(mp, + struct ipu_isys_pipeline, + pipe); if (source_fmt->format.field == V4L2_FIELD_ALTERNATE) ip->interlaced = true; diff --git a/drivers/media/pci/intel/ipu-isys-queue.c b/drivers/media/pci/intel/ipu-isys-queue.c index 486464b5eba2..a9ce27e76855 100644 --- a/drivers/media/pci/intel/ipu-isys-queue.c +++ b/drivers/media/pci/intel/ipu-isys-queue.c @@ -532,9 +532,8 @@ static void buf_queue(struct vb2_buffer *vb) struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(vb->vb2_queue); struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); struct ipu_isys_buffer *ib = vb2_buffer_to_ipu_isys_buffer(vb); - struct media_pipeline *media_pipe = - media_entity_pipeline(&av->vdev.entity); - struct ipu_isys_pipeline *ip = to_ipu_isys_pipeline(media_pipe); + struct media_pipeline *mp = media_entity_pipeline(&av->vdev.entity); + struct ipu_isys_pipeline *ip = to_ipu_isys_pipeline(mp); struct ipu_isys_buffer_list bl; struct ipu_fw_isys_frame_buff_set_abi *buf = NULL; @@ -566,8 +565,7 @@ static void buf_queue(struct vb2_buffer *vb) if (ib->req) return; - if (!pipe_av || !media_pipe || - !vb->vb2_queue->start_streaming_called) { + if (!pipe_av || !mp || !vb->vb2_queue->start_streaming_called) { dev_dbg(&av->isys->adev->dev, "no pipe or streaming, adding to incoming\n"); return; @@ -860,8 +858,8 @@ static void stop_streaming(struct vb2_queue *q) { struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(q); struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); - struct ipu_isys_pipeline *ip = - to_ipu_isys_pipeline(media_entity_pipeline(&av->vdev.entity)); + struct media_pipeline *mp = media_entity_pipeline(&av->vdev.entity); + struct ipu_isys_pipeline *ip = to_ipu_isys_pipeline(mp); struct ipu_isys_video *pipe_av = container_of(ip, struct ipu_isys_video, ip); @@ -953,8 +951,8 @@ ipu_isys_buf_calc_sequence_time(struct ipu_isys_buffer *ib, struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(vb->vb2_queue); struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); struct device *dev = &av->isys->adev->dev; - struct ipu_isys_pipeline *ip = - to_ipu_isys_pipeline(media_entity_pipeline(&av->vdev.entity)); + struct media_pipeline *mp = media_entity_pipeline(&av->vdev.entity); + struct ipu_isys_pipeline *ip = to_ipu_isys_pipeline(mp); u64 ns; u32 sequence; @@ -1052,6 +1050,7 @@ void ipu_isys_queue_buf_ready(struct ipu_isys_pipeline *ip, first = false; continue; } + if (info->error_info.error == IPU_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO) { /* @@ -1073,6 +1072,7 @@ void ipu_isys_queue_buf_ready(struct ipu_isys_pipeline *ip, spin_unlock_irqrestore(&aq->lock, flags); ipu_isys_buf_calc_sequence_time(ib, info); + /* * For interlaced buffers, the notification to user space * is postponed to capture_done event since the field diff --git a/drivers/media/pci/intel/ipu-isys-subdev.c b/drivers/media/pci/intel/ipu-isys-subdev.c index 27c1c4066eb9..f5d869107c13 100644 --- a/drivers/media/pci/intel/ipu-isys-subdev.c +++ b/drivers/media/pci/intel/ipu-isys-subdev.c @@ -708,8 +708,10 @@ int ipu_isys_subdev_link_validate(struct v4l2_subdev *sd, { struct v4l2_subdev *source_sd = media_entity_to_v4l2_subdev(link->source->entity); - struct ipu_isys_pipeline *ip = - to_ipu_isys_pipeline(media_entity_pipeline(&sd->entity)); + struct media_pipeline *mp = media_entity_pipeline(&sd->entity); + struct ipu_isys_pipeline *ip = container_of(mp, + struct ipu_isys_pipeline, + pipe); struct ipu_isys_subdev *asd = to_ipu_isys_subdev(sd); if (!source_sd) @@ -803,7 +805,7 @@ int ipu_isys_subdev_init(struct ipu_isys_subdev *asd, int src_pad_idx, int sink_pad_idx) { - int i; + unsigned int i; int rval = -EINVAL; mutex_init(&asd->mutex); diff --git a/drivers/media/pci/intel/ipu-isys-video.c b/drivers/media/pci/intel/ipu-isys-video.c index 61476c2b556b..92b369d7df9e 100644 --- a/drivers/media/pci/intel/ipu-isys-video.c +++ b/drivers/media/pci/intel/ipu-isys-video.c @@ -632,11 +632,9 @@ static int link_validate(struct media_link *link) { struct ipu_isys_video *av = container_of(link->sink, struct ipu_isys_video, pad); - /* pipe not ready, do not use media_entity_pipeline() to get pipe */ struct ipu_isys_pipeline *ip = &av->ip; struct v4l2_subdev *sd; - WARN_ON(!ip); if (!link->source->entity) return -EINVAL; @@ -696,8 +694,8 @@ static void put_stream_opened(struct ipu_isys_video *av) static int get_stream_handle(struct ipu_isys_video *av) { - struct ipu_isys_pipeline *ip = - to_ipu_isys_pipeline(media_entity_pipeline(&av->vdev.entity)); + struct media_pipeline *mp = media_entity_pipeline(&av->vdev.entity); + struct ipu_isys_pipeline *ip = to_ipu_isys_pipeline(mp); unsigned int stream_handle; unsigned long flags; @@ -718,8 +716,8 @@ static int get_stream_handle(struct ipu_isys_video *av) static void put_stream_handle(struct ipu_isys_video *av) { - struct ipu_isys_pipeline *ip = - to_ipu_isys_pipeline(media_entity_pipeline(&av->vdev.entity)); + struct media_pipeline *mp = media_entity_pipeline(&av->vdev.entity); + struct ipu_isys_pipeline *ip = to_ipu_isys_pipeline(mp); unsigned long flags; spin_lock_irqsave(&av->isys->lock, flags); @@ -934,8 +932,8 @@ void ipu_isys_prepare_fw_cfg_default(struct ipu_isys_video *av, struct ipu_fw_isys_stream_cfg_data_abi *cfg) { - struct ipu_isys_pipeline *ip = - to_ipu_isys_pipeline(media_entity_pipeline(&av->vdev.entity)); + struct media_pipeline *mp = media_entity_pipeline(&av->vdev.entity); + struct ipu_isys_pipeline *ip = to_ipu_isys_pipeline(mp); struct ipu_isys_queue *aq = &av->aq; struct ipu_fw_isys_output_pin_info_abi *pin_info; struct ipu_isys *isys = av->isys; @@ -1087,8 +1085,8 @@ static unsigned int get_comp_format(u32 code) static int start_stream_firmware(struct ipu_isys_video *av, struct ipu_isys_buffer_list *bl) { - struct ipu_isys_pipeline *ip = - to_ipu_isys_pipeline(media_entity_pipeline(&av->vdev.entity)); + struct media_pipeline *mp = media_entity_pipeline(&av->vdev.entity); + struct ipu_isys_pipeline *ip = to_ipu_isys_pipeline(mp); struct device *dev = &av->isys->adev->dev; struct v4l2_subdev_selection sel_fmt = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, @@ -1292,8 +1290,8 @@ static int start_stream_firmware(struct ipu_isys_video *av, rval = -EIO; goto out_stream_close; } - dev_dbg(dev, "start stream: complete\n"); + return 0; out_stream_close: @@ -1326,8 +1324,8 @@ static int start_stream_firmware(struct ipu_isys_video *av, static void stop_streaming_firmware(struct ipu_isys_video *av) { - struct ipu_isys_pipeline *ip = - to_ipu_isys_pipeline(media_entity_pipeline(&av->vdev.entity)); + struct media_pipeline *mp = media_entity_pipeline(&av->vdev.entity); + struct ipu_isys_pipeline *ip = to_ipu_isys_pipeline(mp); struct device *dev = &av->isys->adev->dev; int rval, tout; enum ipu_fw_isys_send_type send_type = @@ -1355,8 +1353,8 @@ static void stop_streaming_firmware(struct ipu_isys_video *av) static void close_streaming_firmware(struct ipu_isys_video *av) { - struct ipu_isys_pipeline *ip = - to_ipu_isys_pipeline(media_entity_pipeline(&av->vdev.entity)); + struct media_pipeline *mp = media_entity_pipeline(&av->vdev.entity); + struct ipu_isys_pipeline *ip = to_ipu_isys_pipeline(mp); struct device *dev = &av->isys->adev->dev; int rval, tout; @@ -1377,6 +1375,7 @@ static void close_streaming_firmware(struct ipu_isys_video *av) dev_err(dev, "stream close error: %d\n", ip->error); else dev_dbg(dev, "close stream: complete\n"); + put_stream_opened(av); put_stream_handle(av); } @@ -1419,16 +1418,16 @@ int ipu_isys_video_prepare_streaming(struct ipu_isys_video *av, struct media_entity_graph graph; #endif struct media_entity *entity; - struct media_pipeline *media_pipe; struct media_device *mdev = &av->isys->media_dev; + struct media_pipeline *mp; int rval; unsigned int i; dev_dbg(dev, "prepare stream: %d\n", state); if (!state) { - media_pipe = media_entity_pipeline(&av->vdev.entity); - ip = to_ipu_isys_pipeline(media_pipe); + mp = media_entity_pipeline(&av->vdev.entity); + ip = to_ipu_isys_pipeline(mp); if (ip->interlaced && isys->short_packet_source == IPU_ISYS_SHORT_PACKET_FROM_RECEIVER) @@ -1530,12 +1529,13 @@ static void configure_stream_watermark(struct ipu_isys_video *av) int ret = 0; struct v4l2_subdev *esd; struct v4l2_ctrl *ctrl; - struct ipu_isys_pipeline *ip = - to_ipu_isys_pipeline(media_entity_pipeline(&av->vdev.entity)); + struct ipu_isys_pipeline *ip; struct isys_iwake_watermark *iwake_watermark; struct v4l2_control vb = { .id = V4L2_CID_VBLANK, .value = 0 }; struct v4l2_control hb = { .id = V4L2_CID_HBLANK, .value = 0 }; + struct media_pipeline *mp = media_entity_pipeline(&av->vdev.entity); + ip = to_ipu_isys_pipeline(mp); if (!ip->external->entity) { WARN_ON(1); return; @@ -1642,8 +1642,8 @@ int ipu_isys_video_set_streaming(struct ipu_isys_video *av, struct media_entity_enum entities; struct media_entity *entity, *entity2; - struct ipu_isys_pipeline *ip = - to_ipu_isys_pipeline(media_entity_pipeline(&av->vdev.entity)); + struct media_pipeline *mp = media_entity_pipeline(&av->vdev.entity); + struct ipu_isys_pipeline *ip = to_ipu_isys_pipeline(mp); struct v4l2_subdev *sd, *esd; int rval = 0; diff --git a/drivers/media/pci/intel/ipu-isys.c b/drivers/media/pci/intel/ipu-isys.c index 16ee40bd6bf5..e5332165228e 100644 --- a/drivers/media/pci/intel/ipu-isys.c +++ b/drivers/media/pci/intel/ipu-isys.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2013 - 2024 Intel Corporation +#include #include #include #include @@ -13,6 +14,10 @@ #include #include +#if IS_ENABLED(CONFIG_IPU_BRIDGE) && \ +LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0) +#include +#endif #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0) #include @@ -634,6 +639,7 @@ void update_watermark_setting(struct ipu_isys *isys) did = calc_fill_time_us - ltr; ltr_did_type = LTR_IWAKE_ON; } + threshold_bytes = did * isys_pb_datarate_mbs; /* calculate iwake threshold with 2KB granularity pages */ iwake_threshold = @@ -758,10 +764,21 @@ static int isys_notifier_bound(struct v4l2_async_notifier *notifier, struct ipu_isys, notifier); struct sensor_async_sd *s_asd = container_of(asc, struct sensor_async_sd, asc); + int ret; +#if IS_ENABLED(CONFIG_IPU_BRIDGE) + + ret = ipu_bridge_instantiate_vcm(sd->dev); + if (ret) { + dev_err(&isys->adev->dev, "instantiate vcm failed\n"); + return ret; + } +#endif dev_info(&isys->adev->dev, "bind %s nlanes is %d port is %d\n", sd->name, s_asd->csi2.nlanes, s_asd->csi2.port); - isys_complete_ext_device_registration(isys, sd, &s_asd->csi2); + ret = isys_complete_ext_device_registration(isys, sd, &s_asd->csi2); + if (ret) + return ret; return v4l2_device_register_subdev_nodes(&isys->v4l2_dev); } @@ -860,6 +877,7 @@ static int isys_notifier_init(struct ipu_isys *isys) "v4l2 parse_fwnode_endpoints() failed: %d\n", ret); return ret; } + if (list_empty(&isys->notifier.asd_list)) { /* isys probe could continue with async subdevs missing */ dev_warn(&isys->adev->dev, "no subdev found in graph\n"); @@ -1854,3 +1872,6 @@ MODULE_AUTHOR("Yu Xia "); MODULE_AUTHOR("Jerry Hu "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Intel ipu input system driver"); +#if IS_ENABLED(CONFIG_IPU_BRIDGE) +MODULE_IMPORT_NS(INTEL_IPU_BRIDGE); +#endif diff --git a/drivers/media/pci/intel/ipu-mmu.c b/drivers/media/pci/intel/ipu-mmu.c index a36a6ded84eb..ffaa742eb914 100644 --- a/drivers/media/pci/intel/ipu-mmu.c +++ b/drivers/media/pci/intel/ipu-mmu.c @@ -243,8 +243,8 @@ static u32 *alloc_l2_pt(struct ipu_mmu_info *mmu_info) return pt; } -static size_t l2_unmap(struct ipu_mmu_info *mmu_info, unsigned long iova, - phys_addr_t dummy, size_t size); +static void l2_unmap(struct ipu_mmu_info *mmu_info, unsigned long iova, + phys_addr_t dummy, size_t size); static int l2_map(struct ipu_mmu_info *mmu_info, unsigned long iova, phys_addr_t paddr, size_t size) { @@ -256,7 +256,7 @@ static int l2_map(struct ipu_mmu_info *mmu_info, unsigned long iova, unsigned long flags; dma_addr_t dma; unsigned int l2_entries; - size_t mapped_size = 0; + size_t mapped = 0; int err = 0; spin_lock_irqsave(&mmu_info->lock, flags); @@ -274,8 +274,6 @@ static int l2_map(struct ipu_mmu_info *mmu_info, unsigned long iova, if (likely(!l2_virt)) { l2_virt = alloc_l2_pt(mmu_info); if (!l2_virt) { - spin_unlock_irqrestore(&mmu_info->lock, - flags); err = -ENOMEM; goto error; } @@ -285,8 +283,6 @@ static int l2_map(struct ipu_mmu_info *mmu_info, unsigned long iova, if (!dma) { dev_err(dev, "Failed to map l2pt page\n"); free_page((unsigned long)l2_virt); - - spin_unlock_irqrestore(&mmu_info->lock, flags); err = -EINVAL; goto error; } @@ -314,15 +310,15 @@ static int l2_map(struct ipu_mmu_info *mmu_info, unsigned long iova, iova += ISP_PAGE_SIZE; paddr += ISP_PAGE_SIZE; - mapped_size += ISP_PAGE_SIZE; + mapped += ISP_PAGE_SIZE; size -= ISP_PAGE_SIZE; l2_entries++; } - if (l2_entries) - clflush_cache_range(&l2_pt[l2_idx - l2_entries], - sizeof(l2_pt[0]) * l2_entries); + WARN_ON_ONCE(!l2_entries); + clflush_cache_range(&l2_pt[l2_idx - l2_entries], + sizeof(l2_pt[0]) * l2_entries); } spin_unlock_irqrestore(&mmu_info->lock, flags); @@ -330,10 +326,10 @@ static int l2_map(struct ipu_mmu_info *mmu_info, unsigned long iova, return 0; error: + spin_unlock_irqrestore(&mmu_info->lock, flags); /* unroll mapping in case something went wrong */ - if (size && mapped_size) - l2_unmap(mmu_info, iova - mapped_size, paddr - mapped_size, - mapped_size); + if (mapped) + l2_unmap(mmu_info, iova - mapped, paddr - mapped, mapped); return err; } @@ -351,8 +347,8 @@ static int __ipu_mmu_map(struct ipu_mmu_info *mmu_info, unsigned long iova, return l2_map(mmu_info, iova_start, paddr, size); } -static size_t l2_unmap(struct ipu_mmu_info *mmu_info, unsigned long iova, - phys_addr_t dummy, size_t size) +static void l2_unmap(struct ipu_mmu_info *mmu_info, unsigned long iova, + phys_addr_t dummy, size_t size) { u32 l1_idx; u32 *l2_pt; @@ -369,11 +365,10 @@ static size_t l2_unmap(struct ipu_mmu_info *mmu_info, unsigned long iova, l1_idx, iova); if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval) { - spin_unlock_irqrestore(&mmu_info->lock, flags); dev_err(mmu_info->dev, "unmap iova 0x%8.8lx l1 idx %u which was not mapped\n", iova, l1_idx); - return unmapped << ISP_PAGE_SHIFT; + continue; } l2_pt = mmu_info->l2_pts[l1_idx]; @@ -386,24 +381,23 @@ static size_t l2_unmap(struct ipu_mmu_info *mmu_info, unsigned long iova, l2_pt[l2_idx] = mmu_info->dummy_page_pteval; iova += ISP_PAGE_SIZE; + unmapped += ISP_PAGE_SIZE; size -= ISP_PAGE_SIZE; + l2_entries++; } - if (l2_entries) { - clflush_cache_range(&l2_pt[l2_idx - l2_entries], - sizeof(l2_pt[0]) * l2_entries); - unmapped += l2_entries; - } + WARN_ON_ONCE(!l2_entries); + clflush_cache_range(&l2_pt[l2_idx - l2_entries], + sizeof(l2_pt[0]) * l2_entries); } + WARN_ON_ONCE(size); spin_unlock_irqrestore(&mmu_info->lock, flags); - - return unmapped << ISP_PAGE_SHIFT; } -static size_t __ipu_mmu_unmap(struct ipu_mmu_info *mmu_info, - unsigned long iova, size_t size) +static void __ipu_mmu_unmap(struct ipu_mmu_info *mmu_info, + unsigned long iova, size_t size) { return l2_unmap(mmu_info, iova, 0, size); } @@ -659,8 +653,8 @@ phys_addr_t ipu_mmu_iova_to_phys(struct ipu_mmu_info *mmu_info, } /* drivers/iommu/iommu.c:iommu_unmap() */ -size_t ipu_mmu_unmap(struct ipu_mmu_info *mmu_info, unsigned long iova, - size_t size) +void ipu_mmu_unmap(struct ipu_mmu_info *mmu_info, unsigned long iova, + size_t size) { unsigned int min_pagesz; @@ -677,7 +671,7 @@ size_t ipu_mmu_unmap(struct ipu_mmu_info *mmu_info, unsigned long iova, if (!IS_ALIGNED(iova | size, min_pagesz)) { dev_err(NULL, "unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n", iova, size, min_pagesz); - return -EINVAL; + return; } return __ipu_mmu_unmap(mmu_info, iova, size); @@ -750,7 +744,7 @@ static void ipu_mmu_destroy(struct ipu_mmu *mmu) } free_dummy_page(mmu_info); - dma_unmap_single(mmu_info->dev, TBL_PHYS_ADDR(mmu_info->l1_pt_dma), + dma_unmap_single(mmu_info->dev, mmu_info->l1_pt_dma << ISP_PADDR_SHIFT, PAGE_SIZE, DMA_BIDIRECTIONAL); free_page((unsigned long)mmu_info->dummy_l2_pt); free_page((unsigned long)mmu_info->l1_pt); diff --git a/drivers/media/pci/intel/ipu-mmu.h b/drivers/media/pci/intel/ipu-mmu.h index c84d408b6421..111edc4686c5 100644 --- a/drivers/media/pci/intel/ipu-mmu.h +++ b/drivers/media/pci/intel/ipu-mmu.h @@ -69,8 +69,8 @@ int ipu_mmu_hw_init(struct ipu_mmu *mmu); int ipu_mmu_hw_cleanup(struct ipu_mmu *mmu); int ipu_mmu_map(struct ipu_mmu_info *mmu_info, unsigned long iova, phys_addr_t paddr, size_t size); -size_t ipu_mmu_unmap(struct ipu_mmu_info *mmu_info, unsigned long iova, - size_t size); +void ipu_mmu_unmap(struct ipu_mmu_info *mmu_info, unsigned long iova, + size_t size); phys_addr_t ipu_mmu_iova_to_phys(struct ipu_mmu_info *mmu_info, dma_addr_t iova); #endif diff --git a/drivers/media/pci/intel/ipu-psys.c b/drivers/media/pci/intel/ipu-psys.c index b12ecf3fcc38..dc2d689175db 100644 --- a/drivers/media/pci/intel/ipu-psys.c +++ b/drivers/media/pci/intel/ipu-psys.c @@ -48,6 +48,10 @@ MODULE_PARM_DESC(async_fw_init, "Enable asynchronous firmware initialization"); #define IPU_PSYS_NUM_DEVICES 4 +#define IPU_PSYS_MAX_NUM_DESCS 1024 +#define IPU_PSYS_MAX_NUM_BUFS 1024 +#define IPU_PSYS_MAX_NUM_BUFS_LRU 12 + static int psys_runtime_pm_resume(struct device *dev); static int psys_runtime_pm_suspend(struct device *dev); @@ -66,6 +70,81 @@ static struct bus_type ipu_psys_bus = { .name = IPU_PSYS_NAME, }; +/* + * These are some trivial wrappers that save us from open-coding some + * common patterns and also that's were we have some checking (for the + * time being) + */ +static void ipu_desc_add(struct ipu_psys_fh *fh, struct ipu_psys_desc *desc) +{ + fh->num_descs++; + + WARN_ON_ONCE(fh->num_descs >= IPU_PSYS_MAX_NUM_DESCS); + list_add(&desc->list, &fh->descs_list); +} + +static void ipu_desc_del(struct ipu_psys_fh *fh, struct ipu_psys_desc *desc) +{ + fh->num_descs--; + list_del_init(&desc->list); +} + +static void ipu_buffer_add(struct ipu_psys_fh *fh, + struct ipu_psys_kbuffer *kbuf) +{ + fh->num_bufs++; + + WARN_ON_ONCE(fh->num_bufs >= IPU_PSYS_MAX_NUM_BUFS); + list_add(&kbuf->list, &fh->bufs_list); +} + +static void ipu_buffer_del(struct ipu_psys_fh *fh, + struct ipu_psys_kbuffer *kbuf) +{ + fh->num_bufs--; + list_del_init(&kbuf->list); +} + +static void ipu_buffer_lru_add(struct ipu_psys_fh *fh, + struct ipu_psys_kbuffer *kbuf) +{ + fh->num_bufs_lru++; + list_add_tail(&kbuf->list, &fh->bufs_lru); +} + +static void ipu_buffer_lru_del(struct ipu_psys_fh *fh, + struct ipu_psys_kbuffer *kbuf) +{ + fh->num_bufs_lru--; + list_del_init(&kbuf->list); +} + +static struct ipu_psys_kbuffer *ipu_psys_kbuffer_alloc(void) +{ + struct ipu_psys_kbuffer *kbuf; + + kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); + if (!kbuf) + return NULL; + + atomic_set(&kbuf->map_count, 0); + INIT_LIST_HEAD(&kbuf->list); + return kbuf; +} + +static struct ipu_psys_desc *ipu_psys_desc_alloc(int fd) +{ + struct ipu_psys_desc *desc; + + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) + return NULL; + + desc->fd = fd; + INIT_LIST_HEAD(&desc->list); + return desc; +} + struct ipu_psys_pg *__get_pg_buf(struct ipu_psys *psys, size_t pg_size) { struct ipu_psys_pg *kpg; @@ -101,26 +180,77 @@ struct ipu_psys_pg *__get_pg_buf(struct ipu_psys *psys, size_t pg_size) return kpg; } -static int ipu_psys_unmapbuf_locked(int fd, struct ipu_psys_fh *fh, - struct ipu_psys_kbuffer *kbuf); -struct ipu_psys_kbuffer *ipu_psys_lookup_kbuffer(struct ipu_psys_fh *fh, int fd) +static struct ipu_psys_desc *psys_desc_lookup(struct ipu_psys_fh *fh, int fd) +{ + struct ipu_psys_desc *desc; + + list_for_each_entry(desc, &fh->descs_list, list) { + if (desc->fd == fd) + return desc; + } + + return NULL; +} + +static bool dmabuf_cmp(struct dma_buf *lb, struct dma_buf *rb) +{ + return lb == rb && lb->size == rb->size; +} + +static struct ipu_psys_kbuffer *psys_buf_lookup(struct ipu_psys_fh *fh, int fd) { struct ipu_psys_kbuffer *kbuf; + struct dma_buf *dma_buf; + + dma_buf = dma_buf_get(fd); + if (IS_ERR(dma_buf)) + return NULL; + + /* + * First lookup so-called `active` list, that is the list of + * referenced buffers + */ + list_for_each_entry(kbuf, &fh->bufs_list, list) { + if (dmabuf_cmp(kbuf->dbuf, dma_buf)) { + dma_buf_put(dma_buf); + return kbuf; + } + } - list_for_each_entry(kbuf, &fh->bufmap, list) { - if (kbuf->fd == fd) + /* + * We didn't find anything on the `active` list, try the LRU list + * (list of unreferenced buffers) and possibly resurrect a buffer + */ + list_for_each_entry(kbuf, &fh->bufs_lru, list) { + if (dmabuf_cmp(kbuf->dbuf, dma_buf)) { + dma_buf_put(dma_buf); + ipu_buffer_lru_del(fh, kbuf); + ipu_buffer_add(fh, kbuf); return kbuf; + } } + dma_buf_put(dma_buf); return NULL; } +struct ipu_psys_kbuffer *ipu_psys_lookup_kbuffer(struct ipu_psys_fh *fh, int fd) +{ + struct ipu_psys_desc *desc; + + desc = psys_desc_lookup(fh, fd); + if (!desc) + return NULL; + + return desc->kbuf; +} + struct ipu_psys_kbuffer * ipu_psys_lookup_kbuffer_by_kaddr(struct ipu_psys_fh *fh, void *kaddr) { struct ipu_psys_kbuffer *kbuffer; - list_for_each_entry(kbuffer, &fh->bufmap, list) { + list_for_each_entry(kbuffer, &fh->bufs_list, list) { if (kbuffer->kaddr == kaddr) return kbuffer; } @@ -203,7 +333,8 @@ static int ipu_psys_get_userpages(struct ipu_dma_buf_attach *attach) 1, 0, pages, NULL); #elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0) nr = get_user_pages(start & PAGE_MASK, npages, - FOLL_WRITE, pages, NULL); + FOLL_WRITE, + pages, NULL); #else nr = get_user_pages(start & PAGE_MASK, npages, FOLL_WRITE, pages); @@ -397,11 +528,9 @@ static void ipu_dma_buf_release(struct dma_buf *buf) if (!kbuf) return; - if (kbuf->db_attach) { - dev_dbg(kbuf->db_attach->dev, - "releasing buffer %d\n", kbuf->fd); + if (kbuf->db_attach) ipu_psys_put_userpages(kbuf->db_attach->priv); - } + kfree(kbuf); } @@ -581,7 +710,9 @@ static int ipu_psys_open(struct inode *inode, struct file *file) file->private_data = fh; mutex_init(&fh->mutex); - INIT_LIST_HEAD(&fh->bufmap); + INIT_LIST_HEAD(&fh->bufs_list); + INIT_LIST_HEAD(&fh->descs_list); + INIT_LIST_HEAD(&fh->bufs_lru); init_waitqueue_head(&fh->wait); rval = ipu_psys_fh_init(fh); @@ -612,7 +743,7 @@ static inline void ipu_psys_kbuf_unmap(struct ipu_psys_kbuffer *kbuf) struct iosys_map dmap; iosys_map_set_vaddr(&dmap, kbuf->kaddr); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 255) dma_buf_vunmap_unlocked(kbuf->dbuf, &dmap); #else dma_buf_vunmap(kbuf->dbuf, &dmap); @@ -629,8 +760,8 @@ static inline void ipu_psys_kbuf_unmap(struct ipu_psys_kbuffer *kbuf) if (kbuf->kaddr) dma_buf_vunmap(kbuf->dbuf, kbuf->kaddr); #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0) - if (kbuf->sgt) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 255) + if (!IS_ERR_OR_NULL(kbuf->sgt)) dma_buf_unmap_attachment_unlocked(kbuf->db_attach, kbuf->sgt, DMA_BIDIRECTIONAL); @@ -640,7 +771,7 @@ static inline void ipu_psys_kbuf_unmap(struct ipu_psys_kbuffer *kbuf) kbuf->sgt, DMA_BIDIRECTIONAL); #endif - if (kbuf->db_attach) + if (!IS_ERR_OR_NULL(kbuf->db_attach)) dma_buf_detach(kbuf->dbuf, kbuf->db_attach); dma_buf_put(kbuf->dbuf); @@ -649,29 +780,93 @@ static inline void ipu_psys_kbuf_unmap(struct ipu_psys_kbuffer *kbuf) kbuf->sgt = NULL; } +static void __ipu_psys_unmapbuf(struct ipu_psys_fh *fh, + struct ipu_psys_kbuffer *kbuf) +{ + /* From now on it is not safe to use this kbuffer */ + ipu_psys_kbuf_unmap(kbuf); + ipu_buffer_del(fh, kbuf); + if (!kbuf->userptr) + kfree(kbuf); +} + +static int ipu_psys_unmapbuf_locked(int fd, struct ipu_psys_fh *fh) +{ + struct ipu_psys *psys = fh->psys; + struct ipu_psys_kbuffer *kbuf; + struct ipu_psys_desc *desc; + + desc = psys_desc_lookup(fh, fd); + if (WARN_ON_ONCE(!desc)) { + dev_err(&psys->adev->dev, "descriptor not found: %d\n", fd); + return -EINVAL; + } + + kbuf = desc->kbuf; + /* descriptor is gone now */ + ipu_desc_del(fh, desc); + kfree(desc); + + if (WARN_ON_ONCE(!kbuf || !kbuf->dbuf)) { + dev_err(&psys->adev->dev, + "descriptor with no buffer: %d\n", fd); + return -EINVAL; + } + + /* Wait for final UNMAP */ + if (!atomic_dec_and_test(&kbuf->map_count)) + return 0; + + __ipu_psys_unmapbuf(fh, kbuf); + return 0; +} + static int ipu_psys_release(struct inode *inode, struct file *file) { struct ipu_psys *psys = inode_to_ipu_psys(inode); struct ipu_psys_fh *fh = file->private_data; - struct ipu_psys_kbuffer *kbuf, *kbuf0; - struct dma_buf_attachment *db_attach; mutex_lock(&fh->mutex); - /* clean up buffers */ - if (!list_empty(&fh->bufmap)) { - list_for_each_entry_safe(kbuf, kbuf0, &fh->bufmap, list) { - list_del(&kbuf->list); - db_attach = kbuf->db_attach; - - /* Unmap and release buffers */ - if (kbuf->dbuf && db_attach) { - - ipu_psys_kbuf_unmap(kbuf); - } else { - if (db_attach) - ipu_psys_put_userpages(db_attach->priv); - kfree(kbuf); - } + while (!list_empty(&fh->descs_list)) { + struct ipu_psys_desc *desc; + + desc = list_first_entry(&fh->descs_list, + struct ipu_psys_desc, + list); + + ipu_desc_del(fh, desc); + kfree(desc); + } + + while (!list_empty(&fh->bufs_lru)) { + struct ipu_psys_kbuffer *kbuf; + + kbuf = list_first_entry(&fh->bufs_lru, + struct ipu_psys_kbuffer, + list); + + ipu_buffer_lru_del(fh, kbuf); + __ipu_psys_unmapbuf(fh, kbuf); + } + + while (!list_empty(&fh->bufs_list)) { + struct dma_buf_attachment *db_attach; + struct ipu_psys_kbuffer *kbuf; + + kbuf = list_first_entry(&fh->bufs_list, + struct ipu_psys_kbuffer, + list); + + ipu_buffer_del(fh, kbuf); + db_attach = kbuf->db_attach; + + /* Unmap and release buffers */ + if (kbuf->dbuf && db_attach) { + ipu_psys_kbuf_unmap(kbuf); + } else { + if (db_attach) + ipu_psys_put_userpages(db_attach->priv); + kfree(kbuf); } } mutex_unlock(&fh->mutex); @@ -696,10 +891,8 @@ static int ipu_psys_getbuf(struct ipu_psys_buffer *buf, struct ipu_psys_fh *fh) { struct ipu_psys_kbuffer *kbuf; struct ipu_psys *psys = fh->psys; - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + struct ipu_psys_desc *desc; DEFINE_DMA_BUF_EXPORT_INFO(exp_info); -#endif struct dma_buf *dbuf; int ret; @@ -708,7 +901,7 @@ static int ipu_psys_getbuf(struct ipu_psys_buffer *buf, struct ipu_psys_fh *fh) return -EINVAL; } - kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); + kbuf = ipu_psys_kbuffer_alloc(); if (!kbuf) return -ENOMEM; @@ -716,16 +909,12 @@ static int ipu_psys_getbuf(struct ipu_psys_buffer *buf, struct ipu_psys_fh *fh) kbuf->userptr = buf->base.userptr; kbuf->flags = buf->flags; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) exp_info.ops = &ipu_dma_buf_ops; exp_info.size = kbuf->len; exp_info.flags = O_RDWR; exp_info.priv = kbuf; dbuf = dma_buf_export(&exp_info); -#else - dbuf = dma_buf_export(kbuf, &ipu_dma_buf_ops, kbuf->len, 0); -#endif if (IS_ERR(dbuf)) { kfree(kbuf); return PTR_ERR(dbuf); @@ -737,14 +926,22 @@ static int ipu_psys_getbuf(struct ipu_psys_buffer *buf, struct ipu_psys_fh *fh) return ret; } - kbuf->fd = ret; buf->base.fd = ret; buf->flags &= ~IPU_BUFFER_FLAG_USERPTR; buf->flags |= IPU_BUFFER_FLAG_DMA_HANDLE; kbuf->flags = buf->flags; + desc = ipu_psys_desc_alloc(ret); + if (!desc) { + dma_buf_put(dbuf); + return -ENOMEM; + } + + kbuf->dbuf = dbuf; + mutex_lock(&fh->mutex); - list_add(&kbuf->list, &fh->bufmap); + ipu_desc_add(fh, desc); + ipu_buffer_add(fh, kbuf); mutex_unlock(&fh->mutex); dev_dbg(&psys->adev->dev, "IOC_GETBUF: userptr %p size %llu to fd %d", @@ -758,60 +955,75 @@ static int ipu_psys_putbuf(struct ipu_psys_buffer *buf, struct ipu_psys_fh *fh) return 0; } -int ipu_psys_mapbuf_locked(int fd, struct ipu_psys_fh *fh, - struct ipu_psys_kbuffer *kbuf) +static void ipu_psys_kbuffer_lru(struct ipu_psys_fh *fh, + struct ipu_psys_kbuffer *kbuf) +{ + ipu_buffer_del(fh, kbuf); + ipu_buffer_lru_add(fh, kbuf); + + while (fh->num_bufs_lru > IPU_PSYS_MAX_NUM_BUFS_LRU) { + kbuf = list_first_entry(&fh->bufs_lru, + struct ipu_psys_kbuffer, + list); + + ipu_buffer_lru_del(fh, kbuf); + __ipu_psys_unmapbuf(fh, kbuf); + } +} + +struct ipu_psys_kbuffer *ipu_psys_mapbuf_locked(int fd, struct ipu_psys_fh *fh) { struct ipu_psys *psys = fh->psys; + struct ipu_psys_kbuffer *kbuf; + struct ipu_psys_desc *desc; struct dma_buf *dbuf; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) || LINUX_VERSION_CODE == KERNEL_VERSION(5, 15, 255) \ - || LINUX_VERSION_CODE == KERNEL_VERSION(5, 15, 71) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) || \ + LINUX_VERSION_CODE == KERNEL_VERSION(5, 15, 255) || \ + LINUX_VERSION_CODE == KERNEL_VERSION(5, 15, 71) struct iosys_map dmap = { .is_iomem = false, }; #elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) && LINUX_VERSION_CODE != KERNEL_VERSION(5, 10, 46) struct dma_buf_map dmap; #endif - int ret; dbuf = dma_buf_get(fd); if (IS_ERR(dbuf)) - return -EINVAL; + return NULL; + desc = psys_desc_lookup(fh, fd); + if (!desc) { + desc = ipu_psys_desc_alloc(fd); + if (!desc) + goto desc_alloc_fail; + ipu_desc_add(fh, desc); + } + + kbuf = psys_buf_lookup(fh, fd); if (!kbuf) { - /* This fd isn't generated by ipu_psys_getbuf, it - * is a new fd. Create a new kbuf item for this fd, and - * add this kbuf to bufmap list. - */ - kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); - if (!kbuf) { - ret = -ENOMEM; - goto mapbuf_fail; - } + kbuf = ipu_psys_kbuffer_alloc(); + if (!kbuf) + goto buf_alloc_fail; + ipu_buffer_add(fh, kbuf); + } - list_add(&kbuf->list, &fh->bufmap); - } - - /* fd valid and found, need remap */ - if (kbuf->dbuf && (kbuf->dbuf != dbuf || kbuf->len != dbuf->size)) { - dev_dbg(&psys->adev->dev, - "dmabuf fd %d with kbuf %p changed, need remap.\n", - fd, kbuf); - ret = ipu_psys_unmapbuf_locked(fd, fh, kbuf); - if (ret) - goto mapbuf_fail; - - kbuf = ipu_psys_lookup_kbuffer(fh, fd); - /* changed external dmabuf */ - if (!kbuf) { - kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); - if (!kbuf) { - ret = -ENOMEM; - goto mapbuf_fail; - } - list_add(&kbuf->list, &fh->bufmap); + /* If this descriptor references no buffer or new buffer */ + if (desc->kbuf != kbuf) { + if (desc->kbuf) { + /* + * Un-reference old buffer and possibly put it on + * the LRU list + */ + if (atomic_dec_and_test(&desc->kbuf->map_count)) + ipu_psys_kbuffer_lru(fh, desc->kbuf); } + + /* Grab reference of the new buffer */ + atomic_inc(&kbuf->map_count); } + desc->kbuf = kbuf; + if (kbuf->sgt) { dev_dbg(&psys->adev->dev, "fd %d has been mapped!\n", fd); dma_buf_put(dbuf); @@ -823,22 +1035,19 @@ int ipu_psys_mapbuf_locked(int fd, struct ipu_psys_fh *fh, if (kbuf->len == 0) kbuf->len = kbuf->dbuf->size; - kbuf->fd = fd; - kbuf->db_attach = dma_buf_attach(kbuf->dbuf, &psys->adev->dev); if (IS_ERR(kbuf->db_attach)) { - ret = PTR_ERR(kbuf->db_attach); dev_dbg(&psys->adev->dev, "dma buf attach failed\n"); goto kbuf_map_fail; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0) - kbuf->sgt = dma_buf_map_attachment_unlocked(kbuf->db_attach, DMA_BIDIRECTIONAL); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 255) + kbuf->sgt = dma_buf_map_attachment_unlocked(kbuf->db_attach, + DMA_BIDIRECTIONAL); #else kbuf->sgt = dma_buf_map_attachment(kbuf->db_attach, DMA_BIDIRECTIONAL); #endif if (IS_ERR_OR_NULL(kbuf->sgt)) { - ret = -EINVAL; kbuf->sgt = NULL; dev_dbg(&psys->adev->dev, "dma buf map attachment failed\n"); goto kbuf_map_fail; @@ -847,20 +1056,21 @@ int ipu_psys_mapbuf_locked(int fd, struct ipu_psys_fh *fh, kbuf->dma_addr = sg_dma_address(kbuf->sgt->sgl); #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) && LINUX_VERSION_CODE != KERNEL_VERSION(5, 10, 46) -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0) - ret = dma_buf_vmap_unlocked(kbuf->dbuf, &dmap); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 255) + if (dma_buf_vmap_unlocked(kbuf->dbuf, &dmap)) { + dev_dbg(&psys->adev->dev, "dma buf vmap failed\n"); + goto kbuf_map_fail; + } #else - ret = dma_buf_vmap(kbuf->dbuf, &dmap); -#endif - if (ret) { + if (dma_buf_vmap(kbuf->dbuf, &dmap)) { dev_dbg(&psys->adev->dev, "dma buf vmap failed\n"); goto kbuf_map_fail; } +#endif kbuf->kaddr = dmap.vaddr; #else kbuf->kaddr = dma_buf_vmap(kbuf->dbuf); if (!kbuf->kaddr) { - ret = -EINVAL; dev_dbg(&psys->adev->dev, "dma buf vmap failed\n"); goto kbuf_map_fail; } @@ -868,78 +1078,47 @@ int ipu_psys_mapbuf_locked(int fd, struct ipu_psys_fh *fh, dev_dbg(&psys->adev->dev, "%s kbuf %p fd %d with len %llu mapped\n", __func__, kbuf, fd, kbuf->len); -mapbuf_end: +mapbuf_end: kbuf->valid = true; - - return 0; + return kbuf; kbuf_map_fail: + ipu_buffer_del(fh, kbuf); ipu_psys_kbuf_unmap(kbuf); - - list_del(&kbuf->list); + dbuf = ERR_PTR(-EINVAL); if (!kbuf->userptr) kfree(kbuf); -mapbuf_fail: - dma_buf_put(dbuf); +buf_alloc_fail: + ipu_desc_del(fh, desc); + kfree(desc); - dev_err(&psys->adev->dev, "%s failed for fd %d\n", __func__, fd); - return ret; +desc_alloc_fail: + if (!IS_ERR(dbuf)) + dma_buf_put(dbuf); + return NULL; } static long ipu_psys_mapbuf(int fd, struct ipu_psys_fh *fh) { - long ret; struct ipu_psys_kbuffer *kbuf; mutex_lock(&fh->mutex); - kbuf = ipu_psys_lookup_kbuffer(fh, fd); - ret = ipu_psys_mapbuf_locked(fd, fh, kbuf); + kbuf = ipu_psys_mapbuf_locked(fd, fh); mutex_unlock(&fh->mutex); - dev_dbg(&fh->psys->adev->dev, "IOC_MAPBUF ret %ld\n", ret); + dev_dbg(&fh->psys->adev->dev, "IOC_MAPBUF\n"); - return ret; -} - -static int ipu_psys_unmapbuf_locked(int fd, struct ipu_psys_fh *fh, - struct ipu_psys_kbuffer *kbuf) -{ - struct ipu_psys *psys = fh->psys; - - if (!kbuf || fd != kbuf->fd) { - dev_err(&psys->adev->dev, "invalid kbuffer\n"); - return -EINVAL; - } - - /* From now on it is not safe to use this kbuffer */ - ipu_psys_kbuf_unmap(kbuf); - - list_del(&kbuf->list); - - if (!kbuf->userptr) - kfree(kbuf); - - dev_dbg(&psys->adev->dev, "%s fd %d unmapped\n", __func__, fd); - - return 0; + return kbuf ? 0 : -EINVAL; } static long ipu_psys_unmapbuf(int fd, struct ipu_psys_fh *fh) { - struct ipu_psys_kbuffer *kbuf; long ret; mutex_lock(&fh->mutex); - kbuf = ipu_psys_lookup_kbuffer(fh, fd); - if (!kbuf) { - dev_err(&fh->psys->adev->dev, - "buffer with fd %d not found\n", fd); - mutex_unlock(&fh->mutex); - return -EINVAL; - } - ret = ipu_psys_unmapbuf_locked(fd, fh, kbuf); + ret = ipu_psys_unmapbuf_locked(fd, fh); mutex_unlock(&fh->mutex); dev_dbg(&fh->psys->adev->dev, "IOC_UNMAPBUF\n"); diff --git a/drivers/media/pci/intel/ipu-psys.h b/drivers/media/pci/intel/ipu-psys.h index 0fe58bae3814..ca6a817428aa 100644 --- a/drivers/media/pci/intel/ipu-psys.h +++ b/drivers/media/pci/intel/ipu-psys.h @@ -120,11 +120,19 @@ struct ipu_psys { struct ipu_psys_fh { struct ipu_psys *psys; - struct mutex mutex; /* Protects bufmap & kcmds fields */ + struct mutex mutex; /* Protects bufs_list & kcmds fields */ struct list_head list; - struct list_head bufmap; + /* Holds all buffers that this fh owns */ + struct list_head bufs_list; + /* Holds all descriptors (fd:kbuffer associations) */ + struct list_head descs_list; + struct list_head bufs_lru; wait_queue_head_t wait; struct ipu_psys_scheduler sched; + + u32 num_bufs; + u32 num_descs; + u32 num_bufs_lru; }; struct ipu_psys_pg { @@ -173,17 +181,24 @@ struct ipu_dma_buf_attach { struct ipu_psys_kbuffer { u64 len; void *userptr; - u32 flags; - int fd; void *kaddr; struct list_head list; dma_addr_t dma_addr; struct sg_table *sgt; struct dma_buf_attachment *db_attach; struct dma_buf *dbuf; + u32 flags; + /* The number of times this buffer is mapped */ + atomic_t map_count; bool valid; /* True when buffer is usable */ }; +struct ipu_psys_desc { + struct ipu_psys_kbuffer *kbuf; + struct list_head list; + u32 fd; +}; + #define inode_to_ipu_psys(inode) \ container_of((inode)->i_cdev, struct ipu_psys, cdev) @@ -200,8 +215,8 @@ void ipu_psys_run_next(struct ipu_psys *psys); struct ipu_psys_pg *__get_pg_buf(struct ipu_psys *psys, size_t pg_size); struct ipu_psys_kbuffer * ipu_psys_lookup_kbuffer(struct ipu_psys_fh *fh, int fd); -int ipu_psys_mapbuf_locked(int fd, struct ipu_psys_fh *fh, - struct ipu_psys_kbuffer *kbuf); +struct ipu_psys_kbuffer * +ipu_psys_mapbuf_locked(int fd, struct ipu_psys_fh *fh); struct ipu_psys_kbuffer * ipu_psys_lookup_kbuffer_by_kaddr(struct ipu_psys_fh *fh, void *kaddr); #ifdef IPU_PSYS_GPC diff --git a/drivers/media/pci/intel/ipu.c b/drivers/media/pci/intel/ipu.c index 33592a6624f3..eb5d46a47920 100644 --- a/drivers/media/pci/intel/ipu.c +++ b/drivers/media/pci/intel/ipu.c @@ -579,6 +579,7 @@ static int ipu_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) rval = request_cpd_fw(&isp->cpd_fw, isp->cpd_fw_name_new, &pdev->dev); } + if (rval) { dev_err(&isp->pdev->dev, "Requesting signed firmware failed\n"); goto buttress_exit; @@ -744,20 +745,17 @@ static void ipu_pci_remove(struct pci_dev *pdev) isp->pkg_dir_dma_addr = 0; isp->pkg_dir_size = 0; + ipu_mmu_cleanup(isp->psys->mmu); + ipu_mmu_cleanup(isp->isys->mmu); + ipu_bus_del_devices(pdev); pm_runtime_forbid(&pdev->dev); pm_runtime_get_noresume(&pdev->dev); - pci_release_regions(pdev); - pci_disable_device(pdev); - ipu_buttress_exit(isp); release_firmware(isp->cpd_fw); - - ipu_mmu_cleanup(isp->psys->mmu); - ipu_mmu_cleanup(isp->isys->mmu); } #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0) diff --git a/drivers/media/pci/intel/ipu6/ipu-platform.h b/drivers/media/pci/intel/ipu6/ipu-platform.h index 6f70ffbecada..826fe5febf5e 100644 --- a/drivers/media/pci/intel/ipu6/ipu-platform.h +++ b/drivers/media/pci/intel/ipu6/ipu-platform.h @@ -11,16 +11,16 @@ #define IPU6EPES_FIRMWARE_NAME "intel/ipu6epes_fw.bin" #define IPU6_FIRMWARE_NAME "intel/ipu6_fw.bin" #define IPU6EPMTL_FIRMWARE_NAME "intel/ipu6epmtl_fw.bin" -#define IPU6EPMTLES_FIRMWARE_NAME "intel/ipu6epmtles_fw.bin" #define IPU6EPADLN_FIRMWARE_NAME "intel/ipu6epadln_fw.bin" +#define IPU6EPMTLES_FIRMWARE_NAME "intel/ipu6epmtles_fw.bin" #define IPU6SE_FIRMWARE_NAME_NEW "intel/ipu/ipu6se_fw.bin" #define IPU6EP_FIRMWARE_NAME_NEW "intel/ipu/ipu6ep_fw.bin" #define IPU6EPES_FIRMWARE_NAME_NEW "intel/ipu/ipu6epes_fw.bin" #define IPU6_FIRMWARE_NAME_NEW "intel/ipu/ipu6_fw.bin" #define IPU6EPMTL_FIRMWARE_NAME_NEW "intel/ipu/ipu6epmtl_fw.bin" -#define IPU6EPMTLES_FIRMWARE_NAME_NEW "intel/ipu/ipu6epmtles_fw.bin" #define IPU6EPADLN_FIRMWARE_NAME_NEW "intel/ipu/ipu6epadln_fw.bin" +#define IPU6EPMTLES_FIRMWARE_NAME_NEW "intel/ipu/ipu6epmtles_fw.bin" /* * The following definitions are encoded to the media_device's model field so diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c index f5daca2a01bd..4bb5167859c1 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c @@ -447,21 +447,18 @@ int ipu_isys_csi2_set_stream(struct v4l2_subdev *sd, { struct ipu_isys_csi2 *csi2 = to_ipu_isys_csi2(sd); struct ipu_isys *isys = csi2->isys; - struct ipu_isys_pipeline *ip = - container_of(media_entity_pipeline(&sd->entity), - struct ipu_isys_pipeline, pipe); - struct v4l2_subdev *esd = - media_entity_to_v4l2_subdev(ip->external->entity); - struct ipu_isys_csi2_config *cfg; + struct media_pipeline *mp = media_entity_pipeline(&sd->entity); + struct ipu_isys_pipeline *ip = container_of(mp, + struct ipu_isys_pipeline, + pipe); + struct ipu_isys_csi2_config *cfg = + v4l2_get_subdev_hostdata(media_entity_to_v4l2_subdev + (ip->external->entity)); unsigned int port, port_max; int ret = 0; u32 mask = 0; unsigned int i; - if (!esd) - return -EPIPE; - - cfg = v4l2_get_subdev_hostdata(esd); port = cfg->port; dev_dbg(&isys->adev->dev, "for port %u with %u lanes\n", port, nlanes); diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-gpc.c b/drivers/media/pci/intel/ipu6/ipu6-isys-gpc.c index 60328808a62d..63fbf170f7bf 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys-gpc.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys-gpc.c @@ -78,7 +78,6 @@ static int ipu6_isys_gpc_global_enable_set(void *data, u64 val) isys_gpcs->gpc[i].route = 0; isys_gpcs->gpc[i].source = 0; } - pm_runtime_put(&isys->adev->dev); } else { /* diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.c b/drivers/media/pci/intel/ipu6/ipu6-isys.c index 81c29fcbfc6d..93d4c35b5b31 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys.c @@ -187,4 +187,3 @@ irqreturn_t isys_isr(struct ipu_bus_device *adev) return IRQ_HANDLED; } - diff --git a/drivers/media/pci/intel/ipu6/ipu6-psys-gpc.c b/drivers/media/pci/intel/ipu6/ipu6-psys-gpc.c index dbfdf76984f6..6ff92e944112 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-psys-gpc.c +++ b/drivers/media/pci/intel/ipu6/ipu6-psys-gpc.c @@ -81,7 +81,6 @@ static int ipu6_psys_gpc_global_enable_set(void *data, u64 val) psys_gpcs->gpc[idx].route = 0; psys_gpcs->gpc[idx].source = 0; } - pm_runtime_put(&psys->adev->dev); } else { /* Set gpc reg and start all gpc here. diff --git a/drivers/media/pci/intel/ipu6/ipu6-psys.c b/drivers/media/pci/intel/ipu6/ipu6-psys.c index fac11eea5de1..ac5dae41dac4 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-psys.c +++ b/drivers/media/pci/intel/ipu6/ipu6-psys.c @@ -24,6 +24,8 @@ #include "ipu-platform-regs.h" #include "ipu-trace.h" +MODULE_IMPORT_NS(DMA_BUF); + static bool early_pg_transfer; module_param(early_pg_transfer, bool, 0664); MODULE_PARM_DESC(early_pg_transfer, @@ -251,16 +253,9 @@ static struct ipu_psys_kcmd *ipu_psys_copy_cmd(struct ipu_psys_command *cmd, } /* check and remap if possibe */ - ret = ipu_psys_mapbuf_locked(fd, fh, kpgbuf); - if (ret) { - dev_err(&psys->adev->dev, "%s remap failed\n", __func__); - mutex_unlock(&fh->mutex); - goto error; - } - - kpgbuf = ipu_psys_lookup_kbuffer(fh, fd); + kpgbuf = ipu_psys_mapbuf_locked(fd, fh); if (!kpgbuf || !kpgbuf->sgt) { - WARN(1, "kbuf not found or unmapped.\n"); + dev_err(&psys->adev->dev, "%s remap failed\n", __func__); mutex_unlock(&fh->mutex); goto error; } @@ -349,20 +344,13 @@ static struct ipu_psys_kcmd *ipu_psys_copy_cmd(struct ipu_psys_command *cmd, goto error; } - ret = ipu_psys_mapbuf_locked(fd, fh, kpgbuf); - if (ret) { + kpgbuf = ipu_psys_mapbuf_locked(fd, fh); + if (!kpgbuf || !kpgbuf->sgt) { dev_err(&psys->adev->dev, "%s remap failed\n", __func__); mutex_unlock(&fh->mutex); goto error; } - - kpgbuf = ipu_psys_lookup_kbuffer(fh, fd); - if (!kpgbuf || !kpgbuf->sgt) { - WARN(1, "kbuf not found or unmapped.\n"); - mutex_unlock(&fh->mutex); - goto error; - } mutex_unlock(&fh->mutex); kcmd->kbufs[i] = kpgbuf; if (!kcmd->kbufs[i] || !kcmd->kbufs[i]->sgt || diff --git a/drivers/media/pci/intel/ipu6/ipu6.c b/drivers/media/pci/intel/ipu6/ipu6.c index 91d3c19baf75..36985c160c6c 100644 --- a/drivers/media/pci/intel/ipu6/ipu6.c +++ b/drivers/media/pci/intel/ipu6/ipu6.c @@ -293,29 +293,6 @@ void ipu_configure_spc(struct ipu_device *isp, } EXPORT_SYMBOL(ipu_configure_spc); -int ipu_buttress_psys_freq_get(void *data, u64 *val) -{ - struct ipu_device *isp = data; - u32 reg_val; - int rval; - - rval = pm_runtime_get_sync(&isp->psys->dev); - if (rval < 0) { - pm_runtime_put(&isp->psys->dev); - dev_err(&isp->pdev->dev, "Runtime PM failed (%d)\n", rval); - return rval; - } - - reg_val = readl(isp->base + BUTTRESS_REG_PS_FREQ_CTL); - - pm_runtime_put(&isp->psys->dev); - - *val = IPU_PS_FREQ_RATIO_BASE * - (reg_val & IPU_BUTTRESS_PS_FREQ_CTL_DIVISOR_MASK); - - return 0; -} - void ipu_internal_pdata_init(void) { if (ipu_ver == IPU_VER_6 || ipu_ver == IPU_VER_6EP || diff --git a/patch/v6.8/0001-media-ov08x40-Add-Tline-calculation-and-handshake-pi.patch b/patch/v6.8/0001-media-ov08x40-Add-Tline-calculation-and-handshake-pi.patch index 20c94dbc6107..736c333a07fa 100644 --- a/patch/v6.8/0001-media-ov08x40-Add-Tline-calculation-and-handshake-pi.patch +++ b/patch/v6.8/0001-media-ov08x40-Add-Tline-calculation-and-handshake-pi.patch @@ -1,7 +1,7 @@ -From 3f1e304628f1de6aa650777b738c0a0041eb38a7 Mon Sep 17 00:00:00 2001 +From a5624eebbdeb756d5d4b9fec9be10edaa3c3fd02 Mon Sep 17 00:00:00 2001 From: Hao Yao Date: Tue, 26 Mar 2024 11:17:06 +0800 -Subject: [PATCH 1/5] media: ov08x40: Add Tline calculation and handshake pin +Subject: [PATCH 01/12] media: ov08x40: Add Tline calculation and handshake pin support Signed-off-by: Hao Yao @@ -403,5 +403,5 @@ index abbb0b774d43..aac42ad2ae3a 100644 MODULE_DESCRIPTION("OmniVision OV08X40 sensor driver"); MODULE_LICENSE("GPL"); -- -2.43.2 +2.43.0 diff --git a/patch/v6.8/0002-media-Add-IPU6-and-supported-sensors-config.patch b/patch/v6.8/0002-media-Add-IPU6-and-supported-sensors-config.patch index 42915f6f0e6a..92e4c8ae2440 100644 --- a/patch/v6.8/0002-media-Add-IPU6-and-supported-sensors-config.patch +++ b/patch/v6.8/0002-media-Add-IPU6-and-supported-sensors-config.patch @@ -1,7 +1,7 @@ -From e39d4c99e2bf1002cd61faf79db86b95257a4a3e Mon Sep 17 00:00:00 2001 +From 768db18beee57328ca99ebaf465ad8d763167405 Mon Sep 17 00:00:00 2001 From: Hao Yao Date: Mon, 25 Mar 2024 14:40:09 +0800 -Subject: [PATCH 2/5] media: Add IPU6 and supported sensors config +Subject: [PATCH 02/12] media: Add IPU6 and supported sensors config Signed-off-by: Hao Yao --- @@ -145,5 +145,5 @@ index f199a97e1d78..0ac987a40da5 100644 +obj-y += ipu6/ obj-y += ivsc/ -- -2.43.2 +2.43.0 diff --git a/patch/v6.8/0003-media-ipu-bridge-Support-more-sensors.patch b/patch/v6.8/0003-media-ipu-bridge-Support-more-sensors.patch index c5c2fb3db116..e4d7e29ce892 100644 --- a/patch/v6.8/0003-media-ipu-bridge-Support-more-sensors.patch +++ b/patch/v6.8/0003-media-ipu-bridge-Support-more-sensors.patch @@ -1,7 +1,7 @@ -From fd6e61b885b92eb666870949fac0921143e3fa6f Mon Sep 17 00:00:00 2001 +From 4a50a9f94b16b4f88193d9d015114f16b3ea3a1f Mon Sep 17 00:00:00 2001 From: Hao Yao Date: Tue, 26 Mar 2024 10:50:41 +0800 -Subject: [PATCH 3/5] media: ipu-bridge: Support more sensors +Subject: [PATCH 03/12] media: ipu-bridge: Support more sensors Signed-off-by: Hao Yao --- @@ -41,5 +41,5 @@ index f980e3125a7b..2ac59e02b839 100644 static const struct ipu_property_names prop_names = { -- -2.43.2 +2.43.0 diff --git a/patch/v6.8/0004-ACPI-scan-Defer-enumeration-of-devices-with-a-_DEP-p.patch b/patch/v6.8/0004-ACPI-scan-Defer-enumeration-of-devices-with-a-_DEP-p.patch index c5e264d4c711..52b36bc460e0 100644 --- a/patch/v6.8/0004-ACPI-scan-Defer-enumeration-of-devices-with-a-_DEP-p.patch +++ b/patch/v6.8/0004-ACPI-scan-Defer-enumeration-of-devices-with-a-_DEP-p.patch @@ -1,7 +1,7 @@ -From f71be295d188e89f5eab1648467e65010a9daf28 Mon Sep 17 00:00:00 2001 +From 9e155aa642bd0f1187229952fb496026e6fcfee2 Mon Sep 17 00:00:00 2001 From: Wentong Wu Date: Wed, 7 Feb 2024 08:59:08 +0800 -Subject: [PATCH 4/5] ACPI: scan: Defer enumeration of devices with a _DEP +Subject: [PATCH 04/12] ACPI: scan: Defer enumeration of devices with a _DEP pointing to IVSC device Inside IVSC, switching ownership requires an interface with two @@ -30,10 +30,10 @@ Signed-off-by: Rafael J. Wysocki 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c -index 617f3e0e963d..dd817e9ed2e5 100644 +index e6ed1ba91e5c..f32a2c738c8b 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c -@@ -794,6 +794,7 @@ static const char * const acpi_honor_dep_ids[] = { +@@ -798,6 +798,7 @@ static const char * const acpi_honor_dep_ids[] = { "INTC1059", /* IVSC (TGL) driver must be loaded to allow i2c access to camera sensors */ "INTC1095", /* IVSC (ADL) driver must be loaded to allow i2c access to camera sensors */ "INTC100A", /* IVSC (RPL) driver must be loaded to allow i2c access to camera sensors */ @@ -42,5 +42,5 @@ index 617f3e0e963d..dd817e9ed2e5 100644 }; -- -2.43.2 +2.43.0 diff --git a/patch/v6.8/0005-mei-vsc-Unregister-interrupt-handler-for-system-susp.patch b/patch/v6.8/0005-mei-vsc-Unregister-interrupt-handler-for-system-susp.patch index 181d6cc32ed0..6e9a86628f7a 100644 --- a/patch/v6.8/0005-mei-vsc-Unregister-interrupt-handler-for-system-susp.patch +++ b/patch/v6.8/0005-mei-vsc-Unregister-interrupt-handler-for-system-susp.patch @@ -1,7 +1,8 @@ -From a1e3360ca97c88a822e8c30b827c673de626b127 Mon Sep 17 00:00:00 2001 +From afead1790dd3613ca675365447ce09cae75392b7 Mon Sep 17 00:00:00 2001 From: Sakari Ailus -Date: Mon, 18 Mar 2024 10:01:26 +0200 -Subject: [PATCH 5/5] mei: vsc: Unregister interrupt handler for system suspend +Date: Wed, 3 Apr 2024 13:13:41 +0800 +Subject: [PATCH 05/12] mei: vsc: Unregister interrupt handler for system + suspend Unregister the MEI VSC interrupt handler before system suspend and re-register it at system resume time. This mirrors implementation of other @@ -15,6 +16,9 @@ Cc: stable@vger.kernel.org # for 6.8 Reported-by: Dominik Brodowski Signed-off-by: Wentong Wu Signed-off-by: Sakari Ailus +Acked-by: Tomas Winkler +Link: https://lore.kernel.org/r/20240403051341.3534650-2-wentong.wu@intel.com +Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/platform-vsc.c | 17 ++++++- drivers/misc/mei/vsc-tp.c | 84 +++++++++++++++++++++++---------- @@ -68,10 +72,10 @@ index 8d303c6c0000..8db0fcf24e70 100644 static DEFINE_SIMPLE_DEV_PM_OPS(mei_vsc_pm_ops, mei_vsc_suspend, mei_vsc_resume); diff --git a/drivers/misc/mei/vsc-tp.c b/drivers/misc/mei/vsc-tp.c -index 682c62c635b6..d612b3391191 100644 +index 55f7db490d3b..dcf4d9bd3293 100644 --- a/drivers/misc/mei/vsc-tp.c +++ b/drivers/misc/mei/vsc-tp.c -@@ -94,6 +94,27 @@ static const struct acpi_gpio_mapping vsc_tp_acpi_gpios[] = { +@@ -93,6 +93,27 @@ static const struct acpi_gpio_mapping vsc_tp_acpi_gpios[] = { {} }; @@ -81,6 +85,8 @@ index 682c62c635b6..d612b3391191 100644 + + atomic_inc(&tp->assert_cnt); + ++ wake_up(&tp->xfer_wait); ++ + return IRQ_WAKE_THREAD; +} + @@ -88,8 +94,6 @@ index 682c62c635b6..d612b3391191 100644 +{ + struct vsc_tp *tp = data; + -+ wake_up(&tp->xfer_wait); -+ + if (tp->event_notify) + tp->event_notify(tp->event_notify_context); + @@ -99,7 +103,7 @@ index 682c62c635b6..d612b3391191 100644 /* wakeup firmware and wait for response */ static int vsc_tp_wakeup_request(struct vsc_tp *tp) { -@@ -383,6 +404,37 @@ int vsc_tp_register_event_cb(struct vsc_tp *tp, vsc_tp_event_cb_t event_cb, +@@ -380,6 +401,37 @@ int vsc_tp_register_event_cb(struct vsc_tp *tp, vsc_tp_event_cb_t event_cb, } EXPORT_SYMBOL_NS_GPL(vsc_tp_register_event_cb, VSC_TP); @@ -137,7 +141,7 @@ index 682c62c635b6..d612b3391191 100644 /** * vsc_tp_intr_synchronize - synchronize vsc_tp interrupt * @tp: vsc_tp device handle -@@ -413,27 +465,6 @@ void vsc_tp_intr_disable(struct vsc_tp *tp) +@@ -410,27 +462,6 @@ void vsc_tp_intr_disable(struct vsc_tp *tp) } EXPORT_SYMBOL_NS_GPL(vsc_tp_intr_disable, VSC_TP); @@ -147,6 +151,8 @@ index 682c62c635b6..d612b3391191 100644 - - atomic_inc(&tp->assert_cnt); - +- wake_up(&tp->xfer_wait); +- - return IRQ_WAKE_THREAD; -} - @@ -154,8 +160,6 @@ index 682c62c635b6..d612b3391191 100644 -{ - struct vsc_tp *tp = data; - -- wake_up(&tp->xfer_wait); -- - if (tp->event_notify) - tp->event_notify(tp->event_notify_context); - @@ -165,7 +169,7 @@ index 682c62c635b6..d612b3391191 100644 static int vsc_tp_match_any(struct acpi_device *adev, void *data) { struct acpi_device **__adev = data; -@@ -485,10 +516,9 @@ static int vsc_tp_probe(struct spi_device *spi) +@@ -482,10 +513,9 @@ static int vsc_tp_probe(struct spi_device *spi) tp->spi = spi; irq_set_status_flags(spi->irq, IRQ_DISABLE_UNLAZY); @@ -179,7 +183,7 @@ index 682c62c635b6..d612b3391191 100644 if (ret) return ret; -@@ -522,6 +552,8 @@ static int vsc_tp_probe(struct spi_device *spi) +@@ -519,6 +549,8 @@ static int vsc_tp_probe(struct spi_device *spi) err_destroy_lock: mutex_destroy(&tp->mutex); @@ -188,7 +192,7 @@ index 682c62c635b6..d612b3391191 100644 return ret; } -@@ -532,6 +564,8 @@ static void vsc_tp_remove(struct spi_device *spi) +@@ -529,6 +561,8 @@ static void vsc_tp_remove(struct spi_device *spi) platform_device_unregister(tp->pdev); mutex_destroy(&tp->mutex); @@ -212,5 +216,5 @@ index f9513ddc3e40..14ca195cbddc 100644 void vsc_tp_intr_disable(struct vsc_tp *tp); void vsc_tp_intr_synchronize(struct vsc_tp *tp); -- -2.43.2 +2.43.0 diff --git a/patch/v6.8/0006-mei-vsc-reset-ivsc-during-shutdown.patch b/patch/v6.8/0006-mei-vsc-reset-ivsc-during-shutdown.patch index da3d49a7062d..2c7aa0131516 100644 --- a/patch/v6.8/0006-mei-vsc-reset-ivsc-during-shutdown.patch +++ b/patch/v6.8/0006-mei-vsc-reset-ivsc-during-shutdown.patch @@ -1,7 +1,7 @@ -From c30877d19866e37662d6098988a4efcdc118e02e Mon Sep 17 00:00:00 2001 +From a0816e198d3bb80809e0f1d895b4526ba1050bf3 Mon Sep 17 00:00:00 2001 From: Wentong Wu Date: Fri, 21 Jun 2024 11:02:52 +0800 -Subject: [PATCH] mei: vsc: reset ivsc during shutdown +Subject: [PATCH 06/12] mei: vsc: reset ivsc during shutdown During system shutdown, reset ivsc to have chipset in valid state. @@ -12,10 +12,10 @@ Signed-off-by: Wentong Wu 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mei/vsc-tp.c b/drivers/misc/mei/vsc-tp.c -index 870c70ef3..4033e1534 100644 +index dcf4d9bd3293..eae5370b8316 100644 --- a/drivers/misc/mei/vsc-tp.c +++ b/drivers/misc/mei/vsc-tp.c -@@ -331,12 +331,12 @@ int vsc_tp_rom_xfer(struct vsc_tp *tp, const void *obuf, void *ibuf, size_t len) +@@ -328,12 +328,12 @@ int vsc_tp_rom_xfer(struct vsc_tp *tp, const void *obuf, void *ibuf, size_t len) return ret; } @@ -30,7 +30,7 @@ index 870c70ef3..4033e1534 100644 return ret; } -@@ -349,6 +349,8 @@ void vsc_tp_reset(struct vsc_tp *tp) +@@ -346,6 +346,8 @@ void vsc_tp_reset(struct vsc_tp *tp) { disable_irq(tp->spi->irq); @@ -39,7 +39,7 @@ index 870c70ef3..4033e1534 100644 /* toggle reset pin */ gpiod_set_value_cansleep(tp->resetfw, 0); msleep(VSC_TP_RESET_PIN_TOGGLE_INTERVAL_MS); -@@ -557,6 +559,19 @@ static int vsc_tp_probe(struct spi_device *spi) +@@ -554,6 +556,19 @@ static int vsc_tp_probe(struct spi_device *spi) return ret; } @@ -59,7 +59,7 @@ index 870c70ef3..4033e1534 100644 static void vsc_tp_remove(struct spi_device *spi) { struct vsc_tp *tp = spi_get_drvdata(spi); -@@ -580,6 +595,7 @@ MODULE_DEVICE_TABLE(acpi, vsc_tp_acpi_ids); +@@ -577,6 +592,7 @@ MODULE_DEVICE_TABLE(acpi, vsc_tp_acpi_ids); static struct spi_driver vsc_tp_driver = { .probe = vsc_tp_probe, .remove = vsc_tp_remove, @@ -68,5 +68,5 @@ index 870c70ef3..4033e1534 100644 .name = "vsc-tp", .acpi_match_table = vsc_tp_acpi_ids, -- -2.34.1 +2.43.0 diff --git a/patch/v6.8/0007-mei-vsc-Don-t-stop-restart-mei-device-during-system-.patch b/patch/v6.8/0007-mei-vsc-Don-t-stop-restart-mei-device-during-system-.patch index 2d0414320eda..49f77ba41bc7 100644 --- a/patch/v6.8/0007-mei-vsc-Don-t-stop-restart-mei-device-during-system-.patch +++ b/patch/v6.8/0007-mei-vsc-Don-t-stop-restart-mei-device-during-system-.patch @@ -1,7 +1,7 @@ -From 9b5e045029d8bded4c6979874ed3abc347c1415c Mon Sep 17 00:00:00 2001 +From fb88323ce3ebd94952c479fc80faadf0fe7fc27a Mon Sep 17 00:00:00 2001 From: Wentong Wu Date: Mon, 27 May 2024 20:38:35 +0800 -Subject: [PATCH] mei: vsc: Don't stop/restart mei device during system +Subject: [PATCH 07/12] mei: vsc: Don't stop/restart mei device during system suspend/resume The dynamically created mei client device (mei csi) is used as one V4L2 @@ -29,10 +29,10 @@ Signed-off-by: Greg Kroah-Hartman 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/drivers/misc/mei/platform-vsc.c b/drivers/misc/mei/platform-vsc.c -index b543e6b9f3cf..1ec65d87488a 100644 +index 8db0fcf24e70..5a445f3b96a9 100644 --- a/drivers/misc/mei/platform-vsc.c +++ b/drivers/misc/mei/platform-vsc.c -@@ -399,41 +399,32 @@ static void mei_vsc_remove(struct platform_device *pdev) +@@ -401,41 +401,32 @@ static int mei_vsc_remove(struct platform_device *pdev) static int mei_vsc_suspend(struct device *dev) { @@ -90,5 +90,5 @@ index b543e6b9f3cf..1ec65d87488a 100644 static DEFINE_SIMPLE_DEV_PM_OPS(mei_vsc_pm_ops, mei_vsc_suspend, mei_vsc_resume); -- -2.34.1 +2.43.0 diff --git a/patch/v6.8/0008-mei-vsc-Prevent-timeout-error-with-added-delay-post-.patch b/patch/v6.8/0008-mei-vsc-Prevent-timeout-error-with-added-delay-post-.patch index 3c54e725674c..30cb2ede37ca 100644 --- a/patch/v6.8/0008-mei-vsc-Prevent-timeout-error-with-added-delay-post-.patch +++ b/patch/v6.8/0008-mei-vsc-Prevent-timeout-error-with-added-delay-post-.patch @@ -1,7 +1,7 @@ -From 8790622ea947b245bf00b00cc05784110355ecbb Mon Sep 17 00:00:00 2001 +From dfe37854c057a794ed83bc836bb7de0f2c0d738d Mon Sep 17 00:00:00 2001 From: Wentong Wu -Date: Sun, 23 Jun 2024 11:20:41 +0800 -Subject: [PATCH] mei: vsc: Prevent timeout error with added delay +Date: Tue, 25 Jun 2024 16:10:44 +0800 +Subject: [PATCH 08/12] mei: vsc: Prevent timeout error with added delay post-firmware download After completing the firmware download, the firmware requires some @@ -13,12 +13,15 @@ Fixes: 566f5ca97680 ("mei: Add transport driver for IVSC device") Cc: stable@vger.kernel.org # for 6.8+ Signed-off-by: Wentong Wu Tested-by: Jason Chen +Acked-by: Sakari Ailus +Link: https://lore.kernel.org/r/20240625081047.4178494-3-wentong.wu@intel.com +Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/platform-vsc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mei/platform-vsc.c b/drivers/misc/mei/platform-vsc.c -index 1ec65d87488a..d02f6e881139 100644 +index 5a445f3b96a9..a59fb5a13bdc 100644 --- a/drivers/misc/mei/platform-vsc.c +++ b/drivers/misc/mei/platform-vsc.c @@ -28,8 +28,8 @@ @@ -33,5 +36,5 @@ index 1ec65d87488a..d02f6e881139 100644 #define mei_dev_to_vsc_hw(dev) ((struct mei_vsc_hw *)((dev)->hw)) -- -2.34.1 +2.43.0 diff --git a/patch/v6.8/0009-media-ivsc-csi-don-t-count-privacy-on-as-error.patch b/patch/v6.8/0009-media-ivsc-csi-don-t-count-privacy-on-as-error.patch index 61546eeb8b02..b0bb6c307905 100644 --- a/patch/v6.8/0009-media-ivsc-csi-don-t-count-privacy-on-as-error.patch +++ b/patch/v6.8/0009-media-ivsc-csi-don-t-count-privacy-on-as-error.patch @@ -1,7 +1,7 @@ -From 6bc52e063e3478cf5dd538b3dc0a163111957b07 Mon Sep 17 00:00:00 2001 +From ae82b86ec2162b66a7b07b7209496d971c799907 Mon Sep 17 00:00:00 2001 From: Wentong Wu Date: Mon, 3 Jun 2024 09:44:06 +0800 -Subject: [PATCH 1/3] media: ivsc: csi: don't count privacy on as error +Subject: [PATCH 09/12] 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 @@ -18,10 +18,10 @@ Tested-by: Jason Chen 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 89b582a221ab..05c0acb78939 100644 +index 15b905f66ab7..c58826dd5a50 100644 --- a/drivers/media/pci/intel/ivsc/mei_csi.c +++ b/drivers/media/pci/intel/ivsc/mei_csi.c -@@ -190,7 +190,11 @@ static int mei_csi_send(struct mei_csi *csi, u8 *buf, size_t len) +@@ -188,7 +188,11 @@ static int mei_csi_send(struct mei_csi *csi, u8 *buf, size_t len) /* command response status */ ret = csi->cmd_response.status; @@ -35,5 +35,5 @@ index 89b582a221ab..05c0acb78939 100644 goto out; } -- -2.34.1 +2.43.0 diff --git a/patch/v6.8/0010-media-ivsc-csi-add-separate-lock-for-v4l2-control-ha.patch b/patch/v6.8/0010-media-ivsc-csi-add-separate-lock-for-v4l2-control-ha.patch index d61bd47ec5db..a5fc14b71682 100644 --- a/patch/v6.8/0010-media-ivsc-csi-add-separate-lock-for-v4l2-control-ha.patch +++ b/patch/v6.8/0010-media-ivsc-csi-add-separate-lock-for-v4l2-control-ha.patch @@ -1,7 +1,7 @@ -From 12ce4727869b46e93c8d01064621593cfb240404 Mon Sep 17 00:00:00 2001 +From 60955329017e6f64672dde27911f2ff00874c59f Mon Sep 17 00:00:00 2001 From: Wentong Wu Date: Mon, 3 Jun 2024 10:33:28 +0800 -Subject: [PATCH 2/3] media: ivsc: csi: add separate lock for v4l2 control +Subject: [PATCH 10/12] media: ivsc: csi: add separate lock for v4l2 control handler There're possibilities that privacy status change notification happens @@ -18,10 +18,10 @@ Tested-by: Jason Chen 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 05c0acb78939..a46a012b301f 100644 +index c58826dd5a50..c730865bdd8a 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 { +@@ -123,6 +123,8 @@ struct mei_csi { struct v4l2_ctrl_handler ctrl_handler; struct v4l2_ctrl *freq_ctrl; struct v4l2_ctrl *privacy_ctrl; @@ -30,7 +30,7 @@ index 05c0acb78939..a46a012b301f 100644 unsigned int remote_pad; /* start streaming or not */ int streaming; -@@ -563,11 +565,13 @@ static int mei_csi_init_controls(struct mei_csi *csi) +@@ -615,11 +617,13 @@ static int mei_csi_init_controls(struct mei_csi *csi) u32 max; int ret; @@ -45,7 +45,7 @@ index 05c0acb78939..a46a012b301f 100644 max = ARRAY_SIZE(link_freq_menu_items) - 1; csi->freq_ctrl = v4l2_ctrl_new_int_menu(&csi->ctrl_handler, -@@ -756,6 +760,7 @@ static int mei_csi_probe(struct mei_cl_device *cldev, +@@ -798,6 +802,7 @@ static int mei_csi_probe(struct mei_cl_device *cldev, err_ctrl_handler: v4l2_ctrl_handler_free(&csi->ctrl_handler); @@ -53,7 +53,7 @@ index 05c0acb78939..a46a012b301f 100644 v4l2_async_nf_unregister(&csi->notifier); v4l2_async_nf_cleanup(&csi->notifier); -@@ -775,6 +780,7 @@ static void mei_csi_remove(struct mei_cl_device *cldev) +@@ -817,6 +822,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); @@ -62,5 +62,5 @@ index 05c0acb78939..a46a012b301f 100644 v4l2_subdev_cleanup(&csi->subdev); media_entity_cleanup(&csi->subdev.entity); -- -2.34.1 +2.43.0 diff --git a/patch/v6.8/0011-media-ivsc-csi-remove-privacy-status-in-struct-mei_c.patch b/patch/v6.8/0011-media-ivsc-csi-remove-privacy-status-in-struct-mei_c.patch index 713093976635..92f3a6806f5c 100644 --- a/patch/v6.8/0011-media-ivsc-csi-remove-privacy-status-in-struct-mei_c.patch +++ b/patch/v6.8/0011-media-ivsc-csi-remove-privacy-status-in-struct-mei_c.patch @@ -1,7 +1,8 @@ -From 5611cf035ba882092d9c708420910153f332902b Mon Sep 17 00:00:00 2001 +From 3739cc06760919adfa44f10dcb6829a95834722a Mon Sep 17 00:00:00 2001 From: Wentong Wu Date: Mon, 3 Jun 2024 10:57:24 +0800 -Subject: [PATCH 3/3] media: ivsc: csi: remove privacy status in struct mei_csi +Subject: [PATCH 11/12] 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 @@ -15,10 +16,10 @@ Tested-by: Jason Chen 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 a46a012b301f..bff0176fc8f1 100644 +index c730865bdd8a..8a905ba89b57 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 { +@@ -136,9 +136,6 @@ struct mei_csi { u32 nr_of_lanes; /* frequency of the CSI-2 link */ u64 link_freq; @@ -28,7 +29,7 @@ index a46a012b301f..bff0176fc8f1 100644 }; 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) +@@ -269,10 +266,9 @@ static void mei_csi_rx(struct mei_cl_device *cldev) switch (notif.cmd_id) { case CSI_PRIVACY_NOTIF: @@ -43,5 +44,5 @@ index a46a012b301f..bff0176fc8f1 100644 case CSI_SET_OWNER: case CSI_SET_CONF: -- -2.34.1 +2.43.0 diff --git a/patch/v6.8/0012-media-Support-ov05c10-camera-sensor.patch b/patch/v6.8/0012-media-Support-ov05c10-camera-sensor.patch new file mode 100644 index 000000000000..8d2a21ff2d64 --- /dev/null +++ b/patch/v6.8/0012-media-Support-ov05c10-camera-sensor.patch @@ -0,0 +1,62 @@ +From 7a177f98e126861ab36623336e855be1541d3690 Mon Sep 17 00:00:00 2001 +From: Dongcheng Yan +Date: Thu, 25 Apr 2024 17:33:09 +0800 +Subject: [PATCH 12/12] media: Support ov05c10 camera sensor + +Signed-off-by: Dongcheng Yan +--- + drivers/media/i2c/Kconfig | 11 +++++++++++ + drivers/media/i2c/Makefile | 1 + + drivers/media/pci/intel/ipu-bridge.c | 2 ++ + 3 files changed, 14 insertions(+) + +diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig +index 6b5c33bcec7f..be1c61deff9d 100644 +--- a/drivers/media/i2c/Kconfig ++++ b/drivers/media/i2c/Kconfig +@@ -373,6 +373,17 @@ config VIDEO_OV02E10 + This is a Video4Linux2 sensor driver for the OmniVision + ov02e10 camera. + ++config VIDEO_OV05C10 ++ tristate "OmniVision OV05C10 sensor support" ++ depends on ACPI || COMPILE_TEST ++ select V4L2_CCI_I2C ++ help ++ This is a Video4Linux2 sensor driver for the OmniVision ++ OV05C10 camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ov05c10. ++ + config VIDEO_OV08A10 + tristate "OmniVision OV08A10 sensor support" + help +diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile +index 32dd623c6c81..2c1765d5650c 100644 +--- a/drivers/media/i2c/Makefile ++++ b/drivers/media/i2c/Makefile +@@ -83,6 +83,7 @@ obj-$(CONFIG_VIDEO_OV01A1S) += ov01a1s.o + obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o + obj-$(CONFIG_VIDEO_OV02C10) += ov02c10.o + obj-$(CONFIG_VIDEO_OV02E10) += ov02e10.o ++obj-$(CONFIG_VIDEO_OV05C10) += ov05c10.o + obj-$(CONFIG_VIDEO_OV08A10) += ov08a10.o + obj-$(CONFIG_VIDEO_OV08D10) += ov08d10.o + obj-$(CONFIG_VIDEO_OV08X40) += ov08x40.o +diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c +index 2ac59e02b839..f6907740ffdf 100644 +--- a/drivers/media/pci/intel/ipu-bridge.c ++++ b/drivers/media/pci/intel/ipu-bridge.c +@@ -77,6 +77,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { + IPU_SENSOR_CONFIG("OVTI02C1", 1, 400000000), + /* Omnivision ov02e10 */ + IPU_SENSOR_CONFIG("OVTI02E1", 1, 360000000), ++ /* Omnivision ov05c10 */ ++ IPU_SENSOR_CONFIG("OVTI05C1", 1, 480000000), + /* Omnivision ov08a10 */ + IPU_SENSOR_CONFIG("OVTI08A1", 1, 500000000), + }; +-- +2.43.0 +