* Re: [PATCH 1/3] media: mediatek: vcodec: fix h264 multi statless decoder smatch warning
From: Yunfei Dong (董云飞) @ 2024-04-03 3:45 UTC (permalink / raw)
To: nhebert@chromium.org, benjamin.gaignard@collabora.com,
nfraprado@collabora.com, angelogioacchino.delregno@collabora.com,
nicolas.dufresne@collabora.com, hverkuil-cisco@xs4all.nl
Cc: linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org,
frkoenig@chromium.org, stevecho@chromium.org,
linux-media@vger.kernel.org, devicetree@vger.kernel.org,
daniel@ffwll.ch, Project_Global_Chrome_Upstream_Group,
hsinyi@chromium.org, linux-arm-kernel@lists.infradead.org
In-Reply-To: <4949bd54-8c32-4490-ab19-d38796d29ac1@collabora.com>
Hi AngeloGioacchino,
Thanks for your reviewing.
On Tue, 2024-04-02 at 11:50 +0200, AngeloGioacchino Del Regno wrote:
> Il 29/02/24 10:56, Yunfei Dong ha scritto:
> > Fix smatch static checker warning for vdec_h264_req_multi_if.c.
> > Leading to kernel crash when fb is NULL.
> >
> > Fixes: 397edc703a10 ("media: mediatek: vcodec: add h264 decoder")
> > Signed-off-by: Yunfei Dong <yunfei.dong@mediatek.com>
> > ---
> > .../vcodec/decoder/vdec/vdec_h264_req_multi_if.c | 9
> > +++++++--
> > 1 file changed, 7 insertions(+), 2 deletions(-)
> >
> > diff --git
> > a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req
> > _multi_if.c
> > b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req
> > _multi_if.c
> > index 0e741e0dc8ba..ab8e708e0df1 100644
> > ---
> > a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req
> > _multi_if.c
> > +++
> > b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req
> > _multi_if.c
> > @@ -724,11 +724,16 @@ static int vdec_h264_slice_single_decode(void
> > *h_vdec, struct mtk_vcodec_mem *bs
> > return vpu_dec_reset(vpu);
> >
> > fb = inst->ctx->dev->vdec_pdata->get_cap_buffer(inst->ctx);
> > + if (!fb) {
> > + mtk_vdec_err(inst->ctx, "fb buffer is NULL");
> > + return -EBUSY;
> > + }
> > +
> > src_buf_info = container_of(bs, struct mtk_video_dec_buf,
> > bs_buffer);
> > dst_buf_info = container_of(fb, struct mtk_video_dec_buf,
> > frame_buffer);
> >
> > - y_fb_dma = fb ? (u64)fb->base_y.dma_addr : 0;
> > - c_fb_dma = fb ? (u64)fb->base_c.dma_addr : 0;
>
> You're changing the behavior here, can you please explain why this
> change is valid
> into the commit description?
>
The driver already add the condition to check whether fb is NULL at the
front, no need these two lines again.
> Thanks,
> Angelo
>
Best Regards,
Yunfei Dong
> > + y_fb_dma = (u64)fb->base_y.dma_addr;
> > + c_fb_dma = (u64)fb->base_c.dma_addr;
> > mtk_vdec_debug(inst->ctx, "[h264-dec] [%d] y_dma=%llx
> > c_dma=%llx",
> > inst->ctx->decoded_frame_cnt, y_fb_dma,
> > c_fb_dma);
> >
>
>
>
^ permalink raw reply
* [PATCH v4 4/4] drm: panel: Add LG sw43408 panel driver
From: Dmitry Baryshkov @ 2024-04-03 3:43 UTC (permalink / raw)
To: Sumit Semwal, Caleb Connolly, Neil Armstrong, Jessica Zhang,
Sam Ravnborg, David Airlie, Daniel Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: dri-devel, devicetree, linux-kernel, linux-arm-msm, Vinod Koul,
Caleb Connolly, Marijn Suijten
In-Reply-To: <20240403-lg-sw43408-panel-v4-0-a386d5d3b0c6@linaro.org>
From: Sumit Semwal <sumit.semwal@linaro.org>
LG SW43408 is 1080x2160, 4-lane MIPI-DSI panel, used in some Pixel3
phones.
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
[vinod: Add DSC support]
Signed-off-by: Vinod Koul <vkoul@kernel.org>
[caleb: cleanup and support turning off the panel]
Signed-off-by: Caleb Connolly <caleb@connolly.tech>
[DB: partially rewrote the driver and fixed DSC programming]
Reviewed-by: Marijn Suijten <marijn.suijten@somainline.org>
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
MAINTAINERS | 8 +
drivers/gpu/drm/panel/Kconfig | 11 ++
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-lg-sw43408.c | 323 +++++++++++++++++++++++++++++++
4 files changed, 343 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index d36c19c1bf81..4cc43c16e07e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6789,6 +6789,14 @@ S: Maintained
F: Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml
F: drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c
+DRM DRIVER FOR LG SW43408 PANELS
+M: Sumit Semwal <sumit.semwal@linaro.org>
+M: Caleb Connolly <caleb.connolly@linaro.org>
+S: Maintained
+T: git git://anongit.freedesktop.org/drm/drm-misc
+F: Documentation/devicetree/bindings/display/panel/lg,sw43408.yaml
+F: drivers/gpu/drm/panel/panel-lg-sw43408.c
+
DRM DRIVER FOR LOGICVC DISPLAY CONTROLLER
M: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
S: Supported
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 6dc451f58a3e..a55e9437c8cf 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -335,6 +335,17 @@ config DRM_PANEL_LG_LG4573
Say Y here if you want to enable support for LG4573 RGB panel.
To compile this driver as a module, choose M here.
+config DRM_PANEL_LG_SW43408
+ tristate "LG SW43408 panel"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y here if you want to enable support for LG sw43408 panel.
+ The panel has a 1080x2160 resolution and uses
+ 24 bit RGB per pixel. It provides a MIPI DSI interface to
+ the host and has a built-in LED backlight.
+
config DRM_PANEL_MAGNACHIP_D53E6EA8966
tristate "Magnachip D53E6EA8966 DSI panel"
depends on OF && SPI
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 24a02655d726..0b40b010e8e7 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_DRM_PANEL_LEADTEK_LTK050H3146W) += panel-leadtek-ltk050h3146w.o
obj-$(CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829) += panel-leadtek-ltk500hd1829.o
obj-$(CONFIG_DRM_PANEL_LG_LB035Q02) += panel-lg-lb035q02.o
obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
+obj-$(CONFIG_DRM_PANEL_LG_SW43408) += panel-lg-sw43408.o
obj-$(CONFIG_DRM_PANEL_MAGNACHIP_D53E6EA8966) += panel-magnachip-d53e6ea8966.o
obj-$(CONFIG_DRM_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
obj-$(CONFIG_DRM_PANEL_NEWVISION_NV3051D) += panel-newvision-nv3051d.o
diff --git a/drivers/gpu/drm/panel/panel-lg-sw43408.c b/drivers/gpu/drm/panel/panel-lg-sw43408.c
new file mode 100644
index 000000000000..6fcdaa5cf6c5
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-lg-sw43408.c
@@ -0,0 +1,323 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019-2024 Linaro Ltd
+ * Author: Sumit Semwal <sumit.semwal@linaro.org>
+ * Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/display/drm_dsc.h>
+#include <drm/display/drm_dsc_helper.h>
+
+#define NUM_SUPPLIES 2
+
+struct sw43408_panel {
+ struct drm_panel base;
+ struct mipi_dsi_device *link;
+
+ struct regulator_bulk_data supplies[NUM_SUPPLIES];
+
+ struct gpio_desc *reset_gpio;
+
+ struct drm_dsc_config dsc;
+};
+
+static inline struct sw43408_panel *to_panel_info(struct drm_panel *panel)
+{
+ return container_of(panel, struct sw43408_panel, base);
+}
+
+static int sw43408_unprepare(struct drm_panel *panel)
+{
+ struct sw43408_panel *ctx = to_panel_info(panel);
+ int ret;
+
+ ret = mipi_dsi_dcs_set_display_off(ctx->link);
+ if (ret < 0)
+ dev_err(panel->dev, "set_display_off cmd failed ret = %d\n", ret);
+
+ ret = mipi_dsi_dcs_enter_sleep_mode(ctx->link);
+ if (ret < 0)
+ dev_err(panel->dev, "enter_sleep cmd failed ret = %d\n", ret);
+
+ msleep(100);
+
+ gpiod_set_value(ctx->reset_gpio, 1);
+
+ return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+}
+
+static int sw43408_program(struct drm_panel *panel)
+{
+ struct sw43408_panel *ctx = to_panel_info(panel);
+ struct drm_dsc_picture_parameter_set pps;
+
+ mipi_dsi_dcs_write_seq(ctx->link, MIPI_DCS_SET_GAMMA_CURVE, 0x02);
+
+ mipi_dsi_dcs_set_tear_on(ctx->link, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+
+ mipi_dsi_dcs_write_seq(ctx->link, 0x53, 0x0c, 0x30);
+ mipi_dsi_dcs_write_seq(ctx->link, 0x55, 0x00, 0x70, 0xdf, 0x00, 0x70, 0xdf);
+ mipi_dsi_dcs_write_seq(ctx->link, 0xf7, 0x01, 0x49, 0x0c);
+
+ mipi_dsi_dcs_exit_sleep_mode(ctx->link);
+
+ msleep(135);
+
+ /* COMPRESSION_MODE moved after setting the PPS */
+
+ mipi_dsi_dcs_write_seq(ctx->link, 0xb0, 0xac);
+ mipi_dsi_dcs_write_seq(ctx->link, 0xe5,
+ 0x00, 0x3a, 0x00, 0x3a, 0x00, 0x0e, 0x10);
+ mipi_dsi_dcs_write_seq(ctx->link, 0xb5,
+ 0x75, 0x60, 0x2d, 0x5d, 0x80, 0x00, 0x0a, 0x0b,
+ 0x00, 0x05, 0x0b, 0x00, 0x80, 0x0d, 0x0e, 0x40,
+ 0x00, 0x0c, 0x00, 0x16, 0x00, 0xb8, 0x00, 0x80,
+ 0x0d, 0x0e, 0x40, 0x00, 0x0c, 0x00, 0x16, 0x00,
+ 0xb8, 0x00, 0x81, 0x00, 0x03, 0x03, 0x03, 0x01,
+ 0x01);
+ msleep(85);
+ mipi_dsi_dcs_write_seq(ctx->link, 0xcd,
+ 0x00, 0x00, 0x00, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x16, 0x16);
+ mipi_dsi_dcs_write_seq(ctx->link, 0xcb, 0x80, 0x5c, 0x07, 0x03, 0x28);
+ mipi_dsi_dcs_write_seq(ctx->link, 0xc0, 0x02, 0x02, 0x0f);
+ mipi_dsi_dcs_write_seq(ctx->link, 0x55, 0x04, 0x61, 0xdb, 0x04, 0x70, 0xdb);
+ mipi_dsi_dcs_write_seq(ctx->link, 0xb0, 0xca);
+
+ mipi_dsi_dcs_set_display_on(ctx->link);
+
+ msleep(50);
+
+ ctx->link->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ drm_dsc_pps_payload_pack(&pps, ctx->link->dsc);
+ mipi_dsi_picture_parameter_set(ctx->link, &pps);
+
+ ctx->link->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ /*
+ * This panel uses PPS selectors with offset:
+ * PPS 1 if pps_identifier is 0
+ * PPS 2 if pps_identifier is 1
+ */
+ mipi_dsi_compression_mode_ext(ctx->link, true,
+ MIPI_DSI_COMPRESSION_DSC, 1);
+
+
+ return 0;
+}
+
+static int sw43408_prepare(struct drm_panel *panel)
+{
+ struct sw43408_panel *ctx = to_panel_info(panel);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(5000, 6000);
+
+ gpiod_set_value(ctx->reset_gpio, 0);
+ usleep_range(9000, 10000);
+ gpiod_set_value(ctx->reset_gpio, 1);
+ usleep_range(1000, 2000);
+ gpiod_set_value(ctx->reset_gpio, 0);
+ usleep_range(9000, 10000);
+
+ ret = sw43408_program(panel);
+ if (ret)
+ goto poweroff;
+
+ return 0;
+
+poweroff:
+ gpiod_set_value(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+ return ret;
+}
+
+static const struct drm_display_mode sw43408_mode = {
+ .clock = (1080 + 20 + 32 + 20) * (2160 + 20 + 4 + 20) * 60 / 1000,
+
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 20,
+ .hsync_end = 1080 + 20 + 32,
+ .htotal = 1080 + 20 + 32 + 20,
+
+ .vdisplay = 2160,
+ .vsync_start = 2160 + 20,
+ .vsync_end = 2160 + 20 + 4,
+ .vtotal = 2160 + 20 + 4 + 20,
+
+ .width_mm = 62,
+ .height_mm = 124,
+
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+};
+
+static int sw43408_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ return drm_connector_helper_get_modes_fixed(connector, &sw43408_mode);
+}
+
+static int sw43408_backlight_update_status(struct backlight_device *bl)
+{
+ struct mipi_dsi_device *dsi = bl_get_data(bl);
+ uint16_t brightness = backlight_get_brightness(bl);
+
+ return mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
+}
+
+const struct backlight_ops sw43408_backlight_ops = {
+ .update_status = sw43408_backlight_update_status,
+};
+
+static int sw43408_backlight_init(struct sw43408_panel *ctx)
+{
+ struct device *dev = &ctx->link->dev;
+ const struct backlight_properties props = {
+ .type = BACKLIGHT_PLATFORM,
+ .brightness = 255,
+ .max_brightness = 255,
+ };
+
+ ctx->base.backlight = devm_backlight_device_register(dev, dev_name(dev), dev,
+ ctx->link,
+ &sw43408_backlight_ops,
+ &props);
+
+ if (IS_ERR(ctx->base.backlight))
+ return dev_err_probe(dev, PTR_ERR(ctx->base.backlight),
+ "Failed to create backlight\n");
+
+ return 0;
+}
+
+static const struct drm_panel_funcs sw43408_funcs = {
+ .unprepare = sw43408_unprepare,
+ .prepare = sw43408_prepare,
+ .get_modes = sw43408_get_modes,
+};
+
+static const struct of_device_id sw43408_of_match[] = {
+ { .compatible = "lg,sw43408", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sw43408_of_match);
+
+static int sw43408_add(struct sw43408_panel *ctx)
+{
+ struct device *dev = &ctx->link->dev;
+ int ret;
+
+ ctx->supplies[0].supply = "vddi"; /* 1.88 V */
+ ctx->supplies[0].init_load_uA = 62000;
+ ctx->supplies[1].supply = "vpnl"; /* 3.0 V */
+ ctx->supplies[1].init_load_uA = 857000;
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
+ ctx->supplies);
+ if (ret < 0)
+ return ret;
+
+ ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ctx->reset_gpio)) {
+ dev_err(dev, "cannot get reset gpio %ld\n",
+ PTR_ERR(ctx->reset_gpio));
+ return PTR_ERR(ctx->reset_gpio);
+ }
+
+ ret = sw43408_backlight_init(ctx);
+ if (ret < 0)
+ return ret;
+
+ ctx->base.prepare_prev_first = true;
+
+ drm_panel_init(&ctx->base, dev, &sw43408_funcs, DRM_MODE_CONNECTOR_DSI);
+
+ drm_panel_add(&ctx->base);
+ return ret;
+}
+
+static int sw43408_probe(struct mipi_dsi_device *dsi)
+{
+ struct sw43408_panel *ctx;
+ int ret;
+
+ ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ dsi->mode_flags = MIPI_DSI_MODE_LPM;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->lanes = 4;
+
+ ctx->link = dsi;
+ mipi_dsi_set_drvdata(dsi, ctx);
+
+ ret = sw43408_add(ctx);
+ if (ret < 0)
+ return ret;
+
+ /* The panel is DSC panel only, set the dsc params */
+ ctx->dsc.dsc_version_major = 0x1;
+ ctx->dsc.dsc_version_minor = 0x1;
+
+ /* slice_count * slice_width == width */
+ ctx->dsc.slice_height = 16;
+ ctx->dsc.slice_width = 540;
+ ctx->dsc.slice_count = 2;
+ ctx->dsc.bits_per_component = 8;
+ ctx->dsc.bits_per_pixel = 8 << 4;
+ ctx->dsc.block_pred_enable = true;
+
+ dsi->dsc = &ctx->dsc;
+
+ return mipi_dsi_attach(dsi);
+}
+
+static void sw43408_remove(struct mipi_dsi_device *dsi)
+{
+ struct sw43408_panel *ctx = mipi_dsi_get_drvdata(dsi);
+ int ret;
+
+ ret = sw43408_unprepare(&ctx->base);
+ if (ret < 0)
+ dev_err(&dsi->dev, "failed to unprepare panel: %d\n",
+ ret);
+
+ ret = mipi_dsi_detach(dsi);
+ if (ret < 0)
+ dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
+
+ drm_panel_remove(&ctx->base);
+}
+
+static struct mipi_dsi_driver sw43408_driver = {
+ .driver = {
+ .name = "panel-lg-sw43408",
+ .of_match_table = sw43408_of_match,
+ },
+ .probe = sw43408_probe,
+ .remove = sw43408_remove,
+};
+module_mipi_dsi_driver(sw43408_driver);
+
+MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");
+MODULE_DESCRIPTION("LG SW436408 MIPI-DSI LED panel");
+MODULE_LICENSE("GPL");
--
2.39.2
^ permalink raw reply related
* [PATCH v4 3/4] drm/mipi-dsi: add mipi_dsi_compression_mode_ext()
From: Dmitry Baryshkov @ 2024-04-03 3:43 UTC (permalink / raw)
To: Sumit Semwal, Caleb Connolly, Neil Armstrong, Jessica Zhang,
Sam Ravnborg, David Airlie, Daniel Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: dri-devel, devicetree, linux-kernel, linux-arm-msm
In-Reply-To: <20240403-lg-sw43408-panel-v4-0-a386d5d3b0c6@linaro.org>
Add the extended version of mipi_dsi_compression_mode(). It provides
a way to specify the algorithm and PPS selector.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
drivers/gpu/drm/drm_mipi_dsi.c | 41 ++++++++++++++++++++++++++++++++++-------
include/drm/drm_mipi_dsi.h | 9 +++++++++
2 files changed, 43 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index 9874ff6d4718..795001bb7ff1 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -645,29 +645,56 @@ int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
/**
- * mipi_dsi_compression_mode() - enable/disable DSC on the peripheral
+ * mipi_dsi_compression_mode_ext() - enable/disable DSC on the peripheral
* @dsi: DSI peripheral device
* @enable: Whether to enable or disable the DSC
+ * @algo: Selected compression algorithm
+ * @pps_selector: Select PPS from the table of pre-stored or uploaded PPS entries
*
- * Enable or disable Display Stream Compression on the peripheral using the
- * default Picture Parameter Set and VESA DSC 1.1 algorithm.
+ * Enable or disable Display Stream Compression on the peripheral.
*
* Return: 0 on success or a negative error code on failure.
*/
-int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
+int mipi_dsi_compression_mode_ext(struct mipi_dsi_device *dsi, bool enable,
+ enum mipi_dsi_compression_algo algo,
+ unsigned int pps_selector)
{
- /* Note: Needs updating for non-default PPS or algorithm */
- u8 tx[2] = { enable << 0, 0 };
+ u8 tx[2] = { };
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
.type = MIPI_DSI_COMPRESSION_MODE,
.tx_len = sizeof(tx),
.tx_buf = tx,
};
- int ret = mipi_dsi_device_transfer(dsi, &msg);
+ int ret;
+
+ if (algo > 3 || pps_selector > 3)
+ return -EINVAL;
+
+ tx[0] = (enable << 0) |
+ (algo << 1) |
+ (pps_selector << 4);
+
+ ret = mipi_dsi_device_transfer(dsi, &msg);
return (ret < 0) ? ret : 0;
}
+EXPORT_SYMBOL(mipi_dsi_compression_mode_ext);
+
+/**
+ * mipi_dsi_compression_mode() - enable/disable DSC on the peripheral
+ * @dsi: DSI peripheral device
+ * @enable: Whether to enable or disable the DSC
+ *
+ * Enable or disable Display Stream Compression on the peripheral using the
+ * default Picture Parameter Set and VESA DSC 1.1 algorithm.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
+{
+ return mipi_dsi_compression_mode_ext(dsi, enable, MIPI_DSI_COMPRESSION_DSC, 0);
+}
EXPORT_SYMBOL(mipi_dsi_compression_mode);
/**
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index 3011d33eccbd..82b1cc434ea3 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -226,6 +226,12 @@ static inline int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt)
return -EINVAL;
}
+enum mipi_dsi_compression_algo {
+ MIPI_DSI_COMPRESSION_DSC = 0,
+ MIPI_DSI_COMPRESSION_VENDOR = 3,
+ /* other two values are reserved, DSI 1.3 */
+};
+
struct mipi_dsi_device *
mipi_dsi_device_register_full(struct mipi_dsi_host *host,
const struct mipi_dsi_device_info *info);
@@ -242,6 +248,9 @@ int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi);
int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
u16 value);
int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable);
+int mipi_dsi_compression_mode_ext(struct mipi_dsi_device *dsi, bool enable,
+ enum mipi_dsi_compression_algo algo,
+ unsigned int pps_selector);
int mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
const struct drm_dsc_picture_parameter_set *pps);
--
2.39.2
^ permalink raw reply related
* [PATCH v4 2/4] drm/mipi-dsi: use correct return type for the DSC functions
From: Dmitry Baryshkov @ 2024-04-03 3:43 UTC (permalink / raw)
To: Sumit Semwal, Caleb Connolly, Neil Armstrong, Jessica Zhang,
Sam Ravnborg, David Airlie, Daniel Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: dri-devel, devicetree, linux-kernel, linux-arm-msm,
Marijn Suijten
In-Reply-To: <20240403-lg-sw43408-panel-v4-0-a386d5d3b0c6@linaro.org>
The functions mipi_dsi_compression_mode() and
mipi_dsi_picture_parameter_set() return 0-or-error rather than a buffer
size. Follow example of other similar MIPI DSI functions and use int
return type instead of size_t.
Fixes: f4dea1aaa9a1 ("drm/dsi: add helpers for DSI compression mode and PPS packets")
Reviewed-by: Marijn Suijten <marijn.suijten@somainline.org>
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
drivers/gpu/drm/drm_mipi_dsi.c | 6 +++---
include/drm/drm_mipi_dsi.h | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index ef6e416522f8..9874ff6d4718 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -654,7 +654,7 @@ EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
*
* Return: 0 on success or a negative error code on failure.
*/
-ssize_t mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
+int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
{
/* Note: Needs updating for non-default PPS or algorithm */
u8 tx[2] = { enable << 0, 0 };
@@ -679,8 +679,8 @@ EXPORT_SYMBOL(mipi_dsi_compression_mode);
*
* Return: 0 on success or a negative error code on failure.
*/
-ssize_t mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
- const struct drm_dsc_picture_parameter_set *pps)
+int mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
+ const struct drm_dsc_picture_parameter_set *pps)
{
struct mipi_dsi_msg msg = {
.channel = dsi->channel,
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index c0aec0d4d664..3011d33eccbd 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -241,9 +241,9 @@ int mipi_dsi_shutdown_peripheral(struct mipi_dsi_device *dsi);
int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi);
int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
u16 value);
-ssize_t mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable);
-ssize_t mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
- const struct drm_dsc_picture_parameter_set *pps);
+int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable);
+int mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
+ const struct drm_dsc_picture_parameter_set *pps);
ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload,
size_t size);
--
2.39.2
^ permalink raw reply related
* [PATCH v4 1/4] dt-bindings: panel: Add LG SW43408 MIPI-DSI panel
From: Dmitry Baryshkov @ 2024-04-03 3:43 UTC (permalink / raw)
To: Sumit Semwal, Caleb Connolly, Neil Armstrong, Jessica Zhang,
Sam Ravnborg, David Airlie, Daniel Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: dri-devel, devicetree, linux-kernel, linux-arm-msm, Vinod Koul,
Caleb Connolly, Krzysztof Kozlowski
In-Reply-To: <20240403-lg-sw43408-panel-v4-0-a386d5d3b0c6@linaro.org>
From: Sumit Semwal <sumit.semwal@linaro.org>
LG SW43408 is 1080x2160, 4-lane MIPI-DSI panel present on Google Pixel 3
phones.
Signed-off-by: Vinod Koul <vkoul@kernel.org>
Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
[caleb: convert to yaml]
Signed-off-by: Caleb Connolly <caleb@connolly.tech>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
.../bindings/display/panel/lg,sw43408.yaml | 62 ++++++++++++++++++++++
1 file changed, 62 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/panel/lg,sw43408.yaml b/Documentation/devicetree/bindings/display/panel/lg,sw43408.yaml
new file mode 100644
index 000000000000..1e08648f5bc7
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/lg,sw43408.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/lg,sw43408.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: LG SW43408 1080x2160 DSI panel
+
+maintainers:
+ - Caleb Connolly <caleb.connolly@linaro.org>
+
+description:
+ This panel is used on the Pixel 3, it is a 60hz OLED panel which
+ required DSC (Display Stream Compression) and has rounded corners.
+
+allOf:
+ - $ref: panel-common.yaml#
+
+properties:
+ compatible:
+ items:
+ - const: lg,sw43408
+
+ reg: true
+ port: true
+ vddi-supply: true
+ vpnl-supply: true
+ reset-gpios: true
+
+required:
+ - compatible
+ - vddi-supply
+ - vpnl-supply
+ - reset-gpios
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel@0 {
+ compatible = "lg,sw43408";
+ reg = <0>;
+
+ vddi-supply = <&vreg_l14a_1p88>;
+ vpnl-supply = <&vreg_l28a_3p0>;
+
+ reset-gpios = <&tlmm 6 GPIO_ACTIVE_LOW>;
+
+ port {
+ endpoint {
+ remote-endpoint = <&mdss_dsi0_out>;
+ };
+ };
+ };
+ };
+...
--
2.39.2
^ permalink raw reply related
* [PATCH v4 0/4] drm/panel: add support for LG SW43408 panel
From: Dmitry Baryshkov @ 2024-04-03 3:43 UTC (permalink / raw)
To: Sumit Semwal, Caleb Connolly, Neil Armstrong, Jessica Zhang,
Sam Ravnborg, David Airlie, Daniel Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: dri-devel, devicetree, linux-kernel, linux-arm-msm, Vinod Koul,
Caleb Connolly, Krzysztof Kozlowski, Marijn Suijten
The LG SW43408 panel is used on Google Pixel3 devices. For a long time
we could not submit the driver, as the panel was not coming up from the
reset. The panel seems to be picky about some of the delays during init
and it also uses non-standard payload for MIPI_DSI_COMPRESSION_MODE.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
---
Changes in v4:
- Fix order of mipi_dsi_compression_mode_ext() args (Marijn)
- Expanded kerneldoc coments for this function (Marijn)
- And added arguments validation (Marijn)
- In the panel driver send the COMPRESSION_MODE in LPM mode like it was
done originally
- Expanded the .clock maths to show the reason behind the value (Marijn)
- Moved the mode out of the match data (Marijn)
- Link to v3: https://lore.kernel.org/r/20240402-lg-sw43408-panel-v3-0-144f17a11a56@linaro.org
Changes in v3:
- Fixed return type of MIPI DSC functions
- Replaced mipi_dsi_compression_mode_raw() with
mipi_dsi_compression_mode_ext() (Marijn)
- Link to v2: https://lore.kernel.org/r/20240330-lg-sw43408-panel-v2-0-293a58717b38@linaro.org
Changes in v2:
- Removed formatting char from schema (Krzysztof)
- Moved additionalProperties after required (Krzysztof)
- Added example to the schema (Krzysztof)
- Removed obsolete comment in the commit message (Marijn)
- Moved DSC params to the panel struct (Marijn)
- Changed dsc_en to be an array (Marijn)
- Added comment regiarding slice_width and slice_count (Marijn)
- Link to v1: https://lore.kernel.org/r/20240330-lg-sw43408-panel-v1-0-f5580fc9f2da@linaro.org
---
Dmitry Baryshkov (2):
drm/mipi-dsi: use correct return type for the DSC functions
drm/mipi-dsi: add mipi_dsi_compression_mode_ext()
Sumit Semwal (2):
dt-bindings: panel: Add LG SW43408 MIPI-DSI panel
drm: panel: Add LG sw43408 panel driver
.../bindings/display/panel/lg,sw43408.yaml | 62 ++++
MAINTAINERS | 8 +
drivers/gpu/drm/drm_mipi_dsi.c | 45 ++-
drivers/gpu/drm/panel/Kconfig | 11 +
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-lg-sw43408.c | 323 +++++++++++++++++++++
include/drm/drm_mipi_dsi.h | 15 +-
7 files changed, 453 insertions(+), 12 deletions(-)
---
base-commit: a6bd6c9333397f5a0e2667d4d82fef8c970108f2
change-id: 20240330-lg-sw43408-panel-b552f411c53e
Best regards,
--
Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
^ permalink raw reply
* [PATCH v3 2/2] media: i2c: Add GC05A2 image sensor driver
From: Zhi Mao @ 2024-04-03 3:38 UTC (permalink / raw)
To: mchehab, robh+dt, krzysztof.kozlowski+dt, sakari.ailus
Cc: laurent.pinchart, shengnan.wang, yaya.chang,
Project_Global_Chrome_Upstream_Group, yunkec, conor+dt,
matthias.bgg, angelogioacchino.delregno, jacopo.mondi, zhi.mao,
10572168, hverkuil-cisco, heiko, jernej.skrabec, macromorgan,
linus.walleij, hdegoede, tomi.valkeinen, gerald.loacker,
andy.shevchenko, bingbu.cao, dan.scally, linux-media, devicetree,
linux-kernel, linux-arm-kernel, linux-mediatek
In-Reply-To: <20240403033825.9072-1-zhi.mao@mediatek.com>
Add a V4L2 sub-device driver for Galaxycore GC05A2 image sensor.
Signed-off-by: Zhi Mao <zhi.mao@mediatek.com>
---
drivers/media/i2c/Kconfig | 10 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/gc05a2.c | 1383 ++++++++++++++++++++++++++++++++++++
3 files changed, 1394 insertions(+)
create mode 100644 drivers/media/i2c/gc05a2.c
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 56f276b920ab..97993bf160f9 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -70,6 +70,16 @@ config VIDEO_GC0308
To compile this driver as a module, choose M here: the
module will be called gc0308.
+config VIDEO_GC05A2
+ tristate "GalaxyCore gc05a2 sensor support"
+ select V4L2_CCI_I2C
+ help
+ This is a Video4Linux2 sensor driver for the GalaxyCore gc05a2
+ camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gc05a2.
+
config VIDEO_GC2145
select V4L2_CCI_I2C
tristate "GalaxyCore GC2145 sensor support"
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index dfbe6448b549..8ed6faf0f854 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_VIDEO_DW9768) += dw9768.o
obj-$(CONFIG_VIDEO_DW9807_VCM) += dw9807-vcm.o
obj-$(CONFIG_VIDEO_ET8EK8) += et8ek8/
obj-$(CONFIG_VIDEO_GC0308) += gc0308.o
+obj-$(CONFIG_VIDEO_GC05A2) += gc05a2.o
obj-$(CONFIG_VIDEO_GC2145) += gc2145.o
obj-$(CONFIG_VIDEO_HI556) += hi556.o
obj-$(CONFIG_VIDEO_HI846) += hi846.o
diff --git a/drivers/media/i2c/gc05a2.c b/drivers/media/i2c/gc05a2.c
new file mode 100644
index 000000000000..461d33055a3b
--- /dev/null
+++ b/drivers/media/i2c/gc05a2.c
@@ -0,0 +1,1383 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for GalaxyCore gc05a2 image sensor
+ *
+ * Copyright 2024 MediaTek
+ *
+ * Zhi Mao <zhi.mao@mediatek.com>
+ */
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/math64.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define GC05A2_REG_TEST_PATTERN_EN CCI_REG8(0x008c)
+#define GC05A2_REG_TEST_PATTERN_IDX CCI_REG8(0x008d)
+#define GC05A2_TEST_PATTERN_EN 0x01
+
+#define GC05A2_STREAMING_REG CCI_REG8(0x0100)
+
+#define GC05A2_FLIP_REG CCI_REG8(0x0101)
+#define GC05A2_FLIP_H_MASK BIT(0)
+#define GC05A2_FLIP_V_MASK BIT(1)
+
+#define GC05A2_EXP_REG CCI_REG16(0x0202)
+#define GC05A2_EXP_MARGIN 16
+#define GC05A2_EXP_MIN 4
+#define GC05A2_EXP_STEP 1
+
+#define GC05A2_AGAIN_REG CCI_REG16(0x0204)
+#define GC05A2_AGAIN_MIN 1024
+#define GC05A2_AGAIN_MAX (1024 * 16)
+#define GC05A2_AGAIN_STEP 1
+
+#define GC05A2_FRAME_LENGTH_REG CCI_REG16(0x0340)
+#define GC05A2_VTS_MAX 0xffff
+
+#define GC05A2_REG_CHIP_ID CCI_REG16(0x03f0)
+#define GC05A2_CHIP_ID 0x05a2
+
+#define GC05A2_NATIVE_WIDTH 2592
+#define GC05A2_NATIVE_HEIGHT 1944
+
+#define GC05A2_DEFAULT_CLK_FREQ (24 * HZ_PER_MHZ)
+#define GC05A2_MBUS_CODE MEDIA_BUS_FMT_SGRBG10_1X10
+#define GC05A2_DATA_LANES 2
+#define GC05A2_RGB_DEPTH 10
+#define GC05A2_SLEEP_US (2 * USEC_PER_MSEC)
+
+static const char *const gc05a2_test_pattern_menu[] = {
+ "No Pattern", "Fade_to_gray_Color Bar", "Color Bar",
+ "PN9", "Horizental_gradient", "Checkboard Pattern",
+ "Slant", "Resolution", "Solid Black",
+ "Solid White",
+};
+
+static const s64 gc05a2_link_freq_menu_items[] = {
+ (448 * HZ_PER_MHZ),
+ (224 * HZ_PER_MHZ),
+};
+
+static const char *const gc05a2_supply_name[] = {
+ "avdd",
+ "dvdd",
+ "dovdd",
+};
+
+struct gc05a2 {
+ struct device *dev;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+
+ struct clk *xclk;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(gc05a2_supply_name)];
+ struct gpio_desc *reset_gpio;
+
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+
+ struct regmap *regmap;
+ unsigned long link_freq_bitmap;
+
+ /* True if the device has been identified */
+ bool identified;
+ const struct gc05a2_mode *cur_mode;
+};
+
+struct gc05a2_reg_list {
+ u32 num_of_regs;
+ const struct cci_reg_sequence *regs;
+};
+
+static const struct cci_reg_sequence mode_2592x1944[] = {
+ /* system */
+ { CCI_REG8(0x0135), 0x01 },
+
+ /* pre_setting */
+ { CCI_REG8(0x0084), 0x21 },
+ { CCI_REG8(0x0d05), 0xcc },
+ { CCI_REG8(0x0218), 0x00 },
+ { CCI_REG8(0x005e), 0x48 },
+ { CCI_REG8(0x0d06), 0x01 },
+ { CCI_REG8(0x0007), 0x16 },
+ { CCI_REG8(0x0101), 0x00 },
+
+ /* analog */
+ { CCI_REG8(0x0342), 0x07 },
+ { CCI_REG8(0x0343), 0x28 },
+ { CCI_REG8(0x0220), 0x07 },
+ { CCI_REG8(0x0221), 0xd0 },
+ { CCI_REG8(0x0202), 0x07 },
+ { CCI_REG8(0x0203), 0x32 },
+ { CCI_REG8(0x0340), 0x07 },
+ { CCI_REG8(0x0341), 0xf0 },
+ { CCI_REG8(0x0219), 0x00 },
+ { CCI_REG8(0x0346), 0x00 },
+ { CCI_REG8(0x0347), 0x04 },
+ { CCI_REG8(0x0d14), 0x00 },
+ { CCI_REG8(0x0d13), 0x05 },
+ { CCI_REG8(0x0d16), 0x05 },
+ { CCI_REG8(0x0d15), 0x1d },
+ { CCI_REG8(0x00c0), 0x0a },
+ { CCI_REG8(0x00c1), 0x30 },
+ { CCI_REG8(0x034a), 0x07 },
+ { CCI_REG8(0x034b), 0xa8 },
+ { CCI_REG8(0x0e0a), 0x00 },
+ { CCI_REG8(0x0e0b), 0x00 },
+ { CCI_REG8(0x0e0e), 0x03 },
+ { CCI_REG8(0x0e0f), 0x00 },
+ { CCI_REG8(0x0e06), 0x0a },
+ { CCI_REG8(0x0e23), 0x15 },
+ { CCI_REG8(0x0e24), 0x15 },
+ { CCI_REG8(0x0e2a), 0x10 },
+ { CCI_REG8(0x0e2b), 0x10 },
+ { CCI_REG8(0x0e17), 0x49 },
+ { CCI_REG8(0x0e1b), 0x1c },
+ { CCI_REG8(0x0e3a), 0x36 },
+ { CCI_REG8(0x0d11), 0x84 },
+ { CCI_REG8(0x0e52), 0x14 },
+ { CCI_REG8(0x000b), 0x10 },
+ { CCI_REG8(0x0008), 0x08 },
+ { CCI_REG8(0x0223), 0x17 },
+ { CCI_REG8(0x0d27), 0x39 },
+ { CCI_REG8(0x0d22), 0x00 },
+ { CCI_REG8(0x03f6), 0x0d },
+ { CCI_REG8(0x0d04), 0x07 },
+ { CCI_REG8(0x03f3), 0x72 },
+ { CCI_REG8(0x03f4), 0xb8 },
+ { CCI_REG8(0x03f5), 0xbc },
+ { CCI_REG8(0x0d02), 0x73 },
+
+ /* auto load start */
+ { CCI_REG8(0x00cb), 0x00 },
+
+ /* OUT 2592*1944 */
+ { CCI_REG8(0x0350), 0x01 },
+ { CCI_REG8(0x0353), 0x00 },
+ { CCI_REG8(0x0354), 0x08 },
+ { CCI_REG8(0x034c), 0x0a },
+ { CCI_REG8(0x034d), 0x20 },
+ { CCI_REG8(0x021f), 0x14 },
+
+ /* MIPI */
+ { CCI_REG8(0x0107), 0x05 },
+ { CCI_REG8(0x0117), 0x01 },
+ { CCI_REG8(0x0d81), 0x00 },
+ { CCI_REG8(0x0d84), 0x0c },
+ { CCI_REG8(0x0d85), 0xa8 },
+ { CCI_REG8(0x0d86), 0x06 },
+ { CCI_REG8(0x0d87), 0x55 },
+ { CCI_REG8(0x0db3), 0x06 },
+ { CCI_REG8(0x0db4), 0x08 },
+ { CCI_REG8(0x0db5), 0x1e },
+ { CCI_REG8(0x0db6), 0x02 },
+ { CCI_REG8(0x0db8), 0x12 },
+ { CCI_REG8(0x0db9), 0x0a },
+ { CCI_REG8(0x0d93), 0x06 },
+ { CCI_REG8(0x0d94), 0x09 },
+ { CCI_REG8(0x0d95), 0x0d },
+ { CCI_REG8(0x0d99), 0x0b },
+ { CCI_REG8(0x0084), 0x01 },
+
+ /* OUT */
+ { CCI_REG8(0x0110), 0x01 },
+};
+
+static const struct cci_reg_sequence mode_1280x720[] = {
+ /* system */
+ { CCI_REG8(0x0135), 0x05 },
+
+ /*pre_setting*/
+ { CCI_REG8(0x0084), 0x21 },
+ { CCI_REG8(0x0d05), 0xcc },
+ { CCI_REG8(0x0218), 0x80 },
+ { CCI_REG8(0x005e), 0x49 },
+ { CCI_REG8(0x0d06), 0x81 },
+ { CCI_REG8(0x0007), 0x16 },
+ { CCI_REG8(0x0101), 0x00 },
+
+ /* analog */
+ { CCI_REG8(0x0342), 0x07 },
+ { CCI_REG8(0x0343), 0x10 },
+ { CCI_REG8(0x0220), 0x07 },
+ { CCI_REG8(0x0221), 0xd0 },
+ { CCI_REG8(0x0202), 0x03 },
+ { CCI_REG8(0x0203), 0x32 },
+ { CCI_REG8(0x0340), 0x04 },
+ { CCI_REG8(0x0341), 0x08 },
+ { CCI_REG8(0x0219), 0x00 },
+ { CCI_REG8(0x0346), 0x01 },
+ { CCI_REG8(0x0347), 0x00 },
+ { CCI_REG8(0x0d14), 0x00 },
+ { CCI_REG8(0x0d13), 0x05 },
+ { CCI_REG8(0x0d16), 0x05 },
+ { CCI_REG8(0x0d15), 0x1d },
+ { CCI_REG8(0x00c0), 0x0a },
+ { CCI_REG8(0x00c1), 0x30 },
+ { CCI_REG8(0x034a), 0x05 },
+ { CCI_REG8(0x034b), 0xb0 },
+ { CCI_REG8(0x0e0a), 0x00 },
+ { CCI_REG8(0x0e0b), 0x00 },
+ { CCI_REG8(0x0e0e), 0x03 },
+ { CCI_REG8(0x0e0f), 0x00 },
+ { CCI_REG8(0x0e06), 0x0a },
+ { CCI_REG8(0x0e23), 0x15 },
+ { CCI_REG8(0x0e24), 0x15 },
+ { CCI_REG8(0x0e2a), 0x10 },
+ { CCI_REG8(0x0e2b), 0x10 },
+ { CCI_REG8(0x0e17), 0x49 },
+ { CCI_REG8(0x0e1b), 0x1c },
+ { CCI_REG8(0x0e3a), 0x36 },
+ { CCI_REG8(0x0d11), 0x84 },
+ { CCI_REG8(0x0e52), 0x14 },
+ { CCI_REG8(0x000b), 0x0e },
+ { CCI_REG8(0x0008), 0x03 },
+ { CCI_REG8(0x0223), 0x16 },
+ { CCI_REG8(0x0d27), 0x39 },
+ { CCI_REG8(0x0d22), 0x00 },
+ { CCI_REG8(0x03f6), 0x0d },
+ { CCI_REG8(0x0d04), 0x07 },
+ { CCI_REG8(0x03f3), 0x72 },
+ { CCI_REG8(0x03f4), 0xb8 },
+ { CCI_REG8(0x03f5), 0xbc },
+ { CCI_REG8(0x0d02), 0x73 },
+
+ /* auto load start */
+ { CCI_REG8(0x00cb), 0xfc },
+
+ /* OUT 1280x720 */
+ { CCI_REG8(0x0350), 0x01 },
+ { CCI_REG8(0x0353), 0x00 },
+ { CCI_REG8(0x0354), 0x0c },
+ { CCI_REG8(0x034c), 0x05 },
+ { CCI_REG8(0x034d), 0x00 },
+ { CCI_REG8(0x021f), 0x14 },
+
+ /* MIPI */
+ { CCI_REG8(0x0107), 0x05 },
+ { CCI_REG8(0x0117), 0x01 },
+ { CCI_REG8(0x0d81), 0x00 },
+ { CCI_REG8(0x0d84), 0x06 },
+ { CCI_REG8(0x0d85), 0x40 },
+ { CCI_REG8(0x0d86), 0x03 },
+ { CCI_REG8(0x0d87), 0x21 },
+ { CCI_REG8(0x0db3), 0x03 },
+ { CCI_REG8(0x0db4), 0x04 },
+ { CCI_REG8(0x0db5), 0x0d },
+ { CCI_REG8(0x0db6), 0x01 },
+ { CCI_REG8(0x0db8), 0x04 },
+ { CCI_REG8(0x0db9), 0x06 },
+ { CCI_REG8(0x0d93), 0x03 },
+ { CCI_REG8(0x0d94), 0x04 },
+ { CCI_REG8(0x0d95), 0x05 },
+ { CCI_REG8(0x0d99), 0x06 },
+ { CCI_REG8(0x0084), 0x01 },
+
+ /* OUT */
+ { CCI_REG8(0x0110), 0x01 },
+};
+
+static const struct cci_reg_sequence mode_table_common[] = {
+ { GC05A2_STREAMING_REG, 0x00 },
+ /* system */
+ { CCI_REG8(0x0315), 0xd4 },
+ { CCI_REG8(0x0d06), 0x01 },
+ { CCI_REG8(0x0a70), 0x80 },
+ { CCI_REG8(0x031a), 0x00 },
+ { CCI_REG8(0x0314), 0x00 },
+ { CCI_REG8(0x0130), 0x08 },
+ { CCI_REG8(0x0132), 0x01 },
+ { CCI_REG8(0x0136), 0x38 },
+ { CCI_REG8(0x0137), 0x03 },
+ { CCI_REG8(0x0134), 0x5b },
+ { CCI_REG8(0x031c), 0xe0 },
+ { CCI_REG8(0x0d82), 0x14 },
+ { CCI_REG8(0x0dd1), 0x56 },
+
+ /* gate_mode */
+ { CCI_REG8(0x0af4), 0x01 },
+ { CCI_REG8(0x0002), 0x10 },
+ { CCI_REG8(0x00c3), 0x34 },
+
+ /* auto load start */
+ { CCI_REG8(0x00c4), 0x00 },
+ { CCI_REG8(0x00c5), 0x01 },
+ { CCI_REG8(0x0af6), 0x00 },
+ { CCI_REG8(0x0ba0), 0x17 },
+ { CCI_REG8(0x0ba1), 0x00 },
+ { CCI_REG8(0x0ba2), 0x00 },
+ { CCI_REG8(0x0ba3), 0x00 },
+ { CCI_REG8(0x0ba4), 0x03 },
+ { CCI_REG8(0x0ba5), 0x00 },
+ { CCI_REG8(0x0ba6), 0x00 },
+ { CCI_REG8(0x0ba7), 0x00 },
+ { CCI_REG8(0x0ba8), 0x40 },
+ { CCI_REG8(0x0ba9), 0x00 },
+ { CCI_REG8(0x0baa), 0x00 },
+ { CCI_REG8(0x0bab), 0x00 },
+ { CCI_REG8(0x0bac), 0x40 },
+ { CCI_REG8(0x0bad), 0x00 },
+ { CCI_REG8(0x0bae), 0x00 },
+ { CCI_REG8(0x0baf), 0x00 },
+ { CCI_REG8(0x0bb0), 0x02 },
+ { CCI_REG8(0x0bb1), 0x00 },
+ { CCI_REG8(0x0bb2), 0x00 },
+ { CCI_REG8(0x0bb3), 0x00 },
+ { CCI_REG8(0x0bb8), 0x02 },
+ { CCI_REG8(0x0bb9), 0x00 },
+ { CCI_REG8(0x0bba), 0x00 },
+ { CCI_REG8(0x0bbb), 0x00 },
+ { CCI_REG8(0x0a70), 0x80 },
+ { CCI_REG8(0x0a71), 0x00 },
+ { CCI_REG8(0x0a72), 0x00 },
+ { CCI_REG8(0x0a66), 0x00 },
+ { CCI_REG8(0x0a67), 0x80 },
+ { CCI_REG8(0x0a4d), 0x4e },
+ { CCI_REG8(0x0a50), 0x00 },
+ { CCI_REG8(0x0a4f), 0x0c },
+ { CCI_REG8(0x0a66), 0x00 },
+ { CCI_REG8(0x00ca), 0x00 },
+ { CCI_REG8(0x00cc), 0x00 },
+ { CCI_REG8(0x00cd), 0x00 },
+ { CCI_REG8(0x0aa1), 0x00 },
+ { CCI_REG8(0x0aa2), 0xe0 },
+ { CCI_REG8(0x0aa3), 0x00 },
+ { CCI_REG8(0x0aa4), 0x40 },
+ { CCI_REG8(0x0a90), 0x03 },
+ { CCI_REG8(0x0a91), 0x0e },
+ { CCI_REG8(0x0a94), 0x80 },
+
+ /* standby */
+ { CCI_REG8(0x0af6), 0x20 },
+ { CCI_REG8(0x0b00), 0x91 },
+ { CCI_REG8(0x0b01), 0x17 },
+ { CCI_REG8(0x0b02), 0x01 },
+ { CCI_REG8(0x0b03), 0x00 },
+ { CCI_REG8(0x0b04), 0x01 },
+ { CCI_REG8(0x0b05), 0x17 },
+ { CCI_REG8(0x0b06), 0x01 },
+ { CCI_REG8(0x0b07), 0x00 },
+ { CCI_REG8(0x0ae9), 0x01 },
+ { CCI_REG8(0x0aea), 0x02 },
+ { CCI_REG8(0x0ae8), 0x53 },
+ { CCI_REG8(0x0ae8), 0x43 },
+
+ /* gain_partition */
+ { CCI_REG8(0x0af6), 0x30 },
+ { CCI_REG8(0x0b00), 0x08 },
+ { CCI_REG8(0x0b01), 0x0f },
+ { CCI_REG8(0x0b02), 0x00 },
+ { CCI_REG8(0x0b04), 0x1c },
+ { CCI_REG8(0x0b05), 0x24 },
+ { CCI_REG8(0x0b06), 0x00 },
+ { CCI_REG8(0x0b08), 0x30 },
+ { CCI_REG8(0x0b09), 0x40 },
+ { CCI_REG8(0x0b0a), 0x00 },
+ { CCI_REG8(0x0b0c), 0x0e },
+ { CCI_REG8(0x0b0d), 0x2a },
+ { CCI_REG8(0x0b0e), 0x00 },
+ { CCI_REG8(0x0b10), 0x0e },
+ { CCI_REG8(0x0b11), 0x2b },
+ { CCI_REG8(0x0b12), 0x00 },
+ { CCI_REG8(0x0b14), 0x0e },
+ { CCI_REG8(0x0b15), 0x23 },
+ { CCI_REG8(0x0b16), 0x00 },
+ { CCI_REG8(0x0b18), 0x0e },
+ { CCI_REG8(0x0b19), 0x24 },
+ { CCI_REG8(0x0b1a), 0x00 },
+ { CCI_REG8(0x0b1c), 0x0c },
+ { CCI_REG8(0x0b1d), 0x0c },
+ { CCI_REG8(0x0b1e), 0x00 },
+ { CCI_REG8(0x0b20), 0x03 },
+ { CCI_REG8(0x0b21), 0x03 },
+ { CCI_REG8(0x0b22), 0x00 },
+ { CCI_REG8(0x0b24), 0x0e },
+ { CCI_REG8(0x0b25), 0x0e },
+ { CCI_REG8(0x0b26), 0x00 },
+ { CCI_REG8(0x0b28), 0x03 },
+ { CCI_REG8(0x0b29), 0x03 },
+ { CCI_REG8(0x0b2a), 0x00 },
+ { CCI_REG8(0x0b2c), 0x12 },
+ { CCI_REG8(0x0b2d), 0x12 },
+ { CCI_REG8(0x0b2e), 0x00 },
+ { CCI_REG8(0x0b30), 0x08 },
+ { CCI_REG8(0x0b31), 0x08 },
+ { CCI_REG8(0x0b32), 0x00 },
+ { CCI_REG8(0x0b34), 0x14 },
+ { CCI_REG8(0x0b35), 0x14 },
+ { CCI_REG8(0x0b36), 0x00 },
+ { CCI_REG8(0x0b38), 0x10 },
+ { CCI_REG8(0x0b39), 0x10 },
+ { CCI_REG8(0x0b3a), 0x00 },
+ { CCI_REG8(0x0b3c), 0x16 },
+ { CCI_REG8(0x0b3d), 0x16 },
+ { CCI_REG8(0x0b3e), 0x00 },
+ { CCI_REG8(0x0b40), 0x10 },
+ { CCI_REG8(0x0b41), 0x10 },
+ { CCI_REG8(0x0b42), 0x00 },
+ { CCI_REG8(0x0b44), 0x19 },
+ { CCI_REG8(0x0b45), 0x19 },
+ { CCI_REG8(0x0b46), 0x00 },
+ { CCI_REG8(0x0b48), 0x16 },
+ { CCI_REG8(0x0b49), 0x16 },
+ { CCI_REG8(0x0b4a), 0x00 },
+ { CCI_REG8(0x0b4c), 0x19 },
+ { CCI_REG8(0x0b4d), 0x19 },
+ { CCI_REG8(0x0b4e), 0x00 },
+ { CCI_REG8(0x0b50), 0x16 },
+ { CCI_REG8(0x0b51), 0x16 },
+ { CCI_REG8(0x0b52), 0x00 },
+ { CCI_REG8(0x0b80), 0x01 },
+ { CCI_REG8(0x0b81), 0x00 },
+ { CCI_REG8(0x0b82), 0x00 },
+ { CCI_REG8(0x0b84), 0x00 },
+ { CCI_REG8(0x0b85), 0x00 },
+ { CCI_REG8(0x0b86), 0x00 },
+ { CCI_REG8(0x0b88), 0x01 },
+ { CCI_REG8(0x0b89), 0x6a },
+ { CCI_REG8(0x0b8a), 0x00 },
+ { CCI_REG8(0x0b8c), 0x00 },
+ { CCI_REG8(0x0b8d), 0x01 },
+ { CCI_REG8(0x0b8e), 0x00 },
+ { CCI_REG8(0x0b90), 0x01 },
+ { CCI_REG8(0x0b91), 0xf6 },
+ { CCI_REG8(0x0b92), 0x00 },
+ { CCI_REG8(0x0b94), 0x00 },
+ { CCI_REG8(0x0b95), 0x02 },
+ { CCI_REG8(0x0b96), 0x00 },
+ { CCI_REG8(0x0b98), 0x02 },
+ { CCI_REG8(0x0b99), 0xc4 },
+ { CCI_REG8(0x0b9a), 0x00 },
+ { CCI_REG8(0x0b9c), 0x00 },
+ { CCI_REG8(0x0b9d), 0x03 },
+ { CCI_REG8(0x0b9e), 0x00 },
+ { CCI_REG8(0x0ba0), 0x03 },
+ { CCI_REG8(0x0ba1), 0xd8 },
+ { CCI_REG8(0x0ba2), 0x00 },
+ { CCI_REG8(0x0ba4), 0x00 },
+ { CCI_REG8(0x0ba5), 0x04 },
+ { CCI_REG8(0x0ba6), 0x00 },
+ { CCI_REG8(0x0ba8), 0x05 },
+ { CCI_REG8(0x0ba9), 0x4d },
+ { CCI_REG8(0x0baa), 0x00 },
+ { CCI_REG8(0x0bac), 0x00 },
+ { CCI_REG8(0x0bad), 0x05 },
+ { CCI_REG8(0x0bae), 0x00 },
+ { CCI_REG8(0x0bb0), 0x07 },
+ { CCI_REG8(0x0bb1), 0x3e },
+ { CCI_REG8(0x0bb2), 0x00 },
+ { CCI_REG8(0x0bb4), 0x00 },
+ { CCI_REG8(0x0bb5), 0x06 },
+ { CCI_REG8(0x0bb6), 0x00 },
+ { CCI_REG8(0x0bb8), 0x0a },
+ { CCI_REG8(0x0bb9), 0x1a },
+ { CCI_REG8(0x0bba), 0x00 },
+ { CCI_REG8(0x0bbc), 0x09 },
+ { CCI_REG8(0x0bbd), 0x36 },
+ { CCI_REG8(0x0bbe), 0x00 },
+ { CCI_REG8(0x0bc0), 0x0e },
+ { CCI_REG8(0x0bc1), 0x66 },
+ { CCI_REG8(0x0bc2), 0x00 },
+ { CCI_REG8(0x0bc4), 0x10 },
+ { CCI_REG8(0x0bc5), 0x06 },
+ { CCI_REG8(0x0bc6), 0x00 },
+ { CCI_REG8(0x02c1), 0xe0 },
+ { CCI_REG8(0x0207), 0x04 },
+ { CCI_REG8(0x02c2), 0x10 },
+ { CCI_REG8(0x02c3), 0x74 },
+ { CCI_REG8(0x02c5), 0x09 },
+ { CCI_REG8(0x02c1), 0xe0 },
+ { CCI_REG8(0x0207), 0x04 },
+ { CCI_REG8(0x02c2), 0x10 },
+ { CCI_REG8(0x02c5), 0x09 },
+ { CCI_REG8(0x02c1), 0xe0 },
+ { CCI_REG8(0x0207), 0x04 },
+ { CCI_REG8(0x02c2), 0x10 },
+ { CCI_REG8(0x02c5), 0x09 },
+
+ /* auto load CH_GAIN */
+ { CCI_REG8(0x0aa1), 0x15 },
+ { CCI_REG8(0x0aa2), 0x50 },
+ { CCI_REG8(0x0aa3), 0x00 },
+ { CCI_REG8(0x0aa4), 0x09 },
+ { CCI_REG8(0x0a90), 0x25 },
+ { CCI_REG8(0x0a91), 0x0e },
+ { CCI_REG8(0x0a94), 0x80 },
+
+ /* ISP */
+ { CCI_REG8(0x0050), 0x00 },
+ { CCI_REG8(0x0089), 0x83 },
+ { CCI_REG8(0x005a), 0x40 },
+ { CCI_REG8(0x00c3), 0x35 },
+ { CCI_REG8(0x00c4), 0x80 },
+ { CCI_REG8(0x0080), 0x10 },
+ { CCI_REG8(0x0040), 0x12 },
+ { CCI_REG8(0x0053), 0x0a },
+ { CCI_REG8(0x0054), 0x44 },
+ { CCI_REG8(0x0055), 0x32 },
+ { CCI_REG8(0x0058), 0x89 },
+ { CCI_REG8(0x004a), 0x03 },
+ { CCI_REG8(0x0048), 0xf0 },
+ { CCI_REG8(0x0049), 0x0f },
+ { CCI_REG8(0x0041), 0x20 },
+ { CCI_REG8(0x0043), 0x0a },
+ { CCI_REG8(0x009d), 0x08 },
+ { CCI_REG8(0x0236), 0x40 },
+
+ /* gain */
+ { CCI_REG8(0x0204), 0x04 },
+ { CCI_REG8(0x0205), 0x00 },
+ { CCI_REG8(0x02b3), 0x00 },
+ { CCI_REG8(0x02b4), 0x00 },
+ { CCI_REG8(0x009e), 0x01 },
+ { CCI_REG8(0x009f), 0x94 },
+
+ /* auto load REG */
+ { CCI_REG8(0x0aa1), 0x10 },
+ { CCI_REG8(0x0aa2), 0xf8 },
+ { CCI_REG8(0x0aa3), 0x00 },
+ { CCI_REG8(0x0aa4), 0x1f },
+ { CCI_REG8(0x0a90), 0x11 },
+ { CCI_REG8(0x0a91), 0x0e },
+ { CCI_REG8(0x0a94), 0x80 },
+ { CCI_REG8(0x03fe), 0x00 },
+ { CCI_REG8(0x0a90), 0x00 },
+ { CCI_REG8(0x0a70), 0x00 },
+ { CCI_REG8(0x0a67), 0x00 },
+ { CCI_REG8(0x0af4), 0x29 },
+
+ /* DPHY */
+ { CCI_REG8(0x0d80), 0x07 },
+ { CCI_REG8(0x0dd3), 0x18 },
+
+ /* CISCTL_Reset */
+ { CCI_REG8(0x031c), 0x80 },
+ { CCI_REG8(0x03fe), 0x30 },
+ { CCI_REG8(0x0d17), 0x06 },
+ { CCI_REG8(0x03fe), 0x00 },
+ { CCI_REG8(0x0d17), 0x00 },
+ { CCI_REG8(0x031c), 0x93 },
+ { CCI_REG8(0x03fe), 0x00 },
+ { CCI_REG8(0x031c), 0x80 },
+ { CCI_REG8(0x03fe), 0x30 },
+ { CCI_REG8(0x0d17), 0x06 },
+ { CCI_REG8(0x03fe), 0x00 },
+ { CCI_REG8(0x0d17), 0x00 },
+ { CCI_REG8(0x031c), 0x93 },
+};
+
+struct gc05a2_mode {
+ u32 width;
+ u32 height;
+ const struct gc05a2_reg_list reg_list;
+
+ u32 hts; /* Horizontal timining size */
+ u32 vts_def; /* Default vertical timining size */
+ u32 vts_min; /* Min vertical timining size */
+};
+
+/* Declare modes in order, from biggest to smallest height. */
+static const struct gc05a2_mode gc05a2_modes[] = {
+ {
+ /* 2592*1944@30fps */
+ .width = GC05A2_NATIVE_WIDTH,
+ .height = GC05A2_NATIVE_HEIGHT,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_2592x1944),
+ .regs = mode_2592x1944,
+ },
+ .hts = 3664,
+ .vts_def = 2032,
+ .vts_min = 2032,
+ },
+ {
+ /* 1280*720@60fps */
+ .width = 1280,
+ .height = 720,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_1280x720),
+ .regs = mode_1280x720,
+ },
+ .hts = 3616,
+ .vts_def = 1032,
+ .vts_min = 1032,
+ },
+};
+
+static inline struct gc05a2 *to_gc05a2(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct gc05a2, sd);
+}
+
+static int gc05a2_power_on(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct gc05a2 *gc05a2 = to_gc05a2(sd);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(gc05a2_supply_name),
+ gc05a2->supplies);
+ if (ret < 0) {
+ dev_err(gc05a2->dev, "failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(gc05a2->xclk);
+ if (ret < 0) {
+ regulator_bulk_disable(ARRAY_SIZE(gc05a2_supply_name),
+ gc05a2->supplies);
+ dev_err(gc05a2->dev, "clk prepare enable failed\n");
+ return ret;
+ }
+
+ fsleep(GC05A2_SLEEP_US);
+
+ gpiod_set_value_cansleep(gc05a2->reset_gpio, 0);
+ fsleep(GC05A2_SLEEP_US);
+
+ return 0;
+}
+
+static int gc05a2_power_off(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct gc05a2 *gc05a2 = to_gc05a2(sd);
+
+ clk_disable_unprepare(gc05a2->xclk);
+ gpiod_set_value_cansleep(gc05a2->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(gc05a2_supply_name),
+ gc05a2->supplies);
+
+ return 0;
+}
+
+static int gc05a2_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = GC05A2_MBUS_CODE;
+
+ return 0;
+}
+
+static int gc05a2_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->code != GC05A2_MBUS_CODE)
+ return -EINVAL;
+
+ if (fse->index >= ARRAY_SIZE(gc05a2_modes))
+ return -EINVAL;
+
+ fse->min_width = gc05a2_modes[fse->index].width;
+ fse->max_width = gc05a2_modes[fse->index].width;
+ fse->min_height = gc05a2_modes[fse->index].height;
+ fse->max_height = gc05a2_modes[fse->index].height;
+
+ return 0;
+}
+
+static int gc05a2_update_cur_mode_controls(struct gc05a2 *gc05a2,
+ const struct gc05a2_mode *mode)
+{
+ s64 exposure_max, h_blank;
+ int ret;
+
+ ret = __v4l2_ctrl_modify_range(gc05a2->vblank,
+ mode->vts_min - mode->height,
+ GC05A2_VTS_MAX - mode->height, 1,
+ mode->vts_def - mode->height);
+ if (ret) {
+ dev_err(gc05a2->dev, "VB ctrl range update failed\n");
+ return ret;
+ }
+
+ h_blank = mode->hts - mode->width;
+ ret = __v4l2_ctrl_modify_range(gc05a2->hblank, h_blank, h_blank, 1,
+ h_blank);
+ if (ret) {
+ dev_err(gc05a2->dev, "HB ctrl range update failed\n");
+ return ret;
+ }
+
+ exposure_max = mode->vts_def - GC05A2_EXP_MARGIN;
+ ret = __v4l2_ctrl_modify_range(gc05a2->exposure, GC05A2_EXP_MIN,
+ exposure_max, GC05A2_EXP_STEP,
+ exposure_max);
+ if (ret) {
+ dev_err(gc05a2->dev, "exposure ctrl range update failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void gc05a2_update_pad_format(struct gc05a2 *gc08a3,
+ const struct gc05a2_mode *mode,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ fmt->width = mode->width;
+ fmt->height = mode->height;
+ fmt->code = GC05A2_MBUS_CODE;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = V4L2_COLORSPACE_RAW;
+ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
+ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+}
+
+static int gc05a2_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct gc05a2 *gc05a2 = to_gc05a2(sd);
+ struct v4l2_mbus_framefmt *mbus_fmt;
+ struct v4l2_rect *crop;
+ const struct gc05a2_mode *mode;
+
+ mode = v4l2_find_nearest_size(gc05a2_modes, ARRAY_SIZE(gc05a2_modes),
+ width, height, fmt->format.width,
+ fmt->format.height);
+
+ /* update crop info to subdev state */
+ crop = v4l2_subdev_state_get_crop(state, 0);
+ crop->width = mode->width;
+ crop->height = mode->height;
+
+ /* update fmt info to subdev state */
+ gc05a2_update_pad_format(gc05a2, mode, &fmt->format);
+ mbus_fmt = v4l2_subdev_state_get_format(state, 0);
+ *mbus_fmt = fmt->format;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+ gc05a2->cur_mode = mode;
+ gc05a2_update_cur_mode_controls(gc05a2, mode);
+
+ return 0;
+}
+
+static int gc05a2_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *v4l2_subdev_state_get_crop(state, 0);
+ break;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.top = 0;
+ sel->r.left = 0;
+ sel->r.width = GC05A2_NATIVE_WIDTH;
+ sel->r.height = GC05A2_NATIVE_HEIGHT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int gc05a2_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ .pad = 0,
+ .format = {
+ .code = GC05A2_MBUS_CODE,
+ .width = gc05a2_modes[0].width,
+ .height = gc05a2_modes[0].height,
+ },
+ };
+
+ gc05a2_set_format(sd, state, &fmt);
+
+ return 0;
+}
+
+static int gc05a2_set_ctrl_hflip(struct gc05a2 *gc05a2, u32 ctrl_val)
+{
+ int ret;
+ u64 val;
+
+ ret = cci_read(gc05a2->regmap, GC05A2_FLIP_REG, &val, NULL);
+ if (ret) {
+ dev_err(gc05a2->dev, "read hflip register failed: %d\n", ret);
+ return ret;
+ }
+
+ return cci_update_bits(gc05a2->regmap, GC05A2_FLIP_REG,
+ GC05A2_FLIP_H_MASK,
+ ctrl_val ? GC05A2_FLIP_H_MASK : 0, NULL);
+}
+
+static int gc05a2_set_ctrl_vflip(struct gc05a2 *gc05a2, u32 ctrl_val)
+{
+ int ret;
+ u64 val;
+
+ ret = cci_read(gc05a2->regmap, GC05A2_FLIP_REG, &val, NULL);
+ if (ret) {
+ dev_err(gc05a2->dev, "read vflip register failed: %d\n", ret);
+ return ret;
+ }
+
+ return cci_update_bits(gc05a2->regmap, GC05A2_FLIP_REG,
+ GC05A2_FLIP_V_MASK,
+ ctrl_val ? GC05A2_FLIP_V_MASK : 0, NULL);
+}
+
+static int gc05a2_test_pattern(struct gc05a2 *gc05a2, u32 pattern_menu)
+{
+ u32 pattern;
+ int ret;
+
+ if (pattern_menu) {
+ switch (pattern_menu) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ pattern = pattern_menu << 4;
+ break;
+
+ case 8:
+ pattern = 0;
+ break;
+
+ case 9:
+ pattern = 4;
+ break;
+
+ default:
+ pattern = 0x00;
+ break;
+ }
+
+ ret = cci_write(gc05a2->regmap, GC05A2_REG_TEST_PATTERN_IDX,
+ pattern, NULL);
+ if (ret)
+ return ret;
+
+ return cci_write(gc05a2->regmap, GC05A2_REG_TEST_PATTERN_EN,
+ GC05A2_TEST_PATTERN_EN, NULL);
+ } else {
+ return cci_write(gc05a2->regmap, GC05A2_REG_TEST_PATTERN_EN,
+ 0x00, NULL);
+ }
+}
+
+static int gc05a2_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gc05a2 *gc05a2 =
+ container_of(ctrl->handler, struct gc05a2, ctrls);
+ int ret = 0;
+ s64 exposure_max;
+ struct v4l2_subdev_state *state;
+ const struct v4l2_mbus_framefmt *format;
+
+ state = v4l2_subdev_get_locked_active_state(&gc05a2->sd);
+ format = v4l2_subdev_state_get_format(state, 0);
+
+ if (ctrl->id == V4L2_CID_VBLANK) {
+ /* Update max exposure while meeting expected vblanking */
+ exposure_max = format->height + ctrl->val - GC05A2_EXP_MARGIN;
+ __v4l2_ctrl_modify_range(gc05a2->exposure,
+ gc05a2->exposure->minimum,
+ exposure_max, gc05a2->exposure->step,
+ exposure_max);
+ }
+
+ /*
+ * Applying V4L2 control value only happens
+ * when power is on for streaming.
+ */
+ if (!pm_runtime_get_if_active(gc05a2->dev))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ ret = cci_write(gc05a2->regmap, GC05A2_EXP_REG,
+ ctrl->val, NULL);
+ break;
+
+ case V4L2_CID_ANALOGUE_GAIN:
+ ret = cci_write(gc05a2->regmap, GC05A2_AGAIN_REG,
+ ctrl->val, NULL);
+ break;
+
+ case V4L2_CID_VBLANK:
+ ret = cci_write(gc05a2->regmap, GC05A2_FRAME_LENGTH_REG,
+ gc05a2->cur_mode->height + ctrl->val, NULL);
+ break;
+
+ case V4L2_CID_HFLIP:
+ ret = gc05a2_set_ctrl_hflip(gc05a2, ctrl->val);
+ break;
+
+ case V4L2_CID_VFLIP:
+ ret = gc05a2_set_ctrl_vflip(gc05a2, ctrl->val);
+ break;
+
+ case V4L2_CID_TEST_PATTERN:
+ ret = gc05a2_test_pattern(gc05a2, ctrl->val);
+ break;
+
+ default:
+ break;
+ }
+
+ pm_runtime_put(gc05a2->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops gc05a2_ctrl_ops = {
+ .s_ctrl = gc05a2_set_ctrl,
+};
+
+static int gc05a2_identify_module(struct gc05a2 *gc05a2)
+{
+ u64 val;
+ int ret;
+
+ if (gc05a2->identified)
+ return 0;
+
+ ret = cci_read(gc05a2->regmap, GC05A2_REG_CHIP_ID, &val, NULL);
+ if (ret)
+ return ret;
+
+ if (val != GC05A2_CHIP_ID) {
+ dev_err(gc05a2->dev, "chip id mismatch: 0x%x!=0x%llx",
+ GC05A2_CHIP_ID, val);
+ return -ENXIO;
+ }
+
+ gc05a2->identified = true;
+
+ return 0;
+}
+
+static int gc05a2_start_streaming(struct gc05a2 *gc05a2)
+{
+ const struct gc05a2_mode *mode;
+ const struct gc05a2_reg_list *reg_list;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(gc05a2->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = gc05a2_identify_module(gc05a2);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = cci_multi_reg_write(gc05a2->regmap,
+ mode_table_common,
+ ARRAY_SIZE(mode_table_common), NULL);
+ if (ret)
+ goto err_rpm_put;
+
+ mode = gc05a2->cur_mode;
+ reg_list = &mode->reg_list;
+
+ ret = cci_multi_reg_write(gc05a2->regmap,
+ reg_list->regs, reg_list->num_of_regs, NULL);
+ if (ret < 0)
+ goto err_rpm_put;
+
+ ret = __v4l2_ctrl_handler_setup(&gc05a2->ctrls);
+ if (ret < 0) {
+ dev_err(gc05a2->dev, "could not sync v4l2 controls\n");
+ goto err_rpm_put;
+ }
+
+ ret = cci_write(gc05a2->regmap, GC05A2_STREAMING_REG, 1, NULL);
+ if (ret < 0) {
+ dev_err(gc05a2->dev, "write STREAMING_REG failed: %d\n", ret);
+ goto err_rpm_put;
+ }
+
+ return 0;
+
+err_rpm_put:
+ pm_runtime_put(gc05a2->dev);
+ return ret;
+}
+
+static int gc05a2_stop_streaming(struct gc05a2 *gc05a2)
+{
+ int ret;
+
+ ret = cci_write(gc05a2->regmap, GC05A2_STREAMING_REG, 0, NULL);
+ if (ret < 0)
+ dev_err(gc05a2->dev, "could not sent stop streaming %d\n", ret);
+
+ pm_runtime_put(gc05a2->dev);
+ return ret;
+}
+
+static int gc05a2_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct gc05a2 *gc05a2 = to_gc05a2(subdev);
+ struct v4l2_subdev_state *state;
+ int ret;
+
+ state = v4l2_subdev_lock_and_get_active_state(subdev);
+
+ if (enable)
+ ret = gc05a2_start_streaming(gc05a2);
+ else
+ ret = gc05a2_stop_streaming(gc05a2);
+
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_video_ops gc05a2_video_ops = {
+ .s_stream = gc05a2_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops gc05a2_subdev_pad_ops = {
+ .enum_mbus_code = gc05a2_enum_mbus_code,
+ .enum_frame_size = gc05a2_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = gc05a2_set_format,
+ .get_selection = gc05a2_get_selection,
+};
+
+static const struct v4l2_subdev_core_ops gc05a2_core_ops = {
+ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_ops gc05a2_subdev_ops = {
+ .core = &gc05a2_core_ops,
+ .video = &gc05a2_video_ops,
+ .pad = &gc05a2_subdev_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops gc05a2_internal_ops = {
+ .init_state = gc05a2_init_state,
+};
+
+static int gc05a2_get_regulators(struct device *dev, struct gc05a2 *gc05a2)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(gc05a2_supply_name); i++)
+ gc05a2->supplies[i].supply = gc05a2_supply_name[i];
+
+ return devm_regulator_bulk_get(dev, ARRAY_SIZE(gc05a2_supply_name),
+ gc05a2->supplies);
+}
+
+static int gc05a2_parse_fwnode(struct gc05a2 *gc05a2)
+{
+ struct fwnode_handle *endpoint;
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
+ int ret;
+ struct device *dev = gc05a2->dev;
+
+ endpoint =
+ fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!endpoint) {
+ dev_err(dev, "endpoint node not found\n");
+ return -EINVAL;
+ }
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
+ if (ret) {
+ dev_err(dev, "parsing endpoint node failed\n");
+ goto done;
+ }
+
+ ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies,
+ bus_cfg.nr_of_link_frequencies,
+ gc05a2_link_freq_menu_items,
+ ARRAY_SIZE(gc05a2_link_freq_menu_items),
+ &gc05a2->link_freq_bitmap);
+ if (ret)
+ goto done;
+
+done:
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+ fwnode_handle_put(endpoint);
+ return ret;
+}
+
+static u64 gc05a2_to_pixel_rate(u32 f_index)
+{
+ u64 pixel_rate =
+ gc05a2_link_freq_menu_items[f_index] * 2 * GC05A2_DATA_LANES;
+
+ return div_u64(pixel_rate, GC05A2_RGB_DEPTH);
+}
+
+static int gc05a2_init_controls(struct gc05a2 *gc05a2)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&gc05a2->sd);
+ const struct gc05a2_mode *mode = &gc05a2_modes[0];
+ const struct v4l2_ctrl_ops *ops = &gc05a2_ctrl_ops;
+ struct v4l2_fwnode_device_properties props;
+ struct v4l2_ctrl_handler *ctrl_hdlr;
+ s64 exposure_max, h_blank;
+ int ret;
+
+ ctrl_hdlr = &gc05a2->ctrls;
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9);
+ if (ret)
+ return ret;
+
+ gc05a2->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &gc05a2_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ gc05a2->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &gc05a2_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_cluster(2, &gc05a2->hflip);
+
+ gc05a2->link_freq =
+ v4l2_ctrl_new_int_menu(ctrl_hdlr,
+ &gc05a2_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(gc05a2_link_freq_menu_items) - 1,
+ 0,
+ gc05a2_link_freq_menu_items);
+ if (gc05a2->link_freq)
+ gc05a2->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ gc05a2->pixel_rate =
+ v4l2_ctrl_new_std(ctrl_hdlr,
+ &gc05a2_ctrl_ops,
+ V4L2_CID_PIXEL_RATE, 0,
+ gc05a2_to_pixel_rate(0),
+ 1,
+ gc05a2_to_pixel_rate(0));
+
+ gc05a2->vblank =
+ v4l2_ctrl_new_std(ctrl_hdlr,
+ &gc05a2_ctrl_ops, V4L2_CID_VBLANK,
+ mode->vts_min - mode->height,
+ GC05A2_VTS_MAX - mode->height, 1,
+ mode->vts_def - mode->height);
+
+ h_blank = mode->hts - mode->width;
+ gc05a2->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &gc05a2_ctrl_ops,
+ V4L2_CID_HBLANK, h_blank, h_blank, 1,
+ h_blank);
+ if (gc05a2->hblank)
+ gc05a2->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ v4l2_ctrl_new_std(ctrl_hdlr, &gc05a2_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN, GC05A2_AGAIN_MIN,
+ GC05A2_AGAIN_MAX, GC05A2_AGAIN_STEP,
+ GC05A2_AGAIN_MIN);
+
+ exposure_max = mode->vts_def - GC05A2_EXP_MARGIN;
+ gc05a2->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &gc05a2_ctrl_ops,
+ V4L2_CID_EXPOSURE, GC05A2_EXP_MIN,
+ exposure_max, GC05A2_EXP_STEP,
+ exposure_max);
+
+ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &gc05a2_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(gc05a2_test_pattern_menu) - 1,
+ 0, 0, gc05a2_test_pattern_menu);
+
+ /* register properties to fwnode (e.g. rotation, orientation) */
+ ret = v4l2_fwnode_device_parse(&client->dev, &props);
+ if (ret)
+ goto error_ctrls;
+
+ ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, ops, &props);
+ if (ret)
+ goto error_ctrls;
+
+ if (ctrl_hdlr->error) {
+ ret = ctrl_hdlr->error;
+ goto error_ctrls;
+ }
+
+ gc05a2->sd.ctrl_handler = ctrl_hdlr;
+
+ return 0;
+
+error_ctrls:
+ v4l2_ctrl_handler_free(ctrl_hdlr);
+
+ return ret;
+}
+
+static int gc05a2_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct gc05a2 *gc05a2;
+ int ret;
+
+ gc05a2 = devm_kzalloc(dev, sizeof(*gc05a2), GFP_KERNEL);
+ if (!gc05a2)
+ return -ENOMEM;
+
+ gc05a2->dev = dev;
+
+ ret = gc05a2_parse_fwnode(gc05a2);
+ if (ret)
+ return ret;
+
+ gc05a2->regmap = devm_cci_regmap_init_i2c(client, 16);
+ if (IS_ERR(gc05a2->regmap))
+ return dev_err_probe(dev, PTR_ERR(gc05a2->regmap),
+ "failed to init CCI\n");
+
+ gc05a2->xclk = devm_clk_get(dev, NULL);
+ if (IS_ERR(gc05a2->xclk))
+ return dev_err_probe(dev, PTR_ERR(gc05a2->xclk),
+ "failed to get xclk\n");
+
+ ret = clk_set_rate(gc05a2->xclk, GC05A2_DEFAULT_CLK_FREQ);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to set xclk frequency\n");
+
+ ret = gc05a2_get_regulators(dev, gc05a2);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "failed to get regulators\n");
+
+ gc05a2->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(gc05a2->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(gc05a2->reset_gpio),
+ "failed to get gpio\n");
+
+ v4l2_i2c_subdev_init(&gc05a2->sd, client, &gc05a2_subdev_ops);
+ gc05a2->sd.internal_ops = &gc05a2_internal_ops;
+ gc05a2->cur_mode = &gc05a2_modes[0];
+
+ ret = gc05a2_init_controls(gc05a2);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to init controls\n");
+
+ gc05a2->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_HAS_EVENTS;
+ gc05a2->pad.flags = MEDIA_PAD_FL_SOURCE;
+ gc05a2->sd.dev = &client->dev;
+ gc05a2->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ ret = media_entity_pads_init(&gc05a2->sd.entity, 1, &gc05a2->pad);
+ if (ret < 0) {
+ dev_err(dev, "could not register media entity\n");
+ goto err_v4l2_ctrl_handler_free;
+ }
+
+ gc05a2->sd.state_lock = gc05a2->ctrls.lock;
+ ret = v4l2_subdev_init_finalize(&gc05a2->sd);
+ if (ret < 0) {
+ dev_err(dev, "v4l2 subdev init error: %d\n", ret);
+ goto err_media_entity_cleanup;
+ }
+
+ pm_runtime_set_active(gc05a2->dev);
+ pm_runtime_enable(gc05a2->dev);
+ pm_runtime_set_autosuspend_delay(gc05a2->dev, 1000);
+ pm_runtime_use_autosuspend(gc05a2->dev);
+ pm_runtime_idle(gc05a2->dev);
+
+ ret = v4l2_async_register_subdev_sensor(&gc05a2->sd);
+ if (ret < 0) {
+ dev_err(dev, "could not register v4l2 device\n");
+ goto err_rpm;
+ }
+
+ return 0;
+
+err_rpm:
+ pm_runtime_disable(gc05a2->dev);
+ v4l2_subdev_cleanup(&gc05a2->sd);
+
+err_media_entity_cleanup:
+ media_entity_cleanup(&gc05a2->sd.entity);
+
+err_v4l2_ctrl_handler_free:
+ v4l2_ctrl_handler_free(&gc05a2->ctrls);
+
+ return ret;
+}
+
+static void gc05a2_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct gc05a2 *gc05a2 = to_gc05a2(sd);
+
+ v4l2_async_unregister_subdev(&gc05a2->sd);
+ v4l2_subdev_cleanup(sd);
+ media_entity_cleanup(&gc05a2->sd.entity);
+ v4l2_ctrl_handler_free(&gc05a2->ctrls);
+
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ gc05a2_power_off(gc05a2->dev);
+ pm_runtime_set_suspended(&client->dev);
+}
+
+static const struct of_device_id gc05a2_of_match[] = {
+ { .compatible = "galaxycore,gc05a2" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, gc05a2_of_match);
+
+static DEFINE_RUNTIME_DEV_PM_OPS(gc05a2_pm_ops,
+ gc05a2_power_off,
+ gc05a2_power_on,
+ NULL);
+
+static struct i2c_driver gc05a2_i2c_driver = {
+ .driver = {
+ .of_match_table = gc05a2_of_match,
+ .pm = pm_ptr(&gc05a2_pm_ops),
+ .name = "gc05a2",
+ },
+ .probe = gc05a2_probe,
+ .remove = gc05a2_remove,
+};
+module_i2c_driver(gc05a2_i2c_driver);
+
+MODULE_DESCRIPTION("GalaxyCore gc05a2 Camera driver");
+MODULE_AUTHOR("Zhi Mao <zhi.mao@mediatek.com>");
+MODULE_LICENSE("GPL");
--
2.25.1
^ permalink raw reply related
* [PATCH v3 1/2] media: dt-bindings: i2c: add GalaxyCore GC05A2 image sensor
From: Zhi Mao @ 2024-04-03 3:38 UTC (permalink / raw)
To: mchehab, robh+dt, krzysztof.kozlowski+dt, sakari.ailus
Cc: laurent.pinchart, shengnan.wang, yaya.chang,
Project_Global_Chrome_Upstream_Group, yunkec, conor+dt,
matthias.bgg, angelogioacchino.delregno, jacopo.mondi, zhi.mao,
10572168, hverkuil-cisco, heiko, jernej.skrabec, macromorgan,
linus.walleij, hdegoede, tomi.valkeinen, gerald.loacker,
andy.shevchenko, bingbu.cao, dan.scally, linux-media, devicetree,
linux-kernel, linux-arm-kernel, linux-mediatek,
Krzysztof Kozlowski
In-Reply-To: <20240403033825.9072-1-zhi.mao@mediatek.com>
Add YAML device tree binding for GC05A2 CMOS image sensor,
and the relevant MAINTAINERS entries.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Zhi Mao <zhi.mao@mediatek.com>
---
.../bindings/media/i2c/galaxycore,gc05a2.yaml | 112 ++++++++++++++++++
1 file changed, 112 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/i2c/galaxycore,gc05a2.yaml
diff --git a/Documentation/devicetree/bindings/media/i2c/galaxycore,gc05a2.yaml b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc05a2.yaml
new file mode 100644
index 000000000000..0e7a7b5ac89f
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/galaxycore,gc05a2.yaml
@@ -0,0 +1,112 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright (c) 2023 MediaTek Inc.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/galaxycore,gc05a2.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: GalaxyCore gc05a2 1/5" 5M Pixel MIPI CSI-2 sensor
+
+maintainers:
+ - Zhi Mao <zhi.mao@mediatek.com>
+
+description:
+ The gc05a2 is a raw image sensor with an MIPI CSI-2 image data
+ interface and CCI (I2C compatible) control bus. The output format
+ is raw Bayer.
+
+properties:
+ compatible:
+ const: galaxycore,gc05a2
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ dovdd-supply: true
+
+ avdd-supply: true
+
+ dvdd-supply: true
+
+ reset-gpios:
+ description: Reference to the GPIO connected to the RESETB pin.
+ maxItems: 1
+
+ port:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ additionalProperties: false
+ description:
+ Output port node, single endpoint describing the CSI-2 transmitter.
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ data-lanes:
+ oneOf:
+ - items:
+ - const: 1
+ - const: 2
+ - const: 3
+ - const: 4
+ - items:
+ - const: 1
+ - const: 2
+
+ link-frequencies: true
+
+ required:
+ - data-lanes
+ - link-frequencies
+
+ required:
+ - endpoint
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - dovdd-supply
+ - avdd-supply
+ - dvdd-supply
+ - reset-gpios
+ - port
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sensor@37 {
+ compatible = "galaxycore,gc05a2";
+ reg = <0x37>;
+
+ clocks = <&gc05a2_clk>;
+
+ reset-gpios = <&pio 21 GPIO_ACTIVE_LOW>;
+
+ avdd-supply = <&gc05a2_avdd>;
+ dovdd-supply = <&gc05a2_dovdd>;
+ dvdd-supply = <&gc05a2_dvdd>;
+
+ port {
+ sensor_out: endpoint {
+ data-lanes = <1 2>;
+ link-frequencies = /bits/ 64 <448000000 224000000>;
+ remote-endpoint = <&seninf_csi_port_1_in>;
+ };
+ };
+ };
+ };
+
+...
--
2.25.1
^ permalink raw reply related
* [PATCH v3 0/2] media: i2c: Add support for GC05A2 sensor
From: Zhi Mao @ 2024-04-03 3:38 UTC (permalink / raw)
To: mchehab, robh+dt, krzysztof.kozlowski+dt, sakari.ailus
Cc: laurent.pinchart, shengnan.wang, yaya.chang,
Project_Global_Chrome_Upstream_Group, yunkec, conor+dt,
matthias.bgg, angelogioacchino.delregno, jacopo.mondi, zhi.mao,
10572168, hverkuil-cisco, heiko, jernej.skrabec, macromorgan,
linus.walleij, hdegoede, tomi.valkeinen, gerald.loacker,
andy.shevchenko, bingbu.cao, dan.scally, linux-media, devicetree,
linux-kernel, linux-arm-kernel, linux-mediatek
This series adds YAML DT binding and V4L2 sub-device driver for Galaxycore's
GC05A2 5-megapixel 10-bit RAW CMOS 1/5" sensor, with an MIPI CSI-2 image data
interface and the I2C control bus.
The driver is implemented with V4L2 framework.
- Async registered as a V4L2 sub-device.
- As the first component of camera system including Seninf, ISP pipeline.
- A media entity that provides one source pad in common.
- Used in camera features on ChromeOS application.
Also this driver supports following features:
- manual exposure and analog gain control support
- vertical blanking control support
- test pattern support
- media controller support
- runtime PM support
- support resolution: 2592x1944@30fps, 1280x720@60fps
Previous versions of this patch-set can be found here:
v2:https://lore.kernel.org/linux-media/20240323014751.4989-1-zhi.mao@mediatek.com/
v1:https://lore.kernel.org/linux-media/20240316025253.2300-1-zhi.mao@mediatek.com/
v0:https://lore.kernel.org/linux-media/20240313054409.8073-1-zhi.mao@mediatek.com/
This series is based on linux-next, tag: next-20240402
Changes in v3:
- gc05a2 sensor driver:
-- remove gc05a2_power_on() in function:probe(),
and use pm_runtime_resume_and_get() to power on sensor in function:start_streaming()
-- move gc05a2_identify_module() frome function:probe() to function:start_streaming()
Thanks
Zhi Mao (2):
media: dt-bindings: i2c: add GalaxyCore GC05A2 image sensor
media: i2c: Add GC05A2 image sensor driver
.../bindings/media/i2c/galaxycore,gc05a2.yaml | 112 ++
drivers/media/i2c/Kconfig | 10 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/gc05a2.c | 1383 +++++++++++++++++
4 files changed, 1506 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/i2c/galaxycore,gc05a2.yaml
create mode 100644 drivers/media/i2c/gc05a2.c
--
2.25.1
^ permalink raw reply
* [PATCH] arm64: dts: qcom: qcs6490-rb3gen2: Specify zap region for gpu
From: Bjorn Andersson @ 2024-04-03 3:33 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Caleb Connolly, Komal Bajaj, Naina Mehta
Cc: linux-arm-msm, devicetree, linux-kernel, Bjorn Andersson
Without the zap region defined the enabled GPU will, if able to find the
other firmware files, attempt to initialize itself without the zap
firmware loading, which causes the rb3gen2 to freeze or crash.
Add the zap-shader node and define the memory-region and firmware path
to avoid this problem.
Fixes: 04cf333afc75 ("arm64: dts: qcom: Add base qcs6490-rb3gen2 board dts")
Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
---
arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts
index 63ebe0774f1d..5b81b5e0b4ce 100644
--- a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts
+++ b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts
@@ -471,6 +471,13 @@ &gcc {
<GCC_WPSS_RSCP_CLK>;
};
+&gpu {
+ zap-shader {
+ memory-region = <&gpu_microcode_mem>;
+ firmware-name = "qcom/qcs6490/a660_zap.mbn";
+ };
+};
+
&qupv3_id_0 {
status = "okay";
};
---
base-commit: 727900b675b749c40ba1f6669c7ae5eb7eb8e837
change-id: 20240326-rb3gen2-gpu-4343c5dc7e40
Best regards,
--
Bjorn Andersson <quic_bjorande@quicinc.com>
^ permalink raw reply related
* Re: [PATCH 3/3] ARM: dts: Modify I2C bus configuration
From: Andrew Jeffery @ 2024-04-03 3:30 UTC (permalink / raw)
To: Renze Nicolai, linux-arm-kernel, devicetree, linux-kernel,
linux-aspeed, arnd, olof, soc, robh+dt, krzysztof.kozlowski+dt,
joel, andrew
In-Reply-To: <20240329130152.878944-4-renze@rnplus.nl>
Hi Renze,
On Fri, 2024-03-29 at 14:01 +0100, Renze Nicolai wrote:
> This commit enables I2C bus 8 which is exposed on the IPMB_1 connector on the X570D4U mainboard.
> Additionally it adds a descriptive comment to I2C busses 1 and 5.
checkpatch on this one too :)
Cheers,
Andrew
^ permalink raw reply
* Re: [PATCH 2/3] ARM: dts: Disable unused ADC channels for Asrock X570D4U BMC
From: Andrew Jeffery @ 2024-04-03 3:29 UTC (permalink / raw)
To: Renze Nicolai, linux-arm-kernel, devicetree, linux-kernel,
linux-aspeed, arnd, olof, soc, robh+dt, krzysztof.kozlowski+dt,
joel, andrew
In-Reply-To: <20240329130152.878944-3-renze@rnplus.nl>
Hi Renze,
On Fri, 2024-03-29 at 14:01 +0100, Renze Nicolai wrote:
> This commit disables unused ADC channels and labels the ADC channels used with their function.
Please run this through checkpatch and address the warnings.
Also, the submitting patches documentation[1] suggests using the
imperative mood - instead of "This commit disables ...", use "Disable
...". The change subject is phrased the expected way.
[1]: https://docs.kernel.org/process/submitting-patches.html
Taking the subject and the description together, the description feels
a little redundant. Maybe it could be trimmed back to
> Also, label the ADC channels used with their function.
Andrew
^ permalink raw reply
* Re: [PATCH 1/3] ARM: dts: Modify GPIO table for Asrock X570D4U BMC
From: Andrew Jeffery @ 2024-04-03 3:21 UTC (permalink / raw)
To: Renze Nicolai, linux-arm-kernel, devicetree, linux-kernel,
linux-aspeed, arnd, olof, soc, robh+dt, krzysztof.kozlowski+dt,
joel, andrew
In-Reply-To: <20240329130152.878944-2-renze@rnplus.nl>
Hi Renze,
Do you mind running this patch and the others in the series through
./scripts/checkpatch.pl? Generally patches sent to the list should not
generate warnings.
It looks like these patches are generated against Joel's bmc/for-next
branch. He's applied your original X570D4U devicetree patch there,
(though that also causes checkpatch warnings).
On Fri, 2024-03-29 at 14:01 +0100, Renze Nicolai wrote:
> This commit removes button-nmi-n, this board does not have support for an NMI button.
> Input status-locatorled-n has been renamed to input-locatorled-n to better indicate the signal type.
> The suffix -n has been appended to the name of control-locatorbutton, button-power, control-power, button-reset, control-reset, input-id0, input-id1, input-id2, output-bmc-ready to reflect the inverted signal polarity.
> GPIO output-rtc-battery-voltage-read-enable has been renamed to output-hwm-vbat-enable, input-alert1-n to input-aux-smb-alert-n, input-alert3-n to input-psu-smb-alert-n, input-mfg to input-mfg-mode-n and input-caseopen to input-case-open-n.
> And GPIOs input-bmc-smb-present-n, input-pcie-wake-n, input-sleep-s3-n, input-sleep-s5-n and input-power-good have been added.
>
For instance, checkpatch warns about these lines in the commit message
being too long. They should be wrapped at 72 characters.
Additionally, the description forms a bit of a list of things the patch
is doing. Patches are easier to review when they only do one thing, as
it removes the need to assess whether there are subtle interactions
between the several things, and if so, whether they're expected and
correct.
I'd prefer this change be split up so there's no need for such
concerns.
> Signed-off-by: Renze Nicolai <renze@rnplus.nl>
> ---
> .../dts/aspeed/aspeed-bmc-asrock-x570d4u.dts | 116 +++++++++---------
> 1 file changed, 58 insertions(+), 58 deletions(-)
>
> diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-asrock-x570d4u.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-asrock-x570d4u.dts
> index 3c975bc41ae7..34bc382bf492 100644
> --- a/arch/arm/boot/dts/aspeed/aspeed-bmc-asrock-x570d4u.dts
> +++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-asrock-x570d4u.dts
> @@ -79,64 +79,64 @@ iio-hwmon {
> &gpio {
> status = "okay";
> gpio-line-names =
> - /*A0-A3*/ "status-locatorled-n", "", "button-nmi-n", "",
> - /*A4-A7*/ "", "", "", "",
> - /*B0-B3*/ "input-bios-post-cmplt-n", "", "", "",
> - /*B4-B7*/ "", "", "", "",
> - /*C0-C3*/ "", "", "", "",
> - /*C4-C7*/ "", "", "control-locatorbutton", "",
> - /*D0-D3*/ "button-power", "control-power", "button-reset", "control-reset",
> - /*D4-D7*/ "", "", "", "",
> - /*E0-E3*/ "", "", "", "",
> - /*E4-E7*/ "", "", "", "",
> - /*F0-F3*/ "", "", "", "",
> - /*F4-F7*/ "", "", "", "",
> - /*G0-G3*/ "output-rtc-battery-voltage-read-enable", "input-id0", "input-id1", "input-id2",
> - /*G4-G7*/ "input-alert1-n", "input-alert2-n", "input-alert3-n", "",
> - /*H0-H3*/ "", "", "", "",
> - /*H4-H7*/ "input-mfg", "", "led-heartbeat-n", "input-caseopen",
> - /*I0-I3*/ "", "", "", "",
> - /*I4-I7*/ "", "", "", "",
> - /*J0-J3*/ "output-bmc-ready", "", "", "",
> - /*J4-J7*/ "", "", "", "",
> - /*K0-K3*/ "", "", "", "",
> - /*K4-K7*/ "", "", "", "",
> - /*L0-L3*/ "", "", "", "",
> - /*L4-L7*/ "", "", "", "",
> - /*M0-M3*/ "", "", "", "",
> - /*M4-M7*/ "", "", "", "",
> - /*N0-N3*/ "", "", "", "",
> - /*N4-N7*/ "", "", "", "",
> - /*O0-O3*/ "", "", "", "",
> - /*O4-O7*/ "", "", "", "",
> - /*P0-P3*/ "", "", "", "",
> - /*P4-P7*/ "", "", "", "",
> - /*Q0-Q3*/ "", "", "", "",
> - /*Q4-Q7*/ "", "", "", "",
> - /*R0-R3*/ "", "", "", "",
> - /*R4-R7*/ "", "", "", "",
> - /*S0-S3*/ "input-bmc-pchhot-n", "", "", "",
> - /*S4-S7*/ "", "", "", "",
> - /*T0-T3*/ "", "", "", "",
> - /*T4-T7*/ "", "", "", "",
> - /*U0-U3*/ "", "", "", "",
> - /*U4-U7*/ "", "", "", "",
> - /*V0-V3*/ "", "", "", "",
> - /*V4-V7*/ "", "", "", "",
> - /*W0-W3*/ "", "", "", "",
> - /*W4-W7*/ "", "", "", "",
> - /*X0-X3*/ "", "", "", "",
> - /*X4-X7*/ "", "", "", "",
> - /*Y0-Y3*/ "", "", "", "",
> - /*Y4-Y7*/ "", "", "", "",
> - /*Z0-Z3*/ "", "", "led-fault-n", "output-bmc-throttle-n",
> - /*Z4-Z7*/ "", "", "", "",
> - /*AA0-AA3*/ "input-cpu1-thermtrip-latch-n", "", "input-cpu1-prochot-n", "",
> - /*AA4-AC7*/ "", "", "", "",
> - /*AB0-AB3*/ "", "", "", "",
> - /*AB4-AC7*/ "", "", "", "",
> - /*AC0-AC3*/ "", "", "", "",
> - /*AC4-AC7*/ "", "", "", "";
> + /*A0-A3*/ "input-locatorled-n", "", "", "",
> + /*A4-A7*/ "", "", "", "",
> + /*B0-B3*/ "input-bios-post-cmplt-n", "", "", "",
> + /*B4-B7*/ "", "", "", "",
> + /*C0-C3*/ "", "", "", "",
> + /*C4-C7*/ "", "", "control-locatorbutton-n", "",
> + /*D0-D3*/ "button-power-n", "control-power-n", "button-reset-n", "control-reset-n",
> + /*D4-D7*/ "", "", "", "",
> + /*E0-E3*/ "", "", "", "",
> + /*E4-E7*/ "", "", "", "",
> + /*F0-F3*/ "", "", "", "",
> + /*F4-F7*/ "", "", "", "",
> + /*G0-G3*/ "output-hwm-vbat-enable", "input-id0-n", "input-id1-n", "input-id2-n",
> + /*G4-G7*/ "input-aux-smb-alert-n", "", "input-psu-smb-alert-n", "",
> + /*H0-H3*/ "", "", "", "",
> + /*H4-H7*/ "input-mfg-mode-n", "", "led-heartbeat-n", "input-case-open-n",
> + /*I0-I3*/ "", "", "", "",
> + /*I4-I7*/ "", "", "", "",
> + /*J0-J3*/ "output-bmc-ready-n", "", "", "",
> + /*J4-J7*/ "", "", "", "",
> + /*K0-K3*/ "", "", "", "",
> + /*K4-K7*/ "", "", "", "",
> + /*L0-L3*/ "", "", "", "",
> + /*L4-L7*/ "", "", "", "",
> + /*M0-M3*/ "", "", "", "",
> + /*M4-M7*/ "", "", "", "",
> + /*N0-N3*/ "", "", "", "",
> + /*N4-N7*/ "", "", "", "",
> + /*O0-O3*/ "", "", "", "",
> + /*O4-O7*/ "", "", "", "",
> + /*P0-P3*/ "", "", "", "",
> + /*P4-P7*/ "", "", "", "",
> + /*Q0-Q3*/ "", "", "", "",
> + /*Q4-Q7*/ "input-bmc-smb-present-n", "", "", "input-pcie-wake-n",
> + /*R0-R3*/ "", "", "", "",
> + /*R4-R7*/ "", "", "", "",
> + /*S0-S3*/ "input-bmc-pchhot-n", "", "", "",
> + /*S4-S7*/ "", "", "", "",
> + /*T0-T3*/ "", "", "", "",
> + /*T4-T7*/ "", "", "", "",
> + /*U0-U3*/ "", "", "", "",
> + /*U4-U7*/ "", "", "", "",
> + /*V0-V3*/ "", "", "", "",
> + /*V4-V7*/ "", "", "", "",
> + /*W0-W3*/ "", "", "", "",
> + /*W4-W7*/ "", "", "", "",
> + /*X0-X3*/ "", "", "", "",
> + /*X4-X7*/ "", "", "", "",
> + /*Y0-Y3*/ "input-sleep-s3-n", "input-sleep-s5-n", "", "",
> + /*Y4-Y7*/ "", "", "", "",
> + /*Z0-Z3*/ "", "", "led-fault-n", "output-bmc-throttle-n",
> + /*Z4-Z7*/ "", "", "", "",
> + /*AA0-AA3*/ "input-cpu1-thermtrip-latch-n", "", "input-cpu1-prochot-n", "",
> + /*AA4-AC7*/ "", "", "", "",
> + /*AB0-AB3*/ "", "input-power-good", "", "",
> + /*AB4-AC7*/ "", "", "", "",
> + /*AC0-AC3*/ "", "", "", "",
> + /*AC4-AC7*/ "", "", "", "";
> };
>
I'd like some discussion in the commit message of whether these names
align with net names in the schematic, follow the OpenBMC GPIO naming
guidelines, or use some other strategy entirely.
Also, the columnisation of the names leads to more warnings from
checkpatch (due to line length). Other Aspeed-based devicetrees tend
not to make the whitespace so significant, and generally group the
GPIOs by complete banks. I prefer that the X570D4U devicetree is
consistent with the others.
Andrew
^ permalink raw reply
* Re: [PATCH v1 1/5] dt-bindings: pwm: Add Loongson PWM controller
From: Binbin Zhou @ 2024-04-03 3:16 UTC (permalink / raw)
To: Rob Herring
Cc: Binbin Zhou, Huacai Chen, Uwe Kleine-König,
Krzysztof Kozlowski, Conor Dooley, Huacai Chen, loongson-kernel,
linux-pwm, devicetree, Xuerui Wang, loongarch
In-Reply-To: <CAMpQs4K_VSqdm7x=cSyMTBYQyOm=th0YrYKdZ74dp35hyRBXgQ@mail.gmail.com>
On Wed, Apr 3, 2024 at 8:37 AM Binbin Zhou <zhoubb.aaron@gmail.com> wrote:
>
> Hi Rob:
>
> Thanks for your reply.
>
> On Tue, Apr 2, 2024 at 11:40 PM Rob Herring <robh@kernel.org> wrote:
> >
> > On Tue, Apr 02, 2024 at 03:58:38PM +0800, Binbin Zhou wrote:
> > > Add Loongson PWM controller binding with DT schema format using
> > > json-schema.
> > >
> > > Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
> > > ---
> > > .../devicetree/bindings/pwm/pwm-loongson.yaml | 64 +++++++++++++++++++
> >
> > Filename should match compatible.
>
> Emm... How about renaming it as loongson, pwm.yaml?
>
> >
> > > MAINTAINERS | 6 ++
> > > 2 files changed, 70 insertions(+)
> > > create mode 100644 Documentation/devicetree/bindings/pwm/pwm-loongson.yaml
> > >
> > > diff --git a/Documentation/devicetree/bindings/pwm/pwm-loongson.yaml b/Documentation/devicetree/bindings/pwm/pwm-loongson.yaml
> > > new file mode 100644
> > > index 000000000000..d25904468353
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/pwm/pwm-loongson.yaml
> > > @@ -0,0 +1,64 @@
> > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/pwm/pwm-loongson.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: Loongson PWM Controller
> > > +
> > > +maintainers:
> > > + - Binbin Zhou <zhoubinbin@loongson.cn>
> > > +
> > > +description:
> > > + It is the generic PWM framework driver for Loongson family.
> >
> > That's describing the driver. Not really relevant to the binding.
> >
> Ok ,I will rewrite this part.
>
> >
> > > + Each PWM has one pulse width output signal and one pulse input
> > > + signal to be measured.
> > > + It can be found on Loongson-2K series cpus and Loongson LS7A bridge chips.
> > > +
> > > +allOf:
> > > + - $ref: pwm.yaml#
> > > +
> > > +properties:
> > > + compatible:
> > > + oneOf:
> > > + - const: loongson,ls7a-pwm
> > > + - items:
> > > + - enum:
> > > + - loongson,ls2k0500-pwm
> > > + - loongson,ls2k1000-pwm
> > > + - loongson,ls2k2000-pwm
> > > + - const: loongson,ls7a-pwm
> > > +
> > > + reg:
> > > + maxItems: 1
> > > +
> > > + interrupts:
> > > + maxItems: 1
> > > +
> > > + clocks:
> > > + maxItems: 1
> > > +
> > > + '#pwm-cells':
> > > + const: 3
> >
> > Please define what is in each cell. If there's only 2 signals, then the
> > first cell defines the output or input (what value for which one?).
Hi Rob:
Sorry, the previous email did not answer this question.
The first cell defines the output signal, and its value is 0.
Thanks.
Binbin
> >
> > Really, the PWM binding is only for outputs, so is a cell even needed? I
> > suppose we could use it for inputs too, but that's really "input
> > capture" type operation that timers often have. I'll defer to the PWM
> > maintainers...
>
> Ok, I will try to add some description about it.
>
> If I understand correctly, the meaning of each cell in "#pwm-cells"is
> determined.
> The first cell specifies the per-chip index of the PWM to use, the
> second cell is the period in nanoseconds and the third cell is the
> polarity.
>
> >
> > > +
> > > +required:
> > > + - compatible
> > > + - reg
> > > + - interrupts
> > > + - clocks
> > > + - '#pwm-cells'
> >
> > pwm.yaml makes this required already.
> >
> Yes, this is unnecessary. I will drop it in the next version.
>
> Thanks.
> Binbin
> > Rob
> >
^ permalink raw reply
* Re: [PATCH 1/7] arm64: dts: imx8-ss-lsio: fix pwm lpcg indices
From: Frank Li @ 2024-04-03 3:10 UTC (permalink / raw)
To: Shawn Guo
Cc: Fabio Estevam, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Shawn Guo, Sascha Hauer, Pengutronix Kernel Team, NXP Linux Team,
Marcel Ziswiler, Philippe Schenker, Max Krummenacher,
Alexander Stein, Joakim Zhang, devicetree, linux-arm-kernel,
linux-kernel, stable
In-Reply-To: <ZgyzxmuMIK87C2nW@dragon>
On Wed, Apr 03, 2024 at 09:41:26AM +0800, Shawn Guo wrote:
> On Tue, Apr 02, 2024 at 11:09:17AM -0400, Frank Li wrote:
> > On Mon, Apr 01, 2024 at 08:04:56PM -0300, Fabio Estevam wrote:
> > > On Mon, Apr 1, 2024 at 7:25 PM Frank Li <Frank.Li@nxp.com> wrote:
> > > >
> > > > lpcg's arg0 should use clock indices instead of index.
> > > >
> > > > pwm0_lpcg: clock-controller@5d400000 {
> > > > ... // Col1 Col2
> > > > clocks = <&clk IMX_SC_R_PWM_0 IMX_SC_PM_CLK_PER>, // 0 0
> > > > <&clk IMX_SC_R_PWM_0 IMX_SC_PM_CLK_PER>, // 1 1
> > > > <&clk IMX_SC_R_PWM_0 IMX_SC_PM_CLK_PER>, // 2 4
> > > > <&lsio_bus_clk>, // 3 5
> > > > <&clk IMX_SC_R_PWM_0 IMX_SC_PM_CLK_PER>; // 4 6
> > > > clock-indices = <IMX_LPCG_CLK_0>, <IMX_LPCG_CLK_1>,
> > > > <IMX_LPCG_CLK_4>, <IMX_LPCG_CLK_5>,
> > > > <IMX_LPCG_CLK_6>;
> > > > };
> > > >
> > > > Col1: index, which exited dts try to get.
> > >
> > > I cannot understand this sentence, sorry.
> >
> > This base on downstream dts code. Downstream code use index in 'Col1' to
> > get clock.
>
> So s/exited/existing you meant?
Yes, sorry for typo.
Frank
>
> Shawn
>
^ permalink raw reply
* Re: [PATCH net-next] dt-bindings: net: snps,dwmac: Align 'snps,priority' type definition
From: patchwork-bot+netdevbpf @ 2024-04-03 2:40 UTC (permalink / raw)
To: Rob Herring
Cc: davem, edumazet, kuba, pabeni, krzysztof.kozlowski+dt, conor+dt,
alexandre.torgue, peppe.cavallaro, joabreu, netdev, devicetree,
linux-kernel
In-Reply-To: <20240401204422.1692359-2-robh@kernel.org>
Hello:
This patch was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Mon, 1 Apr 2024 15:44:22 -0500 you wrote:
> 'snps,priority' is also defined in dma/snps,dw-axi-dmac.yaml as a
> uint32-array. It's preferred to have a single type for a given property
> name, so update the type in snps,dwmac schema to match.
>
> Signed-off-by: Rob Herring <robh@kernel.org>
> ---
> Documentation/devicetree/bindings/net/snps,dwmac.yaml | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
Here is the summary with links:
- [net-next] dt-bindings: net: snps,dwmac: Align 'snps,priority' type definition
https://git.kernel.org/netdev/net-next/c/992c287d8778
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH v1 1/5] dt-bindings: pwm: Add Loongson PWM controller
From: Binbin Zhou @ 2024-04-03 2:37 UTC (permalink / raw)
To: Rob Herring
Cc: Binbin Zhou, Huacai Chen, Uwe Kleine-König,
Krzysztof Kozlowski, Conor Dooley, Huacai Chen, loongson-kernel,
linux-pwm, devicetree, Xuerui Wang, loongarch
In-Reply-To: <20240402174051.GA324804-robh@kernel.org>
Hi Rob:
Thanks for your reply.
On Tue, Apr 2, 2024 at 11:40 PM Rob Herring <robh@kernel.org> wrote:
>
> On Tue, Apr 02, 2024 at 03:58:38PM +0800, Binbin Zhou wrote:
> > Add Loongson PWM controller binding with DT schema format using
> > json-schema.
> >
> > Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
> > ---
> > .../devicetree/bindings/pwm/pwm-loongson.yaml | 64 +++++++++++++++++++
>
> Filename should match compatible.
Emm... How about renaming it as loongson, pwm.yaml?
>
> > MAINTAINERS | 6 ++
> > 2 files changed, 70 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/pwm/pwm-loongson.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/pwm/pwm-loongson.yaml b/Documentation/devicetree/bindings/pwm/pwm-loongson.yaml
> > new file mode 100644
> > index 000000000000..d25904468353
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/pwm/pwm-loongson.yaml
> > @@ -0,0 +1,64 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/pwm/pwm-loongson.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Loongson PWM Controller
> > +
> > +maintainers:
> > + - Binbin Zhou <zhoubinbin@loongson.cn>
> > +
> > +description:
> > + It is the generic PWM framework driver for Loongson family.
>
> That's describing the driver. Not really relevant to the binding.
>
Ok ,I will rewrite this part.
>
> > + Each PWM has one pulse width output signal and one pulse input
> > + signal to be measured.
> > + It can be found on Loongson-2K series cpus and Loongson LS7A bridge chips.
> > +
> > +allOf:
> > + - $ref: pwm.yaml#
> > +
> > +properties:
> > + compatible:
> > + oneOf:
> > + - const: loongson,ls7a-pwm
> > + - items:
> > + - enum:
> > + - loongson,ls2k0500-pwm
> > + - loongson,ls2k1000-pwm
> > + - loongson,ls2k2000-pwm
> > + - const: loongson,ls7a-pwm
> > +
> > + reg:
> > + maxItems: 1
> > +
> > + interrupts:
> > + maxItems: 1
> > +
> > + clocks:
> > + maxItems: 1
> > +
> > + '#pwm-cells':
> > + const: 3
>
> Please define what is in each cell. If there's only 2 signals, then the
> first cell defines the output or input (what value for which one?).
>
> Really, the PWM binding is only for outputs, so is a cell even needed? I
> suppose we could use it for inputs too, but that's really "input
> capture" type operation that timers often have. I'll defer to the PWM
> maintainers...
Ok, I will try to add some description about it.
If I understand correctly, the meaning of each cell in "#pwm-cells"is
determined.
The first cell specifies the per-chip index of the PWM to use, the
second cell is the period in nanoseconds and the third cell is the
polarity.
>
> > +
> > +required:
> > + - compatible
> > + - reg
> > + - interrupts
> > + - clocks
> > + - '#pwm-cells'
>
> pwm.yaml makes this required already.
>
Yes, this is unnecessary. I will drop it in the next version.
Thanks.
Binbin
> Rob
>
^ permalink raw reply
* Re: [PATCH v3 4/4] drm: panel: Add LG sw43408 panel driver
From: Dmitry Baryshkov @ 2024-04-03 2:37 UTC (permalink / raw)
To: Marijn Suijten
Cc: Sumit Semwal, Caleb Connolly, Neil Armstrong, Jessica Zhang,
Sam Ravnborg, David Airlie, Daniel Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, dri-devel, devicetree,
linux-kernel, linux-arm-msm, Vinod Koul, Caleb Connolly
In-Reply-To: <3hh7nfqflj73422q47p6do7aiormxwspwkfg557epeqn2krtcc@dsufsohfaz4l>
On Tue, Apr 02, 2024 at 11:17:52PM +0200, Marijn Suijten wrote:
> On 2024-04-02 02:51:15, Dmitry Baryshkov wrote:
> > From: Sumit Semwal <sumit.semwal@linaro.org>
> >
> > LG SW43408 is 1080x2160, 4-lane MIPI-DSI panel, used in some Pixel3
> > phones.
>
> @60Hz?
With the current settings and timings I'm only getting 30 Hz. I have to
double the mode->clock to get 60.
>
> >
> > Signed-off-by: Sumit Semwal <sumit.semwal@linaro.org>
> > [vinod: Add DSC support]
> > Signed-off-by: Vinod Koul <vkoul@kernel.org>
> > [caleb: cleanup and support turning off the panel]
> > Signed-off-by: Caleb Connolly <caleb@connolly.tech>
> > [DB: partially rewrote the driver and fixed DSC programming]
> > Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
>
> Some small nits but I think this deserves a:
>
> Reviewed-by: Marijn Suijten <marijn.suijten@somainline.org>
>
> > ---
> > MAINTAINERS | 8 +
> > drivers/gpu/drm/panel/Kconfig | 11 ++
> > drivers/gpu/drm/panel/Makefile | 1 +
> > drivers/gpu/drm/panel/panel-lg-sw43408.c | 326 +++++++++++++++++++++++++++++++
> > 4 files changed, 346 insertions(+)
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index d36c19c1bf81..4cc43c16e07e 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -6789,6 +6789,14 @@ S: Maintained
> > F: Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml
> > F: drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c
> >
> > +DRM DRIVER FOR LG SW43408 PANELS
> > +M: Sumit Semwal <sumit.semwal@linaro.org>
> > +M: Caleb Connolly <caleb.connolly@linaro.org>
> > +S: Maintained
> > +T: git git://anongit.freedesktop.org/drm/drm-misc
> > +F: Documentation/devicetree/bindings/display/panel/lg,sw43408.yaml
> > +F: drivers/gpu/drm/panel/panel-lg-sw43408.c
> > +
> > DRM DRIVER FOR LOGICVC DISPLAY CONTROLLER
> > M: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
> > S: Supported
> > diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> > index 6dc451f58a3e..a55e9437c8cf 100644
> > --- a/drivers/gpu/drm/panel/Kconfig
> > +++ b/drivers/gpu/drm/panel/Kconfig
> > @@ -335,6 +335,17 @@ config DRM_PANEL_LG_LG4573
> > Say Y here if you want to enable support for LG4573 RGB panel.
> > To compile this driver as a module, choose M here.
> >
> > +config DRM_PANEL_LG_SW43408
> > + tristate "LG SW43408 panel"
> > + depends on OF
> > + depends on DRM_MIPI_DSI
> > + depends on BACKLIGHT_CLASS_DEVICE
> > + help
> > + Say Y here if you want to enable support for LG sw43408 panel.
> > + The panel has a 1080x2160 resolution and uses
> > + 24 bit RGB per pixel. It provides a MIPI DSI interface to
> > + the host and has a built-in LED backlight.
> > +
> > config DRM_PANEL_MAGNACHIP_D53E6EA8966
> > tristate "Magnachip D53E6EA8966 DSI panel"
> > depends on OF && SPI
> > diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> > index 24a02655d726..0b40b010e8e7 100644
> > --- a/drivers/gpu/drm/panel/Makefile
> > +++ b/drivers/gpu/drm/panel/Makefile
> > @@ -34,6 +34,7 @@ obj-$(CONFIG_DRM_PANEL_LEADTEK_LTK050H3146W) += panel-leadtek-ltk050h3146w.o
> > obj-$(CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829) += panel-leadtek-ltk500hd1829.o
> > obj-$(CONFIG_DRM_PANEL_LG_LB035Q02) += panel-lg-lb035q02.o
> > obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
> > +obj-$(CONFIG_DRM_PANEL_LG_SW43408) += panel-lg-sw43408.o
> > obj-$(CONFIG_DRM_PANEL_MAGNACHIP_D53E6EA8966) += panel-magnachip-d53e6ea8966.o
> > obj-$(CONFIG_DRM_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
> > obj-$(CONFIG_DRM_PANEL_NEWVISION_NV3051D) += panel-newvision-nv3051d.o
> > diff --git a/drivers/gpu/drm/panel/panel-lg-sw43408.c b/drivers/gpu/drm/panel/panel-lg-sw43408.c
> > new file mode 100644
> > index 000000000000..c7611bfa796b
> > --- /dev/null
> > +++ b/drivers/gpu/drm/panel/panel-lg-sw43408.c
> > @@ -0,0 +1,326 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Copyright (C) 2019-2024 Linaro Ltd
> > + * Author: Sumit Semwal <sumit.semwal@linaro.org>
> > + * Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> > + */
> > +
> > +#include <linux/backlight.h>
> > +#include <linux/delay.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/regulator/consumer.h>
> > +
> > +#include <video/mipi_display.h>
> > +
> > +#include <drm/drm_mipi_dsi.h>
> > +#include <drm/drm_panel.h>
> > +#include <drm/drm_probe_helper.h>
> > +#include <drm/display/drm_dsc.h>
> > +#include <drm/display/drm_dsc_helper.h>
> > +
> > +#define NUM_SUPPLIES 2
> > +
> > +struct sw43408_panel {
> > + struct drm_panel base;
> > + struct mipi_dsi_device *link;
> > +
> > + const struct drm_display_mode *mode;
> > +
> > + struct regulator_bulk_data supplies[NUM_SUPPLIES];
> > +
> > + struct gpio_desc *reset_gpio;
> > +
> > + struct drm_dsc_config dsc;
> > +};
> > +
> > +static inline struct sw43408_panel *to_panel_info(struct drm_panel *panel)
> > +{
> > + return container_of(panel, struct sw43408_panel, base);
> > +}
> > +
> > +static int sw43408_unprepare(struct drm_panel *panel)
> > +{
> > + struct sw43408_panel *ctx = to_panel_info(panel);
> > + int ret;
> > +
> > + ret = mipi_dsi_dcs_set_display_off(ctx->link);
> > + if (ret < 0)
> > + dev_err(panel->dev, "set_display_off cmd failed ret = %d\n", ret);
> > +
> > + ret = mipi_dsi_dcs_enter_sleep_mode(ctx->link);
> > + if (ret < 0)
> > + dev_err(panel->dev, "enter_sleep cmd failed ret = %d\n", ret);
> > +
> > + msleep(100);
> > +
> > + gpiod_set_value(ctx->reset_gpio, 1);
> > +
> > + return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> > +}
> > +
> > +static int sw43408_program(struct drm_panel *panel)
> > +{
> > + struct sw43408_panel *ctx = to_panel_info(panel);
> > + struct drm_dsc_picture_parameter_set pps;
> > +
> > + mipi_dsi_dcs_write_seq(ctx->link, MIPI_DCS_SET_GAMMA_CURVE, 0x02);
> > +
> > + mipi_dsi_dcs_set_tear_on(ctx->link, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> > +
> > + mipi_dsi_dcs_write_seq(ctx->link, 0x53, 0x0c, 0x30);
> > + mipi_dsi_dcs_write_seq(ctx->link, 0x55, 0x00, 0x70, 0xdf, 0x00, 0x70, 0xdf);
> > + mipi_dsi_dcs_write_seq(ctx->link, 0xf7, 0x01, 0x49, 0x0c);
> > +
> > + mipi_dsi_dcs_exit_sleep_mode(ctx->link);
> > +
> > + msleep(135);
> > +
> > + /* COMPRESSION_MODE moved after setting the PPS */
> > +
> > + mipi_dsi_dcs_write_seq(ctx->link, 0xb0, 0xac);
> > + mipi_dsi_dcs_write_seq(ctx->link, 0xe5,
> > + 0x00, 0x3a, 0x00, 0x3a, 0x00, 0x0e, 0x10);
> > + mipi_dsi_dcs_write_seq(ctx->link, 0xb5,
> > + 0x75, 0x60, 0x2d, 0x5d, 0x80, 0x00, 0x0a, 0x0b,
> > + 0x00, 0x05, 0x0b, 0x00, 0x80, 0x0d, 0x0e, 0x40,
> > + 0x00, 0x0c, 0x00, 0x16, 0x00, 0xb8, 0x00, 0x80,
> > + 0x0d, 0x0e, 0x40, 0x00, 0x0c, 0x00, 0x16, 0x00,
> > + 0xb8, 0x00, 0x81, 0x00, 0x03, 0x03, 0x03, 0x01,
> > + 0x01);
> > + msleep(85);
> > + mipi_dsi_dcs_write_seq(ctx->link, 0xcd,
> > + 0x00, 0x00, 0x00, 0x19, 0x19, 0x19, 0x19, 0x19,
> > + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
> > + 0x16, 0x16);
> > + mipi_dsi_dcs_write_seq(ctx->link, 0xcb, 0x80, 0x5c, 0x07, 0x03, 0x28);
> > + mipi_dsi_dcs_write_seq(ctx->link, 0xc0, 0x02, 0x02, 0x0f);
> > + mipi_dsi_dcs_write_seq(ctx->link, 0x55, 0x04, 0x61, 0xdb, 0x04, 0x70, 0xdb);
> > + mipi_dsi_dcs_write_seq(ctx->link, 0xb0, 0xca);
> > +
> > + mipi_dsi_dcs_set_display_on(ctx->link);
> > +
> > + msleep(50);
> > +
> > + ctx->link->mode_flags &= ~MIPI_DSI_MODE_LPM;
> > +
> > + drm_dsc_pps_payload_pack(&pps, ctx->link->dsc);
> > + mipi_dsi_picture_parameter_set(ctx->link, &pps);
> > +
> > + /* This panel uses shifted PPS selectors:
> > + * 1 if pps_identifier is 0
> > + * 2 if pps_identifier is 1
> > + */
> > + mipi_dsi_compression_mode_ext(ctx->link, true,
> > + MIPI_DSI_COMPRESSION_DSC, 1);
>
> Let's be careful to watch the order of parameters here whichever way you fix it
> up in the patch that introduces this function.
>
> > +
> > + ctx->link->mode_flags |= MIPI_DSI_MODE_LPM;
> > +
> > + return 0;
> > +}
> > +
> > +static int sw43408_prepare(struct drm_panel *panel)
> > +{
> > + struct sw43408_panel *ctx = to_panel_info(panel);
> > + int ret;
> > +
> > + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> > + if (ret < 0)
> > + return ret;
> > +
> > + usleep_range(5000, 6000);
> > +
> > + gpiod_set_value(ctx->reset_gpio, 0);
> > + usleep_range(9000, 10000);
> > + gpiod_set_value(ctx->reset_gpio, 1);
> > + usleep_range(1000, 2000);
> > + gpiod_set_value(ctx->reset_gpio, 0);
> > + usleep_range(9000, 10000);
> > +
> > + ret = sw43408_program(panel);
> > + if (ret)
> > + goto poweroff;
> > +
> > + return 0;
> > +
> > +poweroff:
> > + gpiod_set_value(ctx->reset_gpio, 1);
> > + regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
> > + return ret;
> > +}
> > +
> > +static int sw43408_get_modes(struct drm_panel *panel,
> > + struct drm_connector *connector)
> > +{
> > + struct sw43408_panel *ctx = to_panel_info(panel);
> > +
> > + return drm_connector_helper_get_modes_fixed(connector, ctx->mode);
> > +}
> > +
> > +static int sw43408_backlight_update_status(struct backlight_device *bl)
> > +{
> > + struct mipi_dsi_device *dsi = bl_get_data(bl);
> > + uint16_t brightness = backlight_get_brightness(bl);
> > +
> > + return mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
> > +}
> > +
> > +const struct backlight_ops sw43408_backlight_ops = {
> > + .update_status = sw43408_backlight_update_status,
> > +};
> > +
> > +static int sw43408_backlight_init(struct sw43408_panel *ctx)
> > +{
> > + struct device *dev = &ctx->link->dev;
> > + const struct backlight_properties props = {
> > + .type = BACKLIGHT_PLATFORM,
> > + .brightness = 255,
> > + .max_brightness = 255,
> > + };
> > +
> > + ctx->base.backlight = devm_backlight_device_register(dev, dev_name(dev), dev,
> > + ctx->link,
> > + &sw43408_backlight_ops,
> > + &props);
> > +
> > + if (IS_ERR(ctx->base.backlight))
> > + return dev_err_probe(dev, PTR_ERR(ctx->base.backlight),
> > + "Failed to create backlight\n");
> > +
> > + return 0;
> > +}
> > +
> > +static const struct drm_panel_funcs sw43408_funcs = {
> > + .unprepare = sw43408_unprepare,
> > + .prepare = sw43408_prepare,
> > + .get_modes = sw43408_get_modes,
> > +};
> > +
> > +static const struct drm_display_mode sw43408_default_mode = {
> > + .clock = 152340,
>
> Since this value is calculated from the values below, I prefer to just show the
> origin of the value:
>
> .clock = (1080 + 20 + 32 + 20) * (2160 + 20 + 4 + 20) * 60 / 1000,
Sure
>
> > +
> > + .hdisplay = 1080,
> > + .hsync_start = 1080 + 20,
> > + .hsync_end = 1080 + 20 + 32,
> > + .htotal = 1080 + 20 + 32 + 20,
> > +
> > + .vdisplay = 2160,
> > + .vsync_start = 2160 + 20,
> > + .vsync_end = 2160 + 20 + 4,
> > + .vtotal = 2160 + 20 + 4 + 20,
> > +
> > + .width_mm = 62,
> > + .height_mm = 124,
> > +
> > + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
> > +};
> > +
> > +static const struct of_device_id sw43408_of_match[] = {
> > + { .compatible = "lg,sw43408", .data = &sw43408_default_mode },
>
> Will you ever use multiple compatibles to select different modes?
>
> For panels that support multiple modes (e.g. a lot of high-end sony devices
> with their 4k@120Hz screens) I'm still planning on adding an atomic_prepare() to
> drm_bridge and drm_panel to make it possible to program the DSC block and send
> DCS relative to the selected mode (and/or perform a fluent mode switch).
I think this got inherited from the initial implementation by Sumit.
Let's rewrite this too.
>
> > + { /* sentinel */ }
> > +};
> > +MODULE_DEVICE_TABLE(of, sw43408_of_match);
> > +
> > +static int sw43408_add(struct sw43408_panel *ctx)
> > +{
> > + struct device *dev = &ctx->link->dev;
> > + int ret;
> > +
> > + ctx->supplies[0].supply = "vddi"; /* 1.88 V */
> > + ctx->supplies[0].init_load_uA = 62000;
> > + ctx->supplies[1].supply = "vpnl"; /* 3.0 V */
> > + ctx->supplies[1].init_load_uA = 857000;
> > +
> > + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies),
> > + ctx->supplies);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> > + if (IS_ERR(ctx->reset_gpio)) {
> > + dev_err(dev, "cannot get reset gpio %ld\n",
> > + PTR_ERR(ctx->reset_gpio));
> > + return PTR_ERR(ctx->reset_gpio);
> > + }
> > +
> > + ret = sw43408_backlight_init(ctx);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ctx->base.prepare_prev_first = true;
> > +
> > + drm_panel_init(&ctx->base, dev, &sw43408_funcs, DRM_MODE_CONNECTOR_DSI);
> > +
> > + drm_panel_add(&ctx->base);
> > + return ret;
> > +}
> > +
> > +static int sw43408_probe(struct mipi_dsi_device *dsi)
> > +{
> > + struct sw43408_panel *ctx;
> > + int ret;
> > +
> > + ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
> > + if (!ctx)
> > + return -ENOMEM;
> > +
> > + ctx->mode = of_device_get_match_data(&dsi->dev);
> > + dsi->mode_flags = MIPI_DSI_MODE_LPM;
> > + dsi->format = MIPI_DSI_FMT_RGB888;
> > + dsi->lanes = 4;
> > +
> > + ctx->link = dsi;
> > + mipi_dsi_set_drvdata(dsi, ctx);
> > +
> > + ret = sw43408_add(ctx);
> > + if (ret < 0)
> > + return ret;
> > +
> > + /* The panel is DSC panel only, set the dsc params */
>
> Grammar?
>
> > + ctx->dsc.dsc_version_major = 0x1;
> > + ctx->dsc.dsc_version_minor = 0x1;
> > +
> > + /* slice_count * slice_width == width */
> > + ctx->dsc.slice_height = 16;
> > + ctx->dsc.slice_width = 540;
> > + ctx->dsc.slice_count = 2;
> > + ctx->dsc.bits_per_component = 8;
> > + ctx->dsc.bits_per_pixel = 8 << 4;
> > + ctx->dsc.block_pred_enable = true;
> > +
> > + dsi->dsc = &ctx->dsc;
> > +
> > + return mipi_dsi_attach(dsi);
> > +}
> > +
> > +static void sw43408_remove(struct mipi_dsi_device *dsi)
> > +{
> > + struct sw43408_panel *ctx = mipi_dsi_get_drvdata(dsi);
> > + int ret;
> > +
> > + ret = sw43408_unprepare(&ctx->base);
> > + if (ret < 0)
> > + dev_err(&dsi->dev, "failed to unprepare panel: %d\n",
> > + ret);
> > +
> > + ret = mipi_dsi_detach(dsi);
> > + if (ret < 0)
> > + dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
> > +
> > + drm_panel_remove(&ctx->base);
> > +}
> > +
> > +static struct mipi_dsi_driver sw43408_driver = {
> > + .driver = {
> > + .name = "panel-lg-sw43408",
> > + .of_match_table = sw43408_of_match,
> > + },
> > + .probe = sw43408_probe,
> > + .remove = sw43408_remove,
> > +};
> > +module_mipi_dsi_driver(sw43408_driver);
> > +
> > +MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");
> > +MODULE_DESCRIPTION("LG SW436408 MIPI-DSI LED panel");
> > +MODULE_LICENSE("GPL");
> >
> > --
> > 2.39.2
> >
--
With best wishes
Dmitry
^ permalink raw reply
* Re: [PATCH v3 3/4] drm/mipi-dsi: add mipi_dsi_compression_mode_ext()
From: Dmitry Baryshkov @ 2024-04-03 2:35 UTC (permalink / raw)
To: Marijn Suijten
Cc: Sumit Semwal, Caleb Connolly, Neil Armstrong, Jessica Zhang,
Sam Ravnborg, David Airlie, Daniel Vetter, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, dri-devel, devicetree,
linux-kernel, linux-arm-msm
In-Reply-To: <dgvztw55cr4wsbfxhf4ondzhaetodfcv4pndfug73suae5vp3v@4ozlmmsemfbm>
On Tue, Apr 02, 2024 at 11:09:29PM +0200, Marijn Suijten wrote:
> On 2024-04-02 02:51:14, Dmitry Baryshkov wrote:
> > Add the extended version of mipi_dsi_compression_mode(). It provides
> > a way to specify the algorithm and PPS selector.
> >
> > Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
> > ---
> > drivers/gpu/drm/drm_mipi_dsi.c | 33 +++++++++++++++++++++++++++------
> > include/drm/drm_mipi_dsi.h | 9 +++++++++
> > 2 files changed, 36 insertions(+), 6 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
> > index 9874ff6d4718..0ecbc811eb7a 100644
> > --- a/drivers/gpu/drm/drm_mipi_dsi.c
> > +++ b/drivers/gpu/drm/drm_mipi_dsi.c
> > @@ -645,19 +645,24 @@ int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
> > EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
> >
> > /**
> > - * mipi_dsi_compression_mode() - enable/disable DSC on the peripheral
> > + * mipi_dsi_compression_mode_ext() - enable/disable DSC on the peripheral
> > * @dsi: DSI peripheral device
> > * @enable: Whether to enable or disable the DSC
> > + * @algo: Selected algorithm
> > + * @pps_selector: The PPS selector
>
> Not a big fan of paraphrasing the parameter name, it adds no value. How about
> describing what this parameter means and what it does?:
>
> PPS table index to use. Corresponds to a table pre-programmed on the peripheral
> or a table programmed with &drm_dsc_picture_parameter_set.pps_identifier.
>
> (That should be a valid kernel-doc cross-reference to the field)
I don't think such meaning is defined in the standard. In the end, the
pps_identifier is 8-bit wide, while this field has just 2 bits and 4
possible values. The standards are pretty vague about this field.
>
> > *
> > - * Enable or disable Display Stream Compression on the peripheral using the
> > - * default Picture Parameter Set and VESA DSC 1.1 algorithm.
> > + * Enable or disable Display Stream Compression on the peripheral.
> > *
> > * Return: 0 on success or a negative error code on failure.
> > */
> > -int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
> > +int mipi_dsi_compression_mode_ext(struct mipi_dsi_device *dsi, bool enable,
> > + enum mipi_dsi_compression_algo algo,
> > + unsigned int pps_selector)
> > {
> > - /* Note: Needs updating for non-default PPS or algorithm */
> > - u8 tx[2] = { enable << 0, 0 };
> > + u8 data = (enable << 0) |
> > + (algo << 1) |
> > + (pps_selector << 4);
>
> Do we need some size validation (if > 3 return -EINVAL)? FIELD_PREP() might be
> too heavy though.
Ack, let's add it.
>
> > + u8 tx[2] = { data, 0 };
> > struct mipi_dsi_msg msg = {
> > .channel = dsi->channel,
> > .type = MIPI_DSI_COMPRESSION_MODE,
> > @@ -668,6 +673,22 @@ int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
> >
> > return (ret < 0) ? ret : 0;
> > }
> > +EXPORT_SYMBOL(mipi_dsi_compression_mode_ext);
> > +
> > +/**
> > + * mipi_dsi_compression_mode() - enable/disable DSC on the peripheral
> > + * @dsi: DSI peripheral device
> > + * @enable: Whether to enable or disable the DSC
> > + *
> > + * Enable or disable Display Stream Compression on the peripheral using the
> > + * default Picture Parameter Set and VESA DSC 1.1 algorithm.
> > + *
> > + * Return: 0 on success or a negative error code on failure.
> > + */
> > +int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable)
> > +{
> > + return mipi_dsi_compression_mode_ext(dsi, enable, 0, MIPI_DSI_COMPRESSION_DSC);
>
> I hope the compiler complains here that it should be MIPI_DSI_COMPRESSION_DSC,0
Nope, it didn't. Both are integers.
>
> (Enum algo first, int pps_selector last)
>
> > +}
> > EXPORT_SYMBOL(mipi_dsi_compression_mode);
> >
> > /**
> > diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
> > index 3011d33eccbd..78cb7b688b1d 100644
> > --- a/include/drm/drm_mipi_dsi.h
> > +++ b/include/drm/drm_mipi_dsi.h
> > @@ -226,6 +226,12 @@ static inline int mipi_dsi_pixel_format_to_bpp(enum mipi_dsi_pixel_format fmt)
> > return -EINVAL;
> > }
> >
> > +enum mipi_dsi_compression_algo {
> > + MIPI_DSI_COMPRESSION_DSC = 0,
>
> Add 1.1? Or does it also allow 1.2 (when the version is also set via PPS)?
I have only DSI 1.3 at hand, which only talks about 1.1. I think 1.2 is
allowed by inheritance. That's why I skipped the version here.
>
> > + MIPI_DSI_COMPRESSION_VENDOR = 3,
> > + /* other two values are reserved, DSI 1.3 */
> > +};
> > +
> > struct mipi_dsi_device *
> > mipi_dsi_device_register_full(struct mipi_dsi_host *host,
> > const struct mipi_dsi_device_info *info);
> > @@ -242,6 +248,9 @@ int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi);
> > int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
> > u16 value);
> > int mipi_dsi_compression_mode(struct mipi_dsi_device *dsi, bool enable);
> > +int mipi_dsi_compression_mode_ext(struct mipi_dsi_device *dsi, bool enable,
> > + unsigned int pps_selector,
> > + enum mipi_dsi_compression_algo algo);
>
> Oh, this declaration is inverse from the definition...
I'll check it.
>
> - Marijn
>
> > int mipi_dsi_picture_parameter_set(struct mipi_dsi_device *dsi,
> > const struct drm_dsc_picture_parameter_set *pps);
> >
> >
> > --
> > 2.39.2
> >
--
With best wishes
Dmitry
^ permalink raw reply
* Re: [PATCH v7 2/2] dmaengine: Loongson1: Add Loongson-1 APB DMA driver
From: Keguang Zhang @ 2024-04-03 2:23 UTC (permalink / raw)
To: Huacai Chen
Cc: Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-mips, dmaengine, devicetree, linux-kernel
In-Reply-To: <CAAhV-H5BSS1k6aCLYg4DT12nq-d4YyAKYBvy2ucLydwjVKNhBw@mail.gmail.com>
On Tue, Apr 2, 2024 at 10:50 PM Huacai Chen <chenhuacai@kernel.org> wrote:
>
> On Tue, Apr 2, 2024 at 6:51 PM Keguang Zhang <keguang.zhang@gmail.com> wrote:
> >
> > On Tue, Apr 2, 2024 at 5:04 PM Huacai Chen <chenhuacai@kernel.org> wrote:
> > >
> > > On Tue, Apr 2, 2024 at 9:56 AM Keguang Zhang <keguang.zhang@gmail.com> wrote:
> > > >
> > > > On Mon, Apr 1, 2024 at 9:24 PM Huacai Chen <chenhuacai@kernel.org> wrote:
> > > > >
> > > > > On Mon, Apr 1, 2024 at 7:10 PM Keguang Zhang <keguang.zhang@gmail.com> wrote:
> > > > > >
> > > > > > On Mon, Apr 1, 2024 at 5:06 PM Huacai Chen <chenhuacai@kernel.org> wrote:
> > > > > > >
> > > > > > > On Mon, Apr 1, 2024 at 10:45 AM Keguang Zhang <keguang.zhang@gmail.com> wrote:
> > > > > > > >
> > > > > > > > Hi Huacai,
> > > > > > > >
> > > > > > > > On Sat, Mar 30, 2024 at 9:59 PM Huacai Chen <chenhuacai@kernel.org> wrote:
> > > > > > > > >
> > > > > > > > > Hi, Keguang,
> > > > > > > > >
> > > > > > > > > On Fri, Mar 29, 2024 at 7:28 PM Keguang Zhang via B4 Relay
> > > > > > > > > <devnull+keguang.zhang.gmail.com@kernel.org> wrote:
> > > > > > > > > >
> > > > > > > > > > From: Keguang Zhang <keguang.zhang@gmail.com>
> > > > > > > > > >
> > > > > > > > > > This patch adds APB DMA driver for Loongson-1 SoCs.
> > > > > > > > > >
> > > > > > > > > > Signed-off-by: Keguang Zhang <keguang.zhang@gmail.com>
> > > > > > > > > > ---
> > > > > > > > > > Changes in v7:
> > > > > > > > > > - Change the comptible to 'loongson,ls1*-apbdma'
> > > > > > > > > > - Update Kconfig and Makefile accordingly
> > > > > > > > > > - Rename the file to loongson1-apb-dma.c to keep the consistency
> > > > > > > > > >
> > > > > > > > > > Changes in v6:
> > > > > > > > > > - Implement .device_prep_dma_cyclic for Loongson1 audio driver,
> > > > > > > > > > - as well as .device_pause and .device_resume.
> > > > > > > > > > - Set the limitation LS1X_DMA_MAX_DESC and put all descriptors
> > > > > > > > > > - into one page to save memory
> > > > > > > > > > - Move dma_pool_zalloc() into ls1x_dma_alloc_desc()
> > > > > > > > > > - Drop dma_slave_config structure
> > > > > > > > > > - Use .remove_new instead of .remove
> > > > > > > > > > - Use KBUILD_MODNAME for the driver name
> > > > > > > > > > - Improve the debug information
> > > > > > > > > >
> > > > > > > > > > Changes in v5:
> > > > > > > > > > - Add DT support
> > > > > > > > > > - Use DT data instead of platform data
> > > > > > > > > > - Use chan_id of struct dma_chan instead of own id
> > > > > > > > > > - Use of_dma_xlate_by_chan_id() instead of ls1x_dma_filter()
> > > > > > > > > > - Update the author information to my official name
> > > > > > > > > >
> > > > > > > > > > Changes in v4:
> > > > > > > > > > - Use dma_slave_map to find the proper channel.
> > > > > > > > > > - Explicitly call devm_request_irq() and tasklet_kill().
> > > > > > > > > > - Fix namespace issue.
> > > > > > > > > > - Some minor fixes and cleanups.
> > > > > > > > > >
> > > > > > > > > > Changes in v3:
> > > > > > > > > > - Rename ls1x_dma_filter_fn to ls1x_dma_filter.
> > > > > > > > > >
> > > > > > > > > > Changes in v2:
> > > > > > > > > > - Change the config from 'DMA_LOONGSON1' to 'LOONGSON1_DMA',
> > > > > > > > > > - and rearrange it in alphabetical order in Kconfig and Makefile.
> > > > > > > > > > - Fix comment style.
> > > > > > > > > > ---
> > > > > > > > > > drivers/dma/Kconfig | 9 +
> > > > > > > > > > drivers/dma/Makefile | 1 +
> > > > > > > > > > drivers/dma/loongson1-apb-dma.c | 665 ++++++++++++++++++++++++++++++++++++++++
> > > > > > > > > > 3 files changed, 675 insertions(+)
> > > > > > > > > >
> > > > > > > > > > diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
> > > > > > > > > > index 002a5ec80620..f7b06c4cdf3f 100644
> > > > > > > > > > --- a/drivers/dma/Kconfig
> > > > > > > > > > +++ b/drivers/dma/Kconfig
> > > > > > > > > > @@ -369,6 +369,15 @@ config K3_DMA
> > > > > > > > > > Support the DMA engine for Hisilicon K3 platform
> > > > > > > > > > devices.
> > > > > > > > > >
> > > > > > > > > > +config LOONGSON1_APB_DMA
> > > > > > > > > > + tristate "Loongson1 APB DMA support"
> > > > > > > > > > + depends on MACH_LOONGSON32 || COMPILE_TEST
> > > > > > > > > > + select DMA_ENGINE
> > > > > > > > > > + select DMA_VIRTUAL_CHANNELS
> > > > > > > > > > + help
> > > > > > > > > > + This selects support for the APB DMA controller in Loongson1 SoCs,
> > > > > > > > > > + which is required by Loongson1 NAND and audio support.
> > > > > > > > > Why not rename to LS1X_APB_DMA and put it just before LS2X_APB_DMA
> > > > > > > > > (and also the driver file name)?
> > > > > > > > >
> > > > > > > > So far all Kconfig entries of Loongson-1 drivers are named with the
> > > > > > > > keyword "LOONGSON1".
> > > > > > > > The same is true for these file names.
> > > > > > > > Therefore, I need to keep the consistency.
> > > > > > > But I see LS1X_IRQ in drivers/irqchip/Kconfig
> > > > > > >
> > > > > > Indeed, that's an exception, which was submitted by Jiaxun several years ago.
> > > > > > Actually, most drivers of Loongson family use the keyword "LOONGSON"
> > > > > > for Kconfig and "loongson" for filename.
> > > > > > Thus I take this keywork as the naming convention.
> > > > > But I think keeping consistency in a same subsystem is better than
> > > > > keeping consistency in a same SoC (but cross subsystems).
> > > > >
> > > > In my opinion, "LS*X" is too short and may be confused with other SoCs.
> > > > Meanwhile, there are only four drivers that use this keyword.
> > > > config I2C_LS2X
> > > > config LS2K_RESET
> > > > config LS2X_APB_DMA
> > > > config LS1X_IRQ
> > > > Then, my suggestion is to change these "LS*X" to "LOONGSON*" to get a
> > > > clear meaning.
> > > We have made a naming conversion some years before with Jiaxun.
> > > 1, Use "Loongson" for CPU in arch code;
> > > 2, Use "LS7A" or something like this for bridges and devices.
> > > 3, For drivers in SoC, if the driver is specific to Loongson-1, use
> > > LS1X, if it is to Loongson-2, use LS2X, if it is shared by both
> > > Loongson-1 and Loongson-2, use LOONGSON.
> > >
> > OK. But the doesn't the answer the question of confusion, such as
> > "Freescale LS1021A".
> > The same problem happens to the filenames.
> > ./drivers/gpu/drm/nouveau/nvkm/nvfw/ls.c
> > ./drivers/gpu/drm/nouveau/nvkm/subdev/acr/lsfw.c
> > ./drivers/gpu/drm/amd/amdgpu/lsdma_v6_0.c
> > ./drivers/gpu/drm/amd/amdgpu/lsdma_v7_0.c
> > ./arch/powerpc/platforms/embedded6xx/ls_uart.c
> > Regarding "LS*X" itself, it contains the wildcard character "X" which
> > itself is confusing.
> > Therefore, I don't think "LS*X" is clear enough.
> >
The confusion problem remains.
Honestly, I don't think "LS" is a good short for "LOONGSON".
> > On the other hand, I see "LOONGSON2_*" strings are still there.
This question remains.
> > In addition, some of "LOONGSON_" definitions are not applicable for
> > Loongson-1 at all, which breaks your convention.
> > config SND_SOC_LOONGSON_I2S_PCI /* Loongson-1 doesn't support I2S */
> > config SND_SOC_LOONGSON_CARD
> They are shared by LS2K and LS7A.
>
> > config DWMAC_LOONGSON1
> > config DWMAC_LOONGSON /* This glue layer doesn't support Loongson-1 */
> > config COMMON_CLK_LOONGSON2
unaddressed
> > config RTC_DRV_LOONGSON
> RTC is shared by LS2K and LS7A.
>
> > config SPI_LOONGSON_CORE
> > config SPI_LOONGSON_PCI /* N/A for Loongson-1 */
> > config SPI_LOONGSON_PLATFORM
> SPI is also shared by LS2K and LS7A.
>
> > config LOONGSON2_CPUFREQ
unaddressed
> > config DRM_LOONGSON /* N/A for Loongson-1 */
> DRM is also shared by LS2K and LS7A.
>
> > config LOONGSON1_WDT
> > config CLKSRC_LOONGSON1_PWM
> > config LOONGSON_LIOINTC /* N/A for Loongson-1 */
> > config LOONGSON_EIOINTC /* N/A for Loongson-1 */
> > config LOONGSON_HTPIC /* N/A for Loongson-1 */
> > config LOONGSON_HTVEC /* N/A for Loongson-1 */
> > config LOONGSON_PCH_PIC /* N/A for Loongson-1 */
> > config LOONGSON_PCH_MSI /* N/A for Loongson-1 */
> > config LOONGSON_PCH_LPC /* N/A for Loongson-1 */
> All interrupt controllers are shared by Loongson-2 and Loongson-3.
>
> > config PINCTRL_LOONGSON2
unaddressed
> > config LOONGSON2_THERMAL
ditto
> > config LOONGSON2_GUTS
ditto
> > config LOONGSON2_PM
ditto
> > config LOONGSON_LAPTOP /* N/A for Loongson-1 */
> Laptop driver is shared by Loongson-2 and Loongson-3.
>
> > config GPIO_LOONGSON
> > config GPIO_LOONGSON_64BIT -> N/A for Loongson-1
> > config GPIO_LOONGSON1
> GPIO driver is shared by LS2K and LS7A.
>
> > config PCI_LOONGSON
> PCI driver is shared by Loongson-2 and Loongson-3.
You said "if it is shared by both Loongson-1 and Loongson-2, use LOONGSON."
Now the rule changes from "Loongson-1 and Loongson-2" to "Loongson-2
and Loongson-3".
Then, when shall we use "LOONGSON"?
Here is the situation: only are four drivers use "LS*".
config I2C_LS2X
config LS2K_RESET
config LS2X_APB_DMA
config LS1X_IRQ
My suggestion is to use the intuitive "LOONGSON*" for both CPU and
drivers, which is easy to understand.
And replace the confusing and unclear "LS*X" with "LOONGSON*".
Use "LOONGSON" when the driver/feature is shared with Loongson-1,
Loongson-2 and Loongson-3.
>
> >
> > What's your plan about the above Kconfig entries?
> Yes, there are exceptions indeed, but very rare. And some of the
> exceptions are due to the limited spare time of Jiaxun and me. But in
> this case, it is better to keep consistency in the DMA subsystem.
>
Sorry, I'm not persuaded.
Please consider my proposal.
Thanks!
> Huacai
>
> > Why can't we use LOONGSON1/LOONGSON2 for drivers?
> >
> >
> > > Huacai
> > >
> > > >
> > > > > Huacai
> > > > >
> > > > > >
> > > > > > > Huacai
> > > > > > >
> > > > > > > >
> > > > > > > >
> > > > > > > > > Huacai
> > > > > > > > >
> > > > > > > > > > +
> > > > > > > > > > config LPC18XX_DMAMUX
> > > > > > > > > > bool "NXP LPC18xx/43xx DMA MUX for PL080"
> > > > > > > > > > depends on ARCH_LPC18XX || COMPILE_TEST
> > > > > > > > > > diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
> > > > > > > > > > index dfd40d14e408..b26f6677978a 100644
> > > > > > > > > > --- a/drivers/dma/Makefile
> > > > > > > > > > +++ b/drivers/dma/Makefile
> > > > > > > > > > @@ -47,6 +47,7 @@ obj-$(CONFIG_INTEL_IDMA64) += idma64.o
> > > > > > > > > > obj-$(CONFIG_INTEL_IOATDMA) += ioat/
> > > > > > > > > > obj-y += idxd/
> > > > > > > > > > obj-$(CONFIG_K3_DMA) += k3dma.o
> > > > > > > > > > +obj-$(CONFIG_LOONGSON1_APB_DMA) += loongson1-apb-dma.o
> > > > > > > > > > obj-$(CONFIG_LPC18XX_DMAMUX) += lpc18xx-dmamux.o
> > > > > > > > > > obj-$(CONFIG_LS2X_APB_DMA) += ls2x-apb-dma.o
> > > > > > > > > > obj-$(CONFIG_MILBEAUT_HDMAC) += milbeaut-hdmac.o
> > > > > > > > > > diff --git a/drivers/dma/loongson1-apb-dma.c b/drivers/dma/loongson1-apb-dma.c
> > > > > > > > > > new file mode 100644
> > > > > > > > > > index 000000000000..d474a2601e6e
> > > > > > > > > > --- /dev/null
> > > > > > > > > > +++ b/drivers/dma/loongson1-apb-dma.c
> > > > > > > > > > @@ -0,0 +1,665 @@
> > > > > > > > > > +// SPDX-License-Identifier: GPL-2.0-or-later
> > > > > > > > > > +/*
> > > > > > > > > > + * Driver for Loongson-1 APB DMA Controller
> > > > > > > > > > + *
> > > > > > > > > > + * Copyright (C) 2015-2024 Keguang Zhang <keguang.zhang@gmail.com>
> > > > > > > > > > + */
> > > > > > > > > > +
> > > > > > > > > > +#include <linux/dmapool.h>
> > > > > > > > > > +#include <linux/dma-mapping.h>
> > > > > > > > > > +#include <linux/init.h>
> > > > > > > > > > +#include <linux/interrupt.h>
> > > > > > > > > > +#include <linux/iopoll.h>
> > > > > > > > > > +#include <linux/module.h>
> > > > > > > > > > +#include <linux/of.h>
> > > > > > > > > > +#include <linux/of_dma.h>
> > > > > > > > > > +#include <linux/platform_device.h>
> > > > > > > > > > +#include <linux/slab.h>
> > > > > > > > > > +
> > > > > > > > > > +#include "dmaengine.h"
> > > > > > > > > > +#include "virt-dma.h"
> > > > > > > > > > +
> > > > > > > > > > +/* Loongson-1 DMA Control Register */
> > > > > > > > > > +#define DMA_CTRL 0x0
> > > > > > > > > > +
> > > > > > > > > > +/* DMA Control Register Bits */
> > > > > > > > > > +#define DMA_STOP BIT(4)
> > > > > > > > > > +#define DMA_START BIT(3)
> > > > > > > > > > +#define DMA_ASK_VALID BIT(2)
> > > > > > > > > > +
> > > > > > > > > > +#define DMA_ADDR_MASK GENMASK(31, 6)
> > > > > > > > > > +
> > > > > > > > > > +/* DMA Next Field Bits */
> > > > > > > > > > +#define DMA_NEXT_VALID BIT(0)
> > > > > > > > > > +
> > > > > > > > > > +/* DMA Command Field Bits */
> > > > > > > > > > +#define DMA_RAM2DEV BIT(12)
> > > > > > > > > > +#define DMA_INT BIT(1)
> > > > > > > > > > +#define DMA_INT_MASK BIT(0)
> > > > > > > > > > +
> > > > > > > > > > +#define LS1X_DMA_MAX_CHANNELS 3
> > > > > > > > > > +
> > > > > > > > > > +/* Size of allocations for hardware descriptors */
> > > > > > > > > > +#define LS1X_DMA_DESCS_SIZE PAGE_SIZE
> > > > > > > > > > +#define LS1X_DMA_MAX_DESC \
> > > > > > > > > > + (LS1X_DMA_DESCS_SIZE / sizeof(struct ls1x_dma_hwdesc))
> > > > > > > > > > +
> > > > > > > > > > +struct ls1x_dma_hwdesc {
> > > > > > > > > > + u32 next; /* next descriptor address */
> > > > > > > > > > + u32 saddr; /* memory DMA address */
> > > > > > > > > > + u32 daddr; /* device DMA address */
> > > > > > > > > > + u32 length;
> > > > > > > > > > + u32 stride;
> > > > > > > > > > + u32 cycles;
> > > > > > > > > > + u32 cmd;
> > > > > > > > > > + u32 stats;
> > > > > > > > > > +};
> > > > > > > > > > +
> > > > > > > > > > +struct ls1x_dma_desc {
> > > > > > > > > > + struct virt_dma_desc vdesc;
> > > > > > > > > > + enum dma_transfer_direction dir;
> > > > > > > > > > + enum dma_transaction_type type;
> > > > > > > > > > + unsigned int bus_width;
> > > > > > > > > > +
> > > > > > > > > > + unsigned int nr_descs; /* number of descriptors */
> > > > > > > > > > +
> > > > > > > > > > + struct ls1x_dma_hwdesc *hwdesc;
> > > > > > > > > > + dma_addr_t hwdesc_phys;
> > > > > > > > > > +};
> > > > > > > > > > +
> > > > > > > > > > +struct ls1x_dma_chan {
> > > > > > > > > > + struct virt_dma_chan vchan;
> > > > > > > > > > + struct dma_pool *desc_pool;
> > > > > > > > > > + phys_addr_t src_addr;
> > > > > > > > > > + phys_addr_t dst_addr;
> > > > > > > > > > + enum dma_slave_buswidth src_addr_width;
> > > > > > > > > > + enum dma_slave_buswidth dst_addr_width;
> > > > > > > > > > +
> > > > > > > > > > + void __iomem *reg_base;
> > > > > > > > > > + int irq;
> > > > > > > > > > +
> > > > > > > > > > + struct ls1x_dma_desc *desc;
> > > > > > > > > > +
> > > > > > > > > > + struct ls1x_dma_hwdesc *curr_hwdesc;
> > > > > > > > > > + dma_addr_t curr_hwdesc_phys;
> > > > > > > > > > +};
> > > > > > > > > > +
> > > > > > > > > > +struct ls1x_dma {
> > > > > > > > > > + struct dma_device ddev;
> > > > > > > > > > + void __iomem *reg_base;
> > > > > > > > > > +
> > > > > > > > > > + unsigned int nr_chans;
> > > > > > > > > > + struct ls1x_dma_chan chan[];
> > > > > > > > > > +};
> > > > > > > > > > +
> > > > > > > > > > +#define to_ls1x_dma_chan(dchan) \
> > > > > > > > > > + container_of(dchan, struct ls1x_dma_chan, vchan.chan)
> > > > > > > > > > +
> > > > > > > > > > +#define to_ls1x_dma_desc(vd) \
> > > > > > > > > > + container_of(vd, struct ls1x_dma_desc, vdesc)
> > > > > > > > > > +
> > > > > > > > > > +/* macros for registers read/write */
> > > > > > > > > > +#define chan_readl(chan, off) \
> > > > > > > > > > + readl((chan)->reg_base + (off))
> > > > > > > > > > +
> > > > > > > > > > +#define chan_writel(chan, off, val) \
> > > > > > > > > > + writel((val), (chan)->reg_base + (off))
> > > > > > > > > > +
> > > > > > > > > > +static inline struct device *chan2dev(struct dma_chan *chan)
> > > > > > > > > > +{
> > > > > > > > > > + return &chan->dev->device;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static inline int ls1x_dma_query(struct ls1x_dma_chan *chan,
> > > > > > > > > > + dma_addr_t *hwdesc_phys)
> > > > > > > > > > +{
> > > > > > > > > > + struct dma_chan *dchan = &chan->vchan.chan;
> > > > > > > > > > + int val, ret;
> > > > > > > > > > +
> > > > > > > > > > + val = *hwdesc_phys & DMA_ADDR_MASK;
> > > > > > > > > > + val |= DMA_ASK_VALID;
> > > > > > > > > > + val |= dchan->chan_id;
> > > > > > > > > > + chan_writel(chan, DMA_CTRL, val);
> > > > > > > > > > + ret = readl_poll_timeout_atomic(chan->reg_base + DMA_CTRL, val,
> > > > > > > > > > + !(val & DMA_ASK_VALID), 0, 3000);
> > > > > > > > > > + if (ret)
> > > > > > > > > > + dev_err(chan2dev(dchan), "failed to query DMA\n");
> > > > > > > > > > +
> > > > > > > > > > + return ret;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static inline int ls1x_dma_start(struct ls1x_dma_chan *chan,
> > > > > > > > > > + dma_addr_t *hwdesc_phys)
> > > > > > > > > > +{
> > > > > > > > > > + struct dma_chan *dchan = &chan->vchan.chan;
> > > > > > > > > > + int val, ret;
> > > > > > > > > > +
> > > > > > > > > > + dev_dbg(chan2dev(dchan), "cookie=%d, starting hwdesc=%x\n",
> > > > > > > > > > + dchan->cookie, *hwdesc_phys);
> > > > > > > > > > +
> > > > > > > > > > + val = *hwdesc_phys & DMA_ADDR_MASK;
> > > > > > > > > > + val |= DMA_START;
> > > > > > > > > > + val |= dchan->chan_id;
> > > > > > > > > > + chan_writel(chan, DMA_CTRL, val);
> > > > > > > > > > + ret = readl_poll_timeout(chan->reg_base + DMA_CTRL, val,
> > > > > > > > > > + !(val & DMA_START), 0, 3000);
> > > > > > > > > > + if (ret)
> > > > > > > > > > + dev_err(chan2dev(dchan), "failed to start DMA\n");
> > > > > > > > > > +
> > > > > > > > > > + return ret;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static inline void ls1x_dma_stop(struct ls1x_dma_chan *chan)
> > > > > > > > > > +{
> > > > > > > > > > + chan_writel(chan, DMA_CTRL, chan_readl(chan, DMA_CTRL) | DMA_STOP);
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static void ls1x_dma_free_chan_resources(struct dma_chan *dchan)
> > > > > > > > > > +{
> > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > +
> > > > > > > > > > + dma_free_coherent(chan2dev(dchan), sizeof(struct ls1x_dma_hwdesc),
> > > > > > > > > > + chan->curr_hwdesc, chan->curr_hwdesc_phys);
> > > > > > > > > > + vchan_free_chan_resources(&chan->vchan);
> > > > > > > > > > + dma_pool_destroy(chan->desc_pool);
> > > > > > > > > > + chan->desc_pool = NULL;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static int ls1x_dma_alloc_chan_resources(struct dma_chan *dchan)
> > > > > > > > > > +{
> > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > +
> > > > > > > > > > + chan->desc_pool = dma_pool_create(dma_chan_name(dchan),
> > > > > > > > > > + chan2dev(dchan),
> > > > > > > > > > + sizeof(struct ls1x_dma_hwdesc),
> > > > > > > > > > + __alignof__(struct ls1x_dma_hwdesc),
> > > > > > > > > > + 0);
> > > > > > > > > > + if (!chan->desc_pool)
> > > > > > > > > > + return -ENOMEM;
> > > > > > > > > > +
> > > > > > > > > > + /* allocate memory for querying current HW descriptor */
> > > > > > > > > > + dma_set_coherent_mask(chan2dev(dchan), DMA_BIT_MASK(32));
> > > > > > > > > > + chan->curr_hwdesc = dma_alloc_coherent(chan2dev(dchan),
> > > > > > > > > > + sizeof(struct ls1x_dma_hwdesc),
> > > > > > > > > > + &chan->curr_hwdesc_phys,
> > > > > > > > > > + GFP_KERNEL);
> > > > > > > > > > + if (!chan->curr_hwdesc)
> > > > > > > > > > + return -ENOMEM;
> > > > > > > > > > +
> > > > > > > > > > + return 0;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static void ls1x_dma_free_desc(struct virt_dma_desc *vdesc)
> > > > > > > > > > +{
> > > > > > > > > > + struct ls1x_dma_desc *desc = to_ls1x_dma_desc(vdesc);
> > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(vdesc->tx.chan);
> > > > > > > > > > +
> > > > > > > > > > + dma_pool_free(chan->desc_pool, desc->hwdesc, desc->hwdesc_phys);
> > > > > > > > > > + chan->desc = NULL;
> > > > > > > > > > + kfree(desc);
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static struct ls1x_dma_desc *
> > > > > > > > > > +ls1x_dma_alloc_desc(struct dma_chan *dchan, int sg_len,
> > > > > > > > > > + enum dma_transfer_direction direction,
> > > > > > > > > > + enum dma_transaction_type type)
> > > > > > > > > > +{
> > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > + struct ls1x_dma_desc *desc;
> > > > > > > > > > +
> > > > > > > > > > + if (sg_len > LS1X_DMA_MAX_DESC) {
> > > > > > > > > > + dev_err(chan2dev(dchan), "sg_len %u exceeds limit %lu",
> > > > > > > > > > + sg_len, LS1X_DMA_MAX_DESC);
> > > > > > > > > > + return NULL;
> > > > > > > > > > + }
> > > > > > > > > > +
> > > > > > > > > > + desc = kzalloc(sizeof(*desc), GFP_NOWAIT);
> > > > > > > > > > + if (!desc)
> > > > > > > > > > + return NULL;
> > > > > > > > > > +
> > > > > > > > > > + /* allocate HW descriptors */
> > > > > > > > > > + desc->hwdesc = dma_pool_zalloc(chan->desc_pool, GFP_NOWAIT,
> > > > > > > > > > + &desc->hwdesc_phys);
> > > > > > > > > > + if (!desc->hwdesc) {
> > > > > > > > > > + dev_err(chan2dev(dchan), "failed to alloc HW descriptors\n");
> > > > > > > > > > + ls1x_dma_free_desc(&desc->vdesc);
> > > > > > > > > > + return NULL;
> > > > > > > > > > + }
> > > > > > > > > > +
> > > > > > > > > > + desc->dir = direction;
> > > > > > > > > > + desc->type = type;
> > > > > > > > > > + desc->nr_descs = sg_len;
> > > > > > > > > > +
> > > > > > > > > > + return desc;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static int ls1x_dma_setup_hwdescs(struct dma_chan *dchan,
> > > > > > > > > > + struct ls1x_dma_desc *desc,
> > > > > > > > > > + struct scatterlist *sgl, unsigned int sg_len)
> > > > > > > > > > +{
> > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > + dma_addr_t next_hwdesc_phys = desc->hwdesc_phys;
> > > > > > > > > > +
> > > > > > > > > > + struct scatterlist *sg;
> > > > > > > > > > + unsigned int dev_addr, cmd, i;
> > > > > > > > > > +
> > > > > > > > > > + switch (desc->dir) {
> > > > > > > > > > + case DMA_MEM_TO_DEV:
> > > > > > > > > > + dev_addr = chan->dst_addr;
> > > > > > > > > > + desc->bus_width = chan->dst_addr_width;
> > > > > > > > > > + cmd = DMA_RAM2DEV | DMA_INT;
> > > > > > > > > > + break;
> > > > > > > > > > + case DMA_DEV_TO_MEM:
> > > > > > > > > > + dev_addr = chan->src_addr;
> > > > > > > > > > + desc->bus_width = chan->src_addr_width;
> > > > > > > > > > + cmd = DMA_INT;
> > > > > > > > > > + break;
> > > > > > > > > > + default:
> > > > > > > > > > + dev_err(chan2dev(dchan), "unsupported DMA direction: %s\n",
> > > > > > > > > > + dmaengine_get_direction_text(desc->dir));
> > > > > > > > > > + return -EINVAL;
> > > > > > > > > > + }
> > > > > > > > > > +
> > > > > > > > > > + /* setup HW descriptors */
> > > > > > > > > > + for_each_sg(sgl, sg, sg_len, i) {
> > > > > > > > > > + dma_addr_t buf_addr = sg_dma_address(sg);
> > > > > > > > > > + size_t buf_len = sg_dma_len(sg);
> > > > > > > > > > + struct ls1x_dma_hwdesc *hwdesc = &desc->hwdesc[i];
> > > > > > > > > > +
> > > > > > > > > > + if (!is_dma_copy_aligned(dchan->device, buf_addr, 0, buf_len)) {
> > > > > > > > > > + dev_err(chan2dev(dchan), "buffer is not aligned!\n");
> > > > > > > > > > + return -EINVAL;
> > > > > > > > > > + }
> > > > > > > > > > +
> > > > > > > > > > + hwdesc->saddr = buf_addr;
> > > > > > > > > > + hwdesc->daddr = dev_addr;
> > > > > > > > > > + hwdesc->length = buf_len / desc->bus_width;
> > > > > > > > > > + hwdesc->stride = 0;
> > > > > > > > > > + hwdesc->cycles = 1;
> > > > > > > > > > + hwdesc->cmd = cmd;
> > > > > > > > > > +
> > > > > > > > > > + if (i) {
> > > > > > > > > > + next_hwdesc_phys += sizeof(*hwdesc);
> > > > > > > > > > + desc->hwdesc[i - 1].next = next_hwdesc_phys
> > > > > > > > > > + | DMA_NEXT_VALID;
> > > > > > > > > > + }
> > > > > > > > > > + }
> > > > > > > > > > +
> > > > > > > > > > + if (desc->type == DMA_CYCLIC)
> > > > > > > > > > + desc->hwdesc[i - 1].next = desc->hwdesc_phys | DMA_NEXT_VALID;
> > > > > > > > > > +
> > > > > > > > > > + for_each_sg(sgl, sg, sg_len, i) {
> > > > > > > > > > + struct ls1x_dma_hwdesc *hwdesc = &desc->hwdesc[i];
> > > > > > > > > > +
> > > > > > > > > > + print_hex_dump_debug("HW DESC: ", DUMP_PREFIX_OFFSET, 16, 4,
> > > > > > > > > > + hwdesc, sizeof(*hwdesc), false);
> > > > > > > > > > + }
> > > > > > > > > > +
> > > > > > > > > > + return 0;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static struct dma_async_tx_descriptor *
> > > > > > > > > > +ls1x_dma_prep_slave_sg(struct dma_chan *dchan,
> > > > > > > > > > + struct scatterlist *sgl, unsigned int sg_len,
> > > > > > > > > > + enum dma_transfer_direction direction,
> > > > > > > > > > + unsigned long flags, void *context)
> > > > > > > > > > +{
> > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > + struct ls1x_dma_desc *desc;
> > > > > > > > > > +
> > > > > > > > > > + dev_dbg(chan2dev(dchan), "sg_len=%u flags=0x%lx dir=%s\n",
> > > > > > > > > > + sg_len, flags, dmaengine_get_direction_text(direction));
> > > > > > > > > > +
> > > > > > > > > > + desc = ls1x_dma_alloc_desc(dchan, sg_len, direction, DMA_SLAVE);
> > > > > > > > > > + if (!desc)
> > > > > > > > > > + return NULL;
> > > > > > > > > > +
> > > > > > > > > > + if (ls1x_dma_setup_hwdescs(dchan, desc, sgl, sg_len)) {
> > > > > > > > > > + ls1x_dma_free_desc(&desc->vdesc);
> > > > > > > > > > + return NULL;
> > > > > > > > > > + }
> > > > > > > > > > +
> > > > > > > > > > + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static struct dma_async_tx_descriptor *
> > > > > > > > > > +ls1x_dma_prep_dma_cyclic(struct dma_chan *dchan,
> > > > > > > > > > + dma_addr_t buf_addr, size_t buf_len, size_t period_len,
> > > > > > > > > > + enum dma_transfer_direction direction,
> > > > > > > > > > + unsigned long flags)
> > > > > > > > > > +{
> > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > + struct ls1x_dma_desc *desc;
> > > > > > > > > > + struct scatterlist *sgl;
> > > > > > > > > > + unsigned int sg_len;
> > > > > > > > > > + unsigned int i;
> > > > > > > > > > +
> > > > > > > > > > + dev_dbg(chan2dev(dchan),
> > > > > > > > > > + "buf_len=%d period_len=%zu flags=0x%lx dir=%s\n", buf_len,
> > > > > > > > > > + period_len, flags, dmaengine_get_direction_text(direction));
> > > > > > > > > > +
> > > > > > > > > > + sg_len = buf_len / period_len;
> > > > > > > > > > + desc = ls1x_dma_alloc_desc(dchan, sg_len, direction, DMA_CYCLIC);
> > > > > > > > > > + if (!desc)
> > > > > > > > > > + return NULL;
> > > > > > > > > > +
> > > > > > > > > > + /* allocate the scatterlist */
> > > > > > > > > > + sgl = kmalloc_array(sg_len, sizeof(*sgl), GFP_NOWAIT);
> > > > > > > > > > + if (!sgl)
> > > > > > > > > > + return NULL;
> > > > > > > > > > +
> > > > > > > > > > + sg_init_table(sgl, sg_len);
> > > > > > > > > > + for (i = 0; i < sg_len; ++i) {
> > > > > > > > > > + sg_set_page(&sgl[i], pfn_to_page(PFN_DOWN(buf_addr)),
> > > > > > > > > > + period_len, offset_in_page(buf_addr));
> > > > > > > > > > + sg_dma_address(&sgl[i]) = buf_addr;
> > > > > > > > > > + sg_dma_len(&sgl[i]) = period_len;
> > > > > > > > > > + buf_addr += period_len;
> > > > > > > > > > + }
> > > > > > > > > > +
> > > > > > > > > > + if (ls1x_dma_setup_hwdescs(dchan, desc, sgl, sg_len)) {
> > > > > > > > > > + ls1x_dma_free_desc(&desc->vdesc);
> > > > > > > > > > + return NULL;
> > > > > > > > > > + }
> > > > > > > > > > +
> > > > > > > > > > + kfree(sgl);
> > > > > > > > > > +
> > > > > > > > > > + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static int ls1x_dma_slave_config(struct dma_chan *dchan,
> > > > > > > > > > + struct dma_slave_config *config)
> > > > > > > > > > +{
> > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > +
> > > > > > > > > > + chan->src_addr = config->src_addr;
> > > > > > > > > > + chan->src_addr_width = config->src_addr_width;
> > > > > > > > > > + chan->dst_addr = config->dst_addr;
> > > > > > > > > > + chan->dst_addr_width = config->dst_addr_width;
> > > > > > > > > > +
> > > > > > > > > > + return 0;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static int ls1x_dma_pause(struct dma_chan *dchan)
> > > > > > > > > > +{
> > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > + unsigned long flags;
> > > > > > > > > > + int ret;
> > > > > > > > > > +
> > > > > > > > > > + spin_lock_irqsave(&chan->vchan.lock, flags);
> > > > > > > > > > + ret = ls1x_dma_query(chan, &chan->curr_hwdesc_phys);
> > > > > > > > > > + if (!ret)
> > > > > > > > > > + ls1x_dma_stop(chan);
> > > > > > > > > > + spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > > > > > > > > +
> > > > > > > > > > + return ret;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static int ls1x_dma_resume(struct dma_chan *dchan)
> > > > > > > > > > +{
> > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > + unsigned long flags;
> > > > > > > > > > + int ret;
> > > > > > > > > > +
> > > > > > > > > > + spin_lock_irqsave(&chan->vchan.lock, flags);
> > > > > > > > > > + ret = ls1x_dma_start(chan, &chan->curr_hwdesc_phys);
> > > > > > > > > > + spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > > > > > > > > +
> > > > > > > > > > + return ret;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static int ls1x_dma_terminate_all(struct dma_chan *dchan)
> > > > > > > > > > +{
> > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > + unsigned long flags;
> > > > > > > > > > + LIST_HEAD(head);
> > > > > > > > > > +
> > > > > > > > > > + spin_lock_irqsave(&chan->vchan.lock, flags);
> > > > > > > > > > + ls1x_dma_stop(chan);
> > > > > > > > > > + vchan_get_all_descriptors(&chan->vchan, &head);
> > > > > > > > > > + spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > > > > > > > > +
> > > > > > > > > > + vchan_dma_desc_free_list(&chan->vchan, &head);
> > > > > > > > > > +
> > > > > > > > > > + return 0;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static enum dma_status ls1x_dma_tx_status(struct dma_chan *dchan,
> > > > > > > > > > + dma_cookie_t cookie,
> > > > > > > > > > + struct dma_tx_state *state)
> > > > > > > > > > +{
> > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > + struct virt_dma_desc *vdesc;
> > > > > > > > > > + enum dma_status status;
> > > > > > > > > > + size_t bytes = 0;
> > > > > > > > > > + unsigned long flags;
> > > > > > > > > > +
> > > > > > > > > > + status = dma_cookie_status(dchan, cookie, state);
> > > > > > > > > > + if (status == DMA_COMPLETE)
> > > > > > > > > > + return status;
> > > > > > > > > > +
> > > > > > > > > > + spin_lock_irqsave(&chan->vchan.lock, flags);
> > > > > > > > > > + vdesc = vchan_find_desc(&chan->vchan, cookie);
> > > > > > > > > > + if (chan->desc && cookie == chan->desc->vdesc.tx.cookie) {
> > > > > > > > > > + struct ls1x_dma_desc *desc = chan->desc;
> > > > > > > > > > + int i;
> > > > > > > > > > +
> > > > > > > > > > + if (ls1x_dma_query(chan, &chan->curr_hwdesc_phys))
> > > > > > > > > > + return status;
> > > > > > > > > > +
> > > > > > > > > > + /* locate the current HW descriptor */
> > > > > > > > > > + for (i = 0; i < desc->nr_descs; i++)
> > > > > > > > > > + if (desc->hwdesc[i].next == chan->curr_hwdesc->next)
> > > > > > > > > > + break;
> > > > > > > > > > +
> > > > > > > > > > + /* count the residues */
> > > > > > > > > > + for (; i < desc->nr_descs; i++)
> > > > > > > > > > + bytes += desc->hwdesc[i].length * desc->bus_width;
> > > > > > > > > > +
> > > > > > > > > > + dma_set_residue(state, bytes);
> > > > > > > > > > + }
> > > > > > > > > > + spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > > > > > > > > +
> > > > > > > > > > + return status;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static void ls1x_dma_issue_pending(struct dma_chan *dchan)
> > > > > > > > > > +{
> > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > + struct virt_dma_desc *vdesc;
> > > > > > > > > > + unsigned long flags;
> > > > > > > > > > +
> > > > > > > > > > + spin_lock_irqsave(&chan->vchan.lock, flags);
> > > > > > > > > > + if (vchan_issue_pending(&chan->vchan) && !chan->desc) {
> > > > > > > > > > + vdesc = vchan_next_desc(&chan->vchan);
> > > > > > > > > > + if (!vdesc) {
> > > > > > > > > > + chan->desc = NULL;
> > > > > > > > > > + return;
> > > > > > > > > > + }
> > > > > > > > > > + chan->desc = to_ls1x_dma_desc(vdesc);
> > > > > > > > > > + ls1x_dma_start(chan, &chan->desc->hwdesc_phys);
> > > > > > > > > > + }
> > > > > > > > > > + spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static irqreturn_t ls1x_dma_irq_handler(int irq, void *data)
> > > > > > > > > > +{
> > > > > > > > > > + struct ls1x_dma_chan *chan = data;
> > > > > > > > > > + struct ls1x_dma_desc *desc = chan->desc;
> > > > > > > > > > + struct dma_chan *dchan = &chan->vchan.chan;
> > > > > > > > > > +
> > > > > > > > > > + if (!desc) {
> > > > > > > > > > + dev_warn(chan2dev(dchan),
> > > > > > > > > > + "IRQ %d with no active descriptor on channel %d\n",
> > > > > > > > > > + irq, dchan->chan_id);
> > > > > > > > > > + return IRQ_NONE;
> > > > > > > > > > + }
> > > > > > > > > > +
> > > > > > > > > > + dev_dbg(chan2dev(dchan), "DMA IRQ %d on channel %d\n", irq,
> > > > > > > > > > + dchan->chan_id);
> > > > > > > > > > +
> > > > > > > > > > + spin_lock(&chan->vchan.lock);
> > > > > > > > > > +
> > > > > > > > > > + if (desc->type == DMA_CYCLIC) {
> > > > > > > > > > + vchan_cyclic_callback(&desc->vdesc);
> > > > > > > > > > + } else {
> > > > > > > > > > + list_del(&desc->vdesc.node);
> > > > > > > > > > + vchan_cookie_complete(&desc->vdesc);
> > > > > > > > > > + chan->desc = NULL;
> > > > > > > > > > + }
> > > > > > > > > > +
> > > > > > > > > > + spin_unlock(&chan->vchan.lock);
> > > > > > > > > > + return IRQ_HANDLED;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static int ls1x_dma_chan_probe(struct platform_device *pdev,
> > > > > > > > > > + struct ls1x_dma *dma, int chan_id)
> > > > > > > > > > +{
> > > > > > > > > > + struct device *dev = &pdev->dev;
> > > > > > > > > > + struct ls1x_dma_chan *chan = &dma->chan[chan_id];
> > > > > > > > > > + char pdev_irqname[4];
> > > > > > > > > > + char *irqname;
> > > > > > > > > > + int ret;
> > > > > > > > > > +
> > > > > > > > > > + sprintf(pdev_irqname, "ch%u", chan_id);
> > > > > > > > > > + chan->irq = platform_get_irq_byname(pdev, pdev_irqname);
> > > > > > > > > > + if (chan->irq < 0)
> > > > > > > > > > + return -ENODEV;
> > > > > > > > > > +
> > > > > > > > > > + irqname = devm_kasprintf(dev, GFP_KERNEL, "%s:%s",
> > > > > > > > > > + dev_name(dev), pdev_irqname);
> > > > > > > > > > + if (!irqname)
> > > > > > > > > > + return -ENOMEM;
> > > > > > > > > > +
> > > > > > > > > > + ret = devm_request_irq(dev, chan->irq, ls1x_dma_irq_handler,
> > > > > > > > > > + IRQF_SHARED, irqname, chan);
> > > > > > > > > > + if (ret)
> > > > > > > > > > + return dev_err_probe(dev, ret,
> > > > > > > > > > + "failed to request IRQ %u!\n", chan->irq);
> > > > > > > > > > +
> > > > > > > > > > + chan->reg_base = dma->reg_base;
> > > > > > > > > > + chan->vchan.desc_free = ls1x_dma_free_desc;
> > > > > > > > > > + vchan_init(&chan->vchan, &dma->ddev);
> > > > > > > > > > + dev_info(dev, "%s (irq %d) initialized\n", pdev_irqname, chan->irq);
> > > > > > > > > > +
> > > > > > > > > > + return 0;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static void ls1x_dma_chan_remove(struct ls1x_dma *dma, int chan_id)
> > > > > > > > > > +{
> > > > > > > > > > + struct device *dev = dma->ddev.dev;
> > > > > > > > > > + struct ls1x_dma_chan *chan = &dma->chan[chan_id];
> > > > > > > > > > +
> > > > > > > > > > + devm_free_irq(dev, chan->irq, chan);
> > > > > > > > > > + list_del(&chan->vchan.chan.device_node);
> > > > > > > > > > + tasklet_kill(&chan->vchan.task);
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static int ls1x_dma_probe(struct platform_device *pdev)
> > > > > > > > > > +{
> > > > > > > > > > + struct device *dev = &pdev->dev;
> > > > > > > > > > + struct dma_device *ddev;
> > > > > > > > > > + struct ls1x_dma *dma;
> > > > > > > > > > + int nr_chans, ret, i;
> > > > > > > > > > +
> > > > > > > > > > + nr_chans = platform_irq_count(pdev);
> > > > > > > > > > + if (nr_chans <= 0)
> > > > > > > > > > + return nr_chans;
> > > > > > > > > > + if (nr_chans > LS1X_DMA_MAX_CHANNELS)
> > > > > > > > > > + return dev_err_probe(dev, -EINVAL,
> > > > > > > > > > + "nr_chans=%d exceeds the maximum\n",
> > > > > > > > > > + nr_chans);
> > > > > > > > > > +
> > > > > > > > > > + dma = devm_kzalloc(dev, struct_size(dma, chan, nr_chans), GFP_KERNEL);
> > > > > > > > > > + if (!dma)
> > > > > > > > > > + return -ENOMEM;
> > > > > > > > > > +
> > > > > > > > > > + /* initialize DMA device */
> > > > > > > > > > + dma->reg_base = devm_platform_ioremap_resource(pdev, 0);
> > > > > > > > > > + if (IS_ERR(dma->reg_base))
> > > > > > > > > > + return PTR_ERR(dma->reg_base);
> > > > > > > > > > +
> > > > > > > > > > + ddev = &dma->ddev;
> > > > > > > > > > + ddev->dev = dev;
> > > > > > > > > > + ddev->copy_align = DMAENGINE_ALIGN_4_BYTES;
> > > > > > > > > > + ddev->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> > > > > > > > > > + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
> > > > > > > > > > + ddev->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> > > > > > > > > > + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
> > > > > > > > > > + ddev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
> > > > > > > > > > + ddev->max_sg_burst = LS1X_DMA_MAX_DESC;
> > > > > > > > > > + ddev->residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
> > > > > > > > > > + ddev->device_alloc_chan_resources = ls1x_dma_alloc_chan_resources;
> > > > > > > > > > + ddev->device_free_chan_resources = ls1x_dma_free_chan_resources;
> > > > > > > > > > + ddev->device_prep_slave_sg = ls1x_dma_prep_slave_sg;
> > > > > > > > > > + ddev->device_prep_dma_cyclic = ls1x_dma_prep_dma_cyclic;
> > > > > > > > > > + ddev->device_config = ls1x_dma_slave_config;
> > > > > > > > > > + ddev->device_pause = ls1x_dma_pause;
> > > > > > > > > > + ddev->device_resume = ls1x_dma_resume;
> > > > > > > > > > + ddev->device_terminate_all = ls1x_dma_terminate_all;
> > > > > > > > > > + ddev->device_tx_status = ls1x_dma_tx_status;
> > > > > > > > > > + ddev->device_issue_pending = ls1x_dma_issue_pending;
> > > > > > > > > > +
> > > > > > > > > > + dma_cap_set(DMA_SLAVE, ddev->cap_mask);
> > > > > > > > > > + INIT_LIST_HEAD(&ddev->channels);
> > > > > > > > > > +
> > > > > > > > > > + /* initialize DMA channels */
> > > > > > > > > > + for (i = 0; i < nr_chans; i++) {
> > > > > > > > > > + ret = ls1x_dma_chan_probe(pdev, dma, i);
> > > > > > > > > > + if (ret)
> > > > > > > > > > + return ret;
> > > > > > > > > > + }
> > > > > > > > > > + dma->nr_chans = nr_chans;
> > > > > > > > > > +
> > > > > > > > > > + ret = dmaenginem_async_device_register(ddev);
> > > > > > > > > > + if (ret) {
> > > > > > > > > > + dev_err(dev, "failed to register DMA device! %d\n", ret);
> > > > > > > > > > + return ret;
> > > > > > > > > > + }
> > > > > > > > > > +
> > > > > > > > > > + ret =
> > > > > > > > > > + of_dma_controller_register(dev->of_node, of_dma_xlate_by_chan_id,
> > > > > > > > > > + ddev);
> > > > > > > > > > + if (ret) {
> > > > > > > > > > + dev_err(dev, "failed to register DMA controller! %d\n", ret);
> > > > > > > > > > + return ret;
> > > > > > > > > > + }
> > > > > > > > > > +
> > > > > > > > > > + platform_set_drvdata(pdev, dma);
> > > > > > > > > > + dev_info(dev, "Loongson1 DMA driver registered\n");
> > > > > > > > > > +
> > > > > > > > > > + return 0;
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static void ls1x_dma_remove(struct platform_device *pdev)
> > > > > > > > > > +{
> > > > > > > > > > + struct ls1x_dma *dma = platform_get_drvdata(pdev);
> > > > > > > > > > + int i;
> > > > > > > > > > +
> > > > > > > > > > + of_dma_controller_free(pdev->dev.of_node);
> > > > > > > > > > +
> > > > > > > > > > + for (i = 0; i < dma->nr_chans; i++)
> > > > > > > > > > + ls1x_dma_chan_remove(dma, i);
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static const struct of_device_id ls1x_dma_match[] = {
> > > > > > > > > > + { .compatible = "loongson,ls1b-apbdma" },
> > > > > > > > > > + { .compatible = "loongson,ls1c-apbdma" },
> > > > > > > > > > + { /* sentinel */ }
> > > > > > > > > > +};
> > > > > > > > > > +MODULE_DEVICE_TABLE(of, ls1x_dma_match);
> > > > > > > > > > +
> > > > > > > > > > +static struct platform_driver ls1x_dma_driver = {
> > > > > > > > > > + .probe = ls1x_dma_probe,
> > > > > > > > > > + .remove_new = ls1x_dma_remove,
> > > > > > > > > > + .driver = {
> > > > > > > > > > + .name = KBUILD_MODNAME,
> > > > > > > > > > + .of_match_table = ls1x_dma_match,
> > > > > > > > > > + },
> > > > > > > > > > +};
> > > > > > > > > > +
> > > > > > > > > > +module_platform_driver(ls1x_dma_driver);
> > > > > > > > > > +
> > > > > > > > > > +MODULE_AUTHOR("Keguang Zhang <keguang.zhang@gmail.com>");
> > > > > > > > > > +MODULE_DESCRIPTION("Loongson-1 APB DMA Controller driver");
> > > > > > > > > > +MODULE_LICENSE("GPL");
> > > > > > > > > >
> > > > > > > > > > --
> > > > > > > > > > 2.40.1
> > > > > > > > > >
> > > > > > > > > >
> > > > > > > >
> > > > > > > >
> > > > > > > >
> > > > > > > > --
> > > > > > > > Best regards,
> > > > > > > >
> > > > > > > > Keguang Zhang
> > > > > > > >
> > > > > >
> > > > > >
> > > > > >
> > > > > > --
> > > > > > Best regards,
> > > > > >
> > > > > > Keguang Zhang
> > > >
> > > >
> > > >
> > > > --
> > > > Best regards,
> > > >
> > > > Keguang Zhang
> >
> >
> >
> > --
> > Best regards,
> >
> > Keguang Zhang
--
Best regards,
Keguang Zhang
^ permalink raw reply
* [PATCH v2] dt-bindings: watchdog: aspeed,ast2400-wdt: Convert to DT schema
From: Andrew Jeffery @ 2024-04-03 2:04 UTC (permalink / raw)
To: wim, linux
Cc: Andrew Jeffery, robh, krzysztof.kozlowski+dt, conor+dt, joel, zev,
linux-watchdog, devicetree, linux-arm-kernel, linux-aspeed,
linux-kernel
Squash warnings such as:
```
arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-galaxy100.dtb: /ahb/apb@1e600000/watchdog@1e785000: failed to match any schema with compatible: ['aspeed,ast2400-wdt']
```
The schema binding additionally defines the clocks property over the
prose binding to align with use of the node in the DTS files.
Signed-off-by: Andrew Jeffery <andrew@codeconstruct.com.au>
---
v2: Address feedback from Rob and Zev
- Rob: https://lore.kernel.org/linux-watchdog/20240402180718.GA358505-robh@kernel.org/
- Zev: https://lore.kernel.org/linux-watchdog/65722a59-2e94-4616-81e1-835615b0e600@hatter.bewilderbeest.net/
v1: https://lore.kernel.org/linux-watchdog/20240402120118.282035-1-andrew@codeconstruct.com.au/
.../bindings/watchdog/aspeed,ast2400-wdt.yaml | 142 ++++++++++++++++++
.../bindings/watchdog/aspeed-wdt.txt | 73 ---------
2 files changed, 142 insertions(+), 73 deletions(-)
create mode 100644 Documentation/devicetree/bindings/watchdog/aspeed,ast2400-wdt.yaml
delete mode 100644 Documentation/devicetree/bindings/watchdog/aspeed-wdt.txt
diff --git a/Documentation/devicetree/bindings/watchdog/aspeed,ast2400-wdt.yaml b/Documentation/devicetree/bindings/watchdog/aspeed,ast2400-wdt.yaml
new file mode 100644
index 000000000000..be78a9865584
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/aspeed,ast2400-wdt.yaml
@@ -0,0 +1,142 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/watchdog/aspeed,ast2400-wdt.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Aspeed watchdog timer controllers
+
+maintainers:
+ - Andrew Jeffery <andrew@codeconstruct.com.au>
+
+properties:
+ compatible:
+ enum:
+ - aspeed,ast2400-wdt
+ - aspeed,ast2500-wdt
+ - aspeed,ast2600-wdt
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+ description: >
+ The clock used to drive the watchdog counter. From the AST2500 no source
+ other than the 1MHz clock can be selected, so the clocks property is
+ optional.
+
+ aspeed,reset-type:
+ $ref: /schemas/types.yaml#/definitions/string
+ enum:
+ - cpu
+ - soc
+ - system
+ - none
+ default: system
+ description: >
+ The watchdog can be programmed to generate one of three different types of
+ reset when a timeout occcurs.
+
+ Specifying 'cpu' will only reset the processor on a timeout event.
+
+ Specifying 'soc' will reset a configurable subset of the SoC's controllers
+ on a timeout event. Controllers critical to the SoC's operation may remain
+ untouched. The set of SoC controllers to reset may be specified via the
+ aspeed,reset-mask property if the node has the aspeed,ast2500-wdt or
+ aspeed,ast2600-wdt compatible.
+
+ Specifying 'system' will reset all controllers on a timeout event, as if
+ EXTRST had been asserted.
+
+ Specifying 'none' will cause the timeout event to have no reset effect.
+ Another watchdog engine on the chip must be used for chip reset operations.
+
+ aspeed,alt-boot:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: >
+ Direct the watchdog to configure the SoC to boot from the alternative boot
+ region if a timeout occurs.
+
+ aspeed,external-signal:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: >
+ Assert the timeout event on an external signal pin associated with the
+ watchdog controller instance. The pin must be muxed appropriately.
+
+ aspeed,ext-pulse-duration:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: >
+ The duration, in microseconds, of the pulse emitted on the external signal
+ pin.
+
+ aspeed,ext-push-pull:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: >
+ If aspeed,external-signal is specified in the node, set the external
+ signal pin's drive type to push-pull. If aspeed,ext-push-pull is not
+ specified then the pin is configured as open-drain.
+
+ aspeed,ext-active-high:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: >
+ If both aspeed,external-signal and aspeed,ext-push-pull are specified in
+ the node, set the pulse polarity to active-high. If aspeed,ext-active-high
+ is not specified then the pin is configured as active-low.
+
+ aspeed,reset-mask:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 1
+ maxItems: 2
+ description: >
+ A bitmask indicating which peripherals will be reset if the watchdog
+ timer expires. On AST2500 SoCs this should be a single word defined using
+ the AST2500_WDT_RESET_* macros; on AST2600 SoCs this should be a two-word
+ array with the first word defined using the AST2600_WDT_RESET1_* macros,
+ and the second word defined using the AST2600_WDT_RESET2_* macros.
+
+required:
+ - compatible
+ - reg
+
+allOf:
+ - if:
+ anyOf:
+ - required:
+ - aspeed,ext-push-pull
+ - required:
+ - aspeed,ext-active-high
+ - required:
+ - aspeed,reset-mask
+ then:
+ properties:
+ compatible:
+ enum:
+ - aspeed,ast2500-wdt
+ - aspeed,ast2600-wdt
+ - if:
+ required:
+ - aspeed,ext-active-high
+ then:
+ required:
+ - aspeed,ext-push-pull
+
+additionalProperties: false
+
+examples:
+ - |
+ watchdog@1e785000 {
+ compatible = "aspeed,ast2400-wdt";
+ reg = <0x1e785000 0x1c>;
+ aspeed,reset-type = "system";
+ aspeed,external-signal;
+ };
+ - |
+ #include <dt-bindings/watchdog/aspeed-wdt.h>
+ watchdog@1e785040 {
+ compatible = "aspeed,ast2600-wdt";
+ reg = <0x1e785040 0x40>;
+ aspeed,reset-type = "soc";
+ aspeed,reset-mask = <AST2600_WDT_RESET1_DEFAULT
+ (AST2600_WDT_RESET2_DEFAULT & ~AST2600_WDT_RESET2_LPC)>;
+ };
diff --git a/Documentation/devicetree/bindings/watchdog/aspeed-wdt.txt b/Documentation/devicetree/bindings/watchdog/aspeed-wdt.txt
deleted file mode 100644
index 3208adb3e52e..000000000000
--- a/Documentation/devicetree/bindings/watchdog/aspeed-wdt.txt
+++ /dev/null
@@ -1,73 +0,0 @@
-Aspeed Watchdog Timer
-
-Required properties:
- - compatible: must be one of:
- - "aspeed,ast2400-wdt"
- - "aspeed,ast2500-wdt"
- - "aspeed,ast2600-wdt"
-
- - reg: physical base address of the controller and length of memory mapped
- region
-
-Optional properties:
-
- - aspeed,reset-type = "cpu|soc|system|none"
-
- Reset behavior - Whenever a timeout occurs the watchdog can be programmed
- to generate one of three different, mutually exclusive, types of resets.
-
- Type "none" can be specified to indicate that no resets are to be done.
- This is useful in situations where another watchdog engine on chip is
- to perform the reset.
-
- If 'aspeed,reset-type=' is not specified the default is to enable system
- reset.
-
- Reset types:
-
- - cpu: Reset CPU on watchdog timeout
-
- - soc: Reset 'System on Chip' on watchdog timeout
-
- - system: Reset system on watchdog timeout
-
- - none: No reset is performed on timeout. Assumes another watchdog
- engine is responsible for this.
-
- - aspeed,alt-boot: If property is present then boot from alternate block.
- - aspeed,external-signal: If property is present then signal is sent to
- external reset counter (only WDT1 and WDT2). If not
- specified no external signal is sent.
- - aspeed,ext-pulse-duration: External signal pulse duration in microseconds
-
-Optional properties for AST2500-compatible watchdogs:
- - aspeed,ext-push-pull: If aspeed,external-signal is present, set the pin's
- drive type to push-pull. The default is open-drain.
- - aspeed,ext-active-high: If aspeed,external-signal is present and and the pin
- is configured as push-pull, then set the pulse
- polarity to active-high. The default is active-low.
-
-Optional properties for AST2500- and AST2600-compatible watchdogs:
- - aspeed,reset-mask: A bitmask indicating which peripherals will be reset if
- the watchdog timer expires. On AST2500 this should be a
- single word defined using the AST2500_WDT_RESET_* macros;
- on AST2600 this should be a two-word array with the first
- word defined using the AST2600_WDT_RESET1_* macros and the
- second word defined using the AST2600_WDT_RESET2_* macros.
-
-Examples:
-
- wdt1: watchdog@1e785000 {
- compatible = "aspeed,ast2400-wdt";
- reg = <0x1e785000 0x1c>;
- aspeed,reset-type = "system";
- aspeed,external-signal;
- };
-
- #include <dt-bindings/watchdog/aspeed-wdt.h>
- wdt2: watchdog@1e785040 {
- compatible = "aspeed,ast2600-wdt";
- reg = <0x1e785040 0x40>;
- aspeed,reset-mask = <AST2600_WDT_RESET1_DEFAULT
- (AST2600_WDT_RESET2_DEFAULT & ~AST2600_WDT_RESET2_LPC)>;
- };
--
2.39.2
^ permalink raw reply related
* Re: [PATCH] arch: arm: mxc: phyCARD-i.MX27: Add USB support
From: Shawn Guo @ 2024-04-03 1:50 UTC (permalink / raw)
To: Michael Grzeschik
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, devicetree,
imx, linux-arm-kernel, linux-kernel
In-Reply-To: <20240328-pca100-v1-1-58df67c2c950@pengutronix.de>
On Tue, Apr 02, 2024 at 09:35:33AM +0200, Michael Grzeschik wrote:
> This patch adds the pinmux and nodes for usbotg and usbh2.
>
> In v6 revision of the pca100 the usb phys were changed to usb3320 which
> are connected by their reset pins. We add the phy configuration to the
> description.
>
> Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
"ARM: dts: ..." for subject prefix.
Shawn
^ permalink raw reply
* Re: [PATCH v5 0/4] arm64: dts: imx8: add cm40 and cm40_uart
From: Shawn Guo @ 2024-04-03 1:45 UTC (permalink / raw)
To: Frank Li
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, devicetree,
imx, linux-arm-kernel, linux-kernel, Dong Aisheng, Peng Fan,
Alexander Stein, Alice Guo
In-Reply-To: <20240402-m4_lpuart-v5-0-3292629ba808@nxp.com>
On Tue, Apr 02, 2024 at 10:41:27AM -0400, Frank Li wrote:
> Alice Guo (1):
> arm64: dts: imx8dxl: add lpuart device in cm40 subsystem
>
> Dong Aisheng (1):
> arm64: dts: imx8: add cm40 subsystem dtsi
>
> Frank Li (2):
> arm64: dts: imx8dxl: update cm40 irq number information
> dts: arm64: imx8dxl-evk: add lpuart1 and cm40 uart
arm64: dts: imx8dxl-evk: ...
Fixed it up and applied the series, thanks!
Shawn
^ permalink raw reply
* Re: [PATCH 1/7] arm64: dts: imx8-ss-lsio: fix pwm lpcg indices
From: Shawn Guo @ 2024-04-03 1:41 UTC (permalink / raw)
To: Frank Li
Cc: Fabio Estevam, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Shawn Guo, Sascha Hauer, Pengutronix Kernel Team, NXP Linux Team,
Marcel Ziswiler, Philippe Schenker, Max Krummenacher,
Alexander Stein, Joakim Zhang, devicetree, linux-arm-kernel,
linux-kernel, stable
In-Reply-To: <ZgwfnZJDRYmYy7Qt@lizhi-Precision-Tower-5810>
On Tue, Apr 02, 2024 at 11:09:17AM -0400, Frank Li wrote:
> On Mon, Apr 01, 2024 at 08:04:56PM -0300, Fabio Estevam wrote:
> > On Mon, Apr 1, 2024 at 7:25 PM Frank Li <Frank.Li@nxp.com> wrote:
> > >
> > > lpcg's arg0 should use clock indices instead of index.
> > >
> > > pwm0_lpcg: clock-controller@5d400000 {
> > > ... // Col1 Col2
> > > clocks = <&clk IMX_SC_R_PWM_0 IMX_SC_PM_CLK_PER>, // 0 0
> > > <&clk IMX_SC_R_PWM_0 IMX_SC_PM_CLK_PER>, // 1 1
> > > <&clk IMX_SC_R_PWM_0 IMX_SC_PM_CLK_PER>, // 2 4
> > > <&lsio_bus_clk>, // 3 5
> > > <&clk IMX_SC_R_PWM_0 IMX_SC_PM_CLK_PER>; // 4 6
> > > clock-indices = <IMX_LPCG_CLK_0>, <IMX_LPCG_CLK_1>,
> > > <IMX_LPCG_CLK_4>, <IMX_LPCG_CLK_5>,
> > > <IMX_LPCG_CLK_6>;
> > > };
> > >
> > > Col1: index, which exited dts try to get.
> >
> > I cannot understand this sentence, sorry.
>
> This base on downstream dts code. Downstream code use index in 'Col1' to
> get clock.
So s/exited/existing you meant?
Shawn
^ permalink raw reply
* Re: [net-next,v2] dt-bindings: net: renesas,ethertsn: Create child-node for MDIO bus
From: patchwork-bot+netdevbpf @ 2024-04-03 1:40 UTC (permalink / raw)
To: =?utf-8?q?Niklas_S=C3=B6derlund_=3Cniklas=2Esoderlund+renesas=40ragnatech=2E?=,
=?utf-8?q?se=3E?=
Cc: s.shtylyov, davem, edumazet, kuba, pabeni, robh+dt,
krzysztof.kozlowski+dt, conor+dt, geert+renesas, netdev,
devicetree, linux-renesas-soc, robh
In-Reply-To: <20240330131228.1541227-1-niklas.soderlund+renesas@ragnatech.se>
Hello:
This patch was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Sat, 30 Mar 2024 14:12:28 +0100 you wrote:
> The bindings for Renesas Ethernet TSN was just merged in v6.9 and the
> design for the bindings followed that of other Renesas Ethernet drivers
> and thus did not force a child-node for the MDIO bus. As there
> are no upstream drivers or users of this binding yet take the
> opportunity to correct this and force the usage of a child-node for the
> MDIO bus.
>
> [...]
Here is the summary with links:
- [net-next,v2] dt-bindings: net: renesas,ethertsn: Create child-node for MDIO bus
https://git.kernel.org/netdev/net-next/c/8da891720cd4
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox