diff --git a/arch/riscv/boot/dts/canaan/Makefile b/arch/riscv/boot/dts/canaan/Makefile index f7a42971808165..6ceab2ebf4dbce 100644 --- a/arch/riscv/boot/dts/canaan/Makefile +++ b/arch/riscv/boot/dts/canaan/Makefile @@ -9,5 +9,6 @@ dtb-$(CONFIG_ARCH_CANAAN) += k230_evb.dtb dtb-$(CONFIG_ARCH_CANAAN) += k230_evb_nand.dtb dtb-$(CONFIG_ARCH_CANAAN) += k230_evb_doorlock.dtb dtb-$(CONFIG_ARCH_CANAAN) += k230_canmv.dtb +dtb-$(CONFIG_ARCH_CANAAN) += k230_canmv_01studio.dtb obj-$(CONFIG_ARCH_CANAAN_K210_DTB_BUILTIN) += $(addsuffix .dtb.o, $(CONFIG_ARCH_CANAAN_K210_DTB_SOURCE)) diff --git a/arch/riscv/boot/dts/canaan/k230.dtsi b/arch/riscv/boot/dts/canaan/k230.dtsi index 6143f5f96fa56a..6d198776d8067f 100755 --- a/arch/riscv/boot/dts/canaan/k230.dtsi +++ b/arch/riscv/boot/dts/canaan/k230.dtsi @@ -7,6 +7,8 @@ /dts-v1/; #include +#include +#include / { model = "Canaan Kendryte K230"; @@ -519,6 +521,7 @@ display: display-subsystem { compatible = "canaan,display-subsystem"; ports = <&vo_out>; + power-domains = <&sysctl_power K230_PM_DOMAIN_DISP>; status = "okay"; }; @@ -532,14 +535,26 @@ vo: vo@90840000 { compatible = "canaan,k230-vo"; - reg = <0x0 0x90840000 0x0 0x10000 >; + reg = <0x0 0x90840000 0x0 0x10000>; interrupt-parent = <&plic>; interrupts = <133 IRQ_TYPE_LEVEL_HIGH>; + resets = <&sysctl_reset + K230_RESET_DISP_REG_OFFSET + K230_RESET_DISP_TYPE + K230_RESET_DISP_DONE_BIT + K230_RESET_DISP_ASSERT_BIT>; status = "okay"; + background = <0xffffff>; + vth_line = <10>; + vo_out: port { #address-cells = <1>; #size-cells = <0>; + vop_out_dsi: endpoint@0 { + reg = <0>; + remote-endpoint = <&dsi_in_vop>; + }; }; }; @@ -550,27 +565,17 @@ #size-cells = <0>; status = "okay"; - panel@0 { - compatible = "canaan,hx8399"; - reg = <0>; - panel-width-mm = <68>; - panel-height-mm = <120>; - - display-timings { - timing-0 { - clock-frequency = <74437200>; - hactive = <1080>; - vactive = <1920>; - hfront-porch = <40>; - hback-porch = <20>; - hsync-len = <20>; - vfront-porch = <206>; - vback-porch = <8>; - vsync-len = <5>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dsi_in_vop: endpoint { + remote-endpoint = <&vop_out_dsi>; }; }; }; }; - }; }; diff --git a/arch/riscv/boot/dts/canaan/k230_canmv.dts b/arch/riscv/boot/dts/canaan/k230_canmv.dts index c261c5838e550c..daa457a31eae5a 100644 --- a/arch/riscv/boot/dts/canaan/k230_canmv.dts +++ b/arch/riscv/boot/dts/canaan/k230_canmv.dts @@ -14,7 +14,6 @@ #include "gpio_provider.dtsi" /* k230 soc gpio provider */ #include "gpio_consumer.dtsi" /* k230 soc gpio consumer */ - / { aliases { serial0 = &uart0; @@ -26,6 +25,18 @@ linux,initrd-end = <0x0 0x0>; stdout-path = "serial0:115200"; }; + + hdmi: connector { + compatible = "hdmi-connector"; + label = "hdmi"; + type = "a"; + + port { + hdmi_connector_in: endpoint { + remote-endpoint = <<9611_out>; + }; + }; + }; }; &ddr { reg = <0x0 0x200000 0x0 0x1fdff000>; /*linux memory config*/ @@ -101,6 +112,14 @@ status = "okay"; }; +&gpio42 { + status = "okay"; +}; + +&gpio43 { + status = "okay"; +}; + &mmc_sd0{ status = "okay"; io_fixed_1v8; @@ -119,29 +138,56 @@ mshc_ctrl_r = <0x00>; }; -&dsi { - status = "okay"; +&uart0 { + status = "okay"; +}; - panel@0 { - compatible = "canaan,hx8399"; - reg = <0>; - - display-timings { - timing-0 { - clock-frequency = <74250000>; - hactive = <1920>; - vactive = <1080>; - hfront-porch = <88>; - hback-porch = <148>; - hsync-len = <44>; - vfront-porch = <36>; - vback-porch = <4>; - vsync-len = <5>; - }; - }; - }; +&i2c3 { + status = "okay"; }; -&uart0 { +&i2c4 { + status = "okay"; + + lt9611: hdmi-bridge@3b { + compatible = "lontium,lt9611"; + reg = <0x3b>; + reset-gpios = <&port42 0 GPIO_ACTIVE_HIGH>; + interrupt-parent = <&gpio43>; + interrupts = <0 IRQ_TYPE_EDGE_FALLING>; + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + lt9611_in: endpoint { + remote-endpoint = <&dsi_out_lt9611>; + }; + }; + + port@2 { + reg = <2>; + lt9611_out: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; + }; + }; + }; +}; + +&dsi { status = "okay"; + + ports { + port@1 { + reg = <1>; + dsi_out_lt9611: endpoint { + remote-endpoint = <<9611_in>; + }; + }; + }; +}; + +&vo { + vth_line = <10>; }; diff --git a/arch/riscv/boot/dts/canaan/k230_canmv_01studio.dts b/arch/riscv/boot/dts/canaan/k230_canmv_01studio.dts new file mode 100644 index 00000000000000..811b69e1957509 --- /dev/null +++ b/arch/riscv/boot/dts/canaan/k230_canmv_01studio.dts @@ -0,0 +1,162 @@ +/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ +#include "k230.dtsi" +#include "clock_provider.dtsi" /* k230 soc clock tree */ +#include "clock_consumer.dtsi" /* k230 soc clock consumer */ +#include "reset_provider.dtsi" /* k230 soc reset provider */ +#include "reset_consumer.dtsi" /* k230 soc reset consumer */ +#include "power_provider.dtsi" /* k230 soc power provider */ +#include "power_consumer.dtsi" /* k230 soc power consumer */ +#include "gpio_provider.dtsi" /* k230 soc gpio provider */ +#include "gpio_consumer.dtsi" /* k230 soc gpio consumer */ + +/ { + aliases { + serial0 = &uart0; + }; + + chosen { + bootargs = "earlycon=sbi console=ttyS0,115200 loglevel=8 root=/dev/mmcblk1p2 rw rootwait audit=0"; + linux,initrd-start = <0x0 0xa100000>; + linux,initrd-end = <0x0 0x0>; + stdout-path = "serial0:115200"; + }; + + hdmi: connector { + compatible = "hdmi-connector"; + label = "hdmi"; + type = "a"; + + port { + hdmi_connector_in: endpoint { + remote-endpoint = <<9611_out>; + }; + }; + }; +}; + +&ddr { + reg = <0x0 0x200000 0x0 0x3fdff000>; /* 1GB RAM (minus reserved) */ +}; + +&spi0 { + status = "okay"; + spi-flash@0 { + compatible = "jedec,spi-nor"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + spi-max-frequency = <100000000>; + spi-tx-bus-width = <8>; + spi-rx-bus-width = <8>; + status = "okay"; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + /* spl boot */ + reg = <0x0 0x00080000>; + label = "spl_boot"; + }; + partition@2000000 { + /* 32MB for update image*/ + reg = <0 0x2000000>; + label = "all_flash"; + }; + }; + }; + +}; + +&usbotg0 { + dr_mode = "host"; + status = "okay"; +}; + +&usbotg1 { + dr_mode = "host"; + status = "okay"; +}; + +&gpio22 { + status = "okay"; +}; + +&gpio23 { + status = "okay"; +}; + +&mmc_sd0{ + status = "okay"; + no-1-8-v; + cap-sd-highspeed; + rx_delay_line = <0x0d>; + tx_delay_line = <0xb0>; +}; + +&mmc_sd1{ + status = "okay"; + no-1-8-v; + cap-sd-highspeed; + mshc_ctrl_r = <0x00>; +}; + +&uart0 { + status = "okay"; +}; + +&i2c3 { + status = "okay"; + + lt9611: hdmi-bridge@3b { + compatible = "lontium,lt9611"; + reg = <0x3b>; + reset-gpios = <&port22 0 GPIO_ACTIVE_HIGH>; + interrupt-parent = <&gpio23>; + interrupts = <0 IRQ_TYPE_EDGE_FALLING>; + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + lt9611_in: endpoint { + remote-endpoint = <&dsi_out_lt9611>; + }; + }; + + port@2 { + reg = <2>; + lt9611_out: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; + }; + }; + }; +}; + +&i2c4 { + status = "okay"; +}; + +&dsi { + status = "okay"; + + ports { + port@1 { + reg = <1>; + dsi_out_lt9611: endpoint { + remote-endpoint = <<9611_in>; + }; + }; + }; +}; + +&vo { + vth_line = <10>; +}; diff --git a/arch/riscv/configs/k230_evb_linux_enable_vector_defconfig b/arch/riscv/configs/k230_evb_linux_enable_vector_defconfig index 25ea252b7aad0d..9d22ce271c20ef 100644 --- a/arch/riscv/configs/k230_evb_linux_enable_vector_defconfig +++ b/arch/riscv/configs/k230_evb_linux_enable_vector_defconfig @@ -47,6 +47,9 @@ CONFIG_SUN_PARTITION=y CONFIG_KARMA_PARTITION=y CONFIG_SYSV68_PARTITION=y CONFIG_CMDLINE_PARTITION=y +CONFIG_CMA=y +CONFIG_DMA_CMA=y +CONFIG_CMA_SIZE_MBYTES=128 CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y @@ -138,6 +141,7 @@ CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_VIRTIO=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_MUX=y +CONFIG_I2C_DESIGNWARE_CORE=y CONFIG_I2C_DESIGNWARE_PLATFORM=y CONFIG_I2C_DESIGNWARE_ICE=y CONFIG_SPI=y @@ -314,15 +318,19 @@ CONFIG_MEDIA_SUPPORT=y # CONFIG_DVB_CXD2099 is not set # CONFIG_DVB_SP2 is not set CONFIG_DRM=y +CONFIG_DRM_FBDEV_EMULATION=y +CONFIG_DRM_DISPLAY_CONNECTOR=y +CONFIG_DRM_LONTIUM_LT9611=y CONFIG_DRM_PANEL_CANAAN_UNIVERSAL=y CONFIG_DRM_CANAAN=y CONFIG_DRM_CANAAN_DSI=y -CONFIG_VG_LITE=m CONFIG_FB_VIRTUAL=m CONFIG_FB_SIMPLE=y CONFIG_LCD_CLASS_DEVICE=y CONFIG_BACKLIGHT_CLASS_DEVICE=y # CONFIG_VGA_CONSOLE is not set +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_LOGO=y CONFIG_USB=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_STORAGE=y @@ -413,3 +421,4 @@ CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=60 CONFIG_DEBUG_ATOMIC_SLEEP=y # CONFIG_RCU_TRACE is not set CONFIG_VECTOR=y +CONFIG_FB_DEVICE=y diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index cf5bc77e2362c4..4926b1f4d0cda0 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -349,6 +349,8 @@ source "drivers/gpu/drm/i2c/Kconfig" source "drivers/gpu/drm/arm/Kconfig" +source "drivers/gpu/drm/canaan/Kconfig" + source "drivers/gpu/drm/radeon/Kconfig" source "drivers/gpu/drm/amd/amdgpu/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 784229d4504dcb..b5d24ddf6046f0 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -156,6 +156,7 @@ obj-y += tests/ obj-$(CONFIG_DRM_MIPI_DBI) += drm_mipi_dbi.o obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o obj-y += arm/ +obj-$(CONFIG_DRM_CANAAN) += canaan/ obj-y += display/ obj-$(CONFIG_DRM_TTM) += ttm/ obj-$(CONFIG_DRM_SCHED) += scheduler/ diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c index 73983f9b50cbf1..d1bb847b454a1c 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611.c @@ -60,6 +60,7 @@ struct lt9611 { enum drm_connector_status status; u8 edid_buf[EDID_SEG_SIZE]; + u32 pcr_m; }; #define LT9611_PAGE_CONTROL 0xff @@ -93,10 +94,14 @@ static struct lt9611 *bridge_to_lt9611(struct drm_bridge *bridge) static int lt9611_mipi_input_analog(struct lt9611 *lt9611) { const struct reg_sequence reg_cfg[] = { - { 0x8106, 0x40 }, /* port A rx current */ + { 0x8106, 0x60 }, /* port A rx current */ + { 0x8107, 0x3f }, /* port A rx current */ + { 0x8108, 0x3f }, /* port A rx current */ { 0x810a, 0xfe }, /* port A ldo voltage set */ { 0x810b, 0xbf }, /* enable port A lprx */ - { 0x8111, 0x40 }, /* port B rx current */ + { 0x8111, 0x60 }, /* port B rx current */ + { 0x8112, 0x3f }, /* port B rx current */ + { 0x8113, 0x3f }, /* port B rx current */ { 0x8115, 0xfe }, /* port B ldo voltage set */ { 0x8116, 0xbf }, /* enable port B lprx */ @@ -111,16 +116,19 @@ static int lt9611_mipi_input_digital(struct lt9611 *lt9611, const struct drm_display_mode *mode) { struct reg_sequence reg_cfg[] = { - { 0x8300, LT9611_4LANES }, - { 0x830a, 0x00 }, + { 0x8250, 0x14 }, + { 0x8300, 0x60 }, + { 0x8303, 0x4f }, + { 0x8304, 0x00 }, + { 0x8307, 0x40 }, { 0x824f, 0x80 }, - { 0x8250, 0x10 }, - { 0x8302, 0x0a }, - { 0x8306, 0x0a }, + { 0x8302, 0x08 }, + { 0x8306, 0x08 }, + { 0x830a, 0x00 }, }; if (lt9611->dsi1_node) - reg_cfg[1].def = 0x03; + reg_cfg[8].def = 0x03; return regmap_multi_reg_write(lt9611->regmap, reg_cfg, ARRAY_SIZE(reg_cfg)); } @@ -137,12 +145,12 @@ static void lt9611_mipi_video_setup(struct lt9611 *lt9611, hactive = mode->hdisplay; hsync_len = mode->hsync_end - mode->hsync_start; hfront_porch = mode->hsync_start - mode->hdisplay; - hsync_porch = mode->htotal - mode->hsync_start; + hsync_porch = hsync_len + mode->htotal - mode->hsync_end; vactive = mode->vdisplay; vsync_len = mode->vsync_end - mode->vsync_start; vfront_porch = mode->vsync_start - mode->vdisplay; - vsync_porch = mode->vtotal - mode->vsync_start; + vsync_porch = vsync_len + mode->vtotal - mode->vsync_end; regmap_write(lt9611->regmap, 0x830d, (u8)(v_total / 256)); regmap_write(lt9611->regmap, 0x830e, (u8)(v_total % 256)); @@ -170,9 +178,8 @@ static void lt9611_mipi_video_setup(struct lt9611 *lt9611, regmap_write(lt9611->regmap, 0x831b, (u8)(hsync_porch % 256)); } -static void lt9611_pcr_setup(struct lt9611 *lt9611, const struct drm_display_mode *mode, unsigned int postdiv) +static void lt9611_pcr_setup(struct lt9611 *lt9611, const struct drm_display_mode *mode) { - unsigned int pcr_m = mode->clock * 5 * postdiv / 27000; const struct reg_sequence reg_cfg[] = { { 0x830b, 0x01 }, { 0x830c, 0x10 }, @@ -189,49 +196,32 @@ static void lt9611_pcr_setup(struct lt9611 *lt9611, const struct drm_display_mod { 0x834a, 0x40 }, /* MK limit */ - { 0x832d, 0x38 }, + { 0x832d, 0x40 }, { 0x8331, 0x08 }, }; - u8 pol = 0x10; - - if (mode->flags & DRM_MODE_FLAG_NHSYNC) - pol |= 0x2; - if (mode->flags & DRM_MODE_FLAG_NVSYNC) - pol |= 0x1; - regmap_write(lt9611->regmap, 0x831d, pol); regmap_multi_reg_write(lt9611->regmap, reg_cfg, ARRAY_SIZE(reg_cfg)); - if (lt9611->dsi1_node) { - unsigned int hact = mode->hdisplay; - - hact >>= 2; - hact += 0x50; - hact = min(hact, 0x3e0U); - regmap_write(lt9611->regmap, 0x830b, hact / 256); - regmap_write(lt9611->regmap, 0x830c, hact % 256); - regmap_write(lt9611->regmap, 0x8348, hact / 256); - regmap_write(lt9611->regmap, 0x8349, hact % 256); - } - regmap_write(lt9611->regmap, 0x8326, pcr_m); + regmap_write(lt9611->regmap, 0x831d, 0x10); + regmap_write(lt9611->regmap, 0x8326, lt9611->pcr_m); /* pcr rst */ regmap_write(lt9611->regmap, 0x8011, 0x5a); regmap_write(lt9611->regmap, 0x8011, 0xfa); } -static int lt9611_pll_setup(struct lt9611 *lt9611, const struct drm_display_mode *mode, unsigned int *postdiv) +static int lt9611_pll_setup(struct lt9611 *lt9611, const struct drm_display_mode *mode) { unsigned int pclk = mode->clock; + u32 postdiv; const struct reg_sequence reg_cfg[] = { /* txpll init */ { 0x8123, 0x40 }, - { 0x8124, 0x64 }, + { 0x8124, 0x62 }, { 0x8125, 0x80 }, { 0x8126, 0x55 }, { 0x812c, 0x37 }, { 0x812f, 0x01 }, - { 0x8126, 0x55 }, { 0x8127, 0x66 }, { 0x8128, 0x88 }, { 0x812a, 0x20 }, @@ -241,29 +231,39 @@ static int lt9611_pll_setup(struct lt9611 *lt9611, const struct drm_display_mode if (pclk > 150000) { regmap_write(lt9611->regmap, 0x812d, 0x88); - *postdiv = 1; - } else if (pclk > 70000) { + postdiv = 1; + } else if (pclk > 80000) { regmap_write(lt9611->regmap, 0x812d, 0x99); - *postdiv = 2; + postdiv = 2; } else { regmap_write(lt9611->regmap, 0x812d, 0xaa); - *postdiv = 4; + postdiv = 4; } - /* - * first divide pclk by 2 first - * - write divide by 64k to 19:16 bits which means shift by 17 - * - write divide by 256 to 15:8 bits which means shift by 9 - * - write remainder to 7:0 bits, which means shift by 1 - */ - regmap_write(lt9611->regmap, 0x82e3, pclk >> 17); /* pclk[19:16] */ - regmap_write(lt9611->regmap, 0x82e4, pclk >> 9); /* pclk[15:8] */ - regmap_write(lt9611->regmap, 0x82e5, pclk >> 1); /* pclk[7:0] */ + lt9611->pcr_m = (pclk * 5 * postdiv) / 27000 - 1; + + regmap_write(lt9611->regmap, 0x832d, 0x40); + regmap_write(lt9611->regmap, 0x8331, 0x08); + regmap_write(lt9611->regmap, 0x8326, 0x80 | lt9611->pcr_m); + + /* pixel clock: divide by 2 and split into 3 bytes */ + pclk = pclk / 2; + regmap_write(lt9611->regmap, 0x82e3, pclk / 65536); + pclk = pclk % 65536; + regmap_write(lt9611->regmap, 0x82e4, pclk / 256); + regmap_write(lt9611->regmap, 0x82e5, pclk % 256); regmap_write(lt9611->regmap, 0x82de, 0x20); regmap_write(lt9611->regmap, 0x82de, 0xe0); - regmap_write(lt9611->regmap, 0x8016, 0xf1); + regmap_write(lt9611->regmap, 0x8011, 0x5a); + regmap_write(lt9611->regmap, 0x8011, 0xfa); + + regmap_write(lt9611->regmap, 0x8016, 0xf2); + + regmap_write(lt9611->regmap, 0x8018, 0xdc); + regmap_write(lt9611->regmap, 0x8018, 0xfc); + regmap_write(lt9611->regmap, 0x8016, 0xf3); return 0; @@ -333,55 +333,14 @@ static int lt9611_video_check(struct lt9611 *lt9611) return temp; } -static void lt9611_hdmi_set_infoframes(struct lt9611 *lt9611, - struct drm_connector *connector, - struct drm_display_mode *mode) +static void lt9611_hdmi_tx_digital(struct lt9611 *lt9611) { - union hdmi_infoframe infoframe; - ssize_t len; - u8 iframes = 0x0a; /* UD1 infoframe */ - u8 buf[32]; - int ret; - int i; - - ret = drm_hdmi_avi_infoframe_from_display_mode(&infoframe.avi, - connector, - mode); - if (ret < 0) - goto out; - - len = hdmi_infoframe_pack(&infoframe, buf, sizeof(buf)); - if (len < 0) - goto out; - - for (i = 0; i < len; i++) - regmap_write(lt9611->regmap, 0x8440 + i, buf[i]); - - ret = drm_hdmi_vendor_infoframe_from_display_mode(&infoframe.vendor.hdmi, - connector, - mode); - if (ret < 0) - goto out; - - len = hdmi_infoframe_pack(&infoframe, buf, sizeof(buf)); - if (len < 0) - goto out; + regmap_write(lt9611->regmap, 0x8443, 0x21); + regmap_write(lt9611->regmap, 0x8445, 0x40); + regmap_write(lt9611->regmap, 0x8447, 0x34); + regmap_write(lt9611->regmap, 0x843d, 0x0a); /* UD1 infoframe */ - for (i = 0; i < len; i++) - regmap_write(lt9611->regmap, 0x8474 + i, buf[i]); - - iframes |= 0x20; - -out: - regmap_write(lt9611->regmap, 0x843d, iframes); /* UD1 infoframe */ -} - -static void lt9611_hdmi_tx_digital(struct lt9611 *lt9611, bool is_hdmi) -{ - if (is_hdmi) - regmap_write(lt9611->regmap, 0x82d6, 0x8c); - else - regmap_write(lt9611->regmap, 0x82d6, 0x0c); + regmap_write(lt9611->regmap, 0x82d6, 0x8e); regmap_write(lt9611->regmap, 0x82d7, 0x04); } @@ -397,10 +356,10 @@ static void lt9611_hdmi_tx_phy(struct lt9611 *lt9611) { 0x8136, 0x00 }, { 0x8137, 0x44 }, { 0x813f, 0x0f }, - { 0x8140, 0xa0 }, - { 0x8141, 0xa0 }, - { 0x8142, 0xa0 }, - { 0x8143, 0xa0 }, + { 0x8140, 0x98 }, + { 0x8141, 0x98 }, + { 0x8142, 0x98 }, + { 0x8143, 0x98 }, { 0x8144, 0x0a }, }; @@ -470,11 +429,13 @@ static void lt9611_sleep_setup(struct lt9611 *lt9611) { 0x8023, 0x01 }, { 0x8157, 0x03 }, /* set addr pin as output */ { 0x8149, 0x0b }, + { 0x8151, 0x30 }, /* disable IRQ */ { 0x8102, 0x48 }, /* MIPI Rx power down */ { 0x8123, 0x80 }, { 0x8130, 0x00 }, - { 0x8011, 0x0a }, + { 0x8100, 0x01 }, /* bandgap power down */ + { 0x8101, 0x00 }, /* system clk power down */ }; regmap_multi_reg_write(lt9611->regmap, @@ -496,16 +457,16 @@ static int lt9611_power_on(struct lt9611 *lt9611) { 0x82cc, 0x78 }, /* irq init */ - { 0x8251, 0x01 }, + { 0x8251, 0x11 }, { 0x8258, 0x0a }, /* hpd irq */ - { 0x8259, 0x80 }, /* hpd debounce width */ + { 0x8259, 0x00 }, /* hpd debounce width */ { 0x829e, 0xf7 }, /* video check irq */ /* power consumption for work */ { 0x8004, 0xf0 }, { 0x8006, 0xf0 }, { 0x800a, 0x80 }, - { 0x800b, 0x40 }, + { 0x800b, 0x46 }, { 0x800d, 0xef }, { 0x8011, 0xfa }, }; @@ -687,40 +648,9 @@ lt9611_bridge_atomic_enable(struct drm_bridge *bridge, struct drm_bridge_state *old_bridge_state) { struct lt9611 *lt9611 = bridge_to_lt9611(bridge); - struct drm_atomic_state *state = old_bridge_state->base.state; - struct drm_connector *connector; - struct drm_connector_state *conn_state; - struct drm_crtc_state *crtc_state; - struct drm_display_mode *mode; - unsigned int postdiv; - - connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); - if (WARN_ON(!connector)) - return; - - conn_state = drm_atomic_get_new_connector_state(state, connector); - if (WARN_ON(!conn_state)) - return; - - crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); - if (WARN_ON(!crtc_state)) - return; - - mode = &crtc_state->adjusted_mode; - - lt9611_mipi_input_digital(lt9611, mode); - lt9611_pll_setup(lt9611, mode, &postdiv); - lt9611_mipi_video_setup(lt9611, mode); - lt9611_pcr_setup(lt9611, mode, postdiv); - - if (lt9611_power_on(lt9611)) { - dev_err(lt9611->dev, "power on failed\n"); - return; - } lt9611_mipi_input_analog(lt9611); - lt9611_hdmi_set_infoframes(lt9611, connector, mode); - lt9611_hdmi_tx_digital(lt9611, connector->display_info.is_hdmi); + lt9611_hdmi_tx_digital(lt9611); lt9611_hdmi_tx_phy(lt9611); msleep(500); @@ -728,6 +658,15 @@ lt9611_bridge_atomic_enable(struct drm_bridge *bridge, lt9611_video_check(lt9611); /* Enable HDMI output */ + regmap_write(lt9611->regmap, 0x8123, 0x40); + regmap_write(lt9611->regmap, 0x82de, 0x20); + regmap_write(lt9611->regmap, 0x82de, 0xe0); + regmap_write(lt9611->regmap, 0x8018, 0xdc); + regmap_write(lt9611->regmap, 0x8018, 0xfc); + regmap_write(lt9611->regmap, 0x8016, 0xf1); + regmap_write(lt9611->regmap, 0x8016, 0xf3); + regmap_write(lt9611->regmap, 0x8011, 0x5a); + regmap_write(lt9611->regmap, 0x8011, 0xfa); regmap_write(lt9611->regmap, 0x8130, 0xea); } @@ -820,29 +759,54 @@ static void lt9611_bridge_atomic_pre_enable(struct drm_bridge *bridge, struct drm_bridge_state *old_bridge_state) { struct lt9611 *lt9611 = bridge_to_lt9611(bridge); - static const struct reg_sequence reg_cfg[] = { + struct drm_atomic_state *state = old_bridge_state->base.state; + struct drm_connector *connector; + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + struct drm_display_mode *mode; + static const struct reg_sequence sleep_wake[] = { { 0x8102, 0x12 }, { 0x8123, 0x40 }, { 0x8130, 0xea }, { 0x8011, 0xfa }, }; - if (!lt9611->sleep) + if (lt9611->sleep) { + regmap_multi_reg_write(lt9611->regmap, + sleep_wake, ARRAY_SIZE(sleep_wake)); + lt9611->sleep = false; + } + + /* Power on before PLL setup - must run before pll_setup writes */ + if (lt9611_power_on(lt9611)) { + dev_err(lt9611->dev, "power on failed\n"); return; + } - regmap_multi_reg_write(lt9611->regmap, - reg_cfg, ARRAY_SIZE(reg_cfg)); + connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); + if (WARN_ON(!connector)) + return; - lt9611->sleep = false; + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (WARN_ON(!conn_state)) + return; + + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); + if (WARN_ON(!crtc_state)) + return; + + mode = &crtc_state->adjusted_mode; + + lt9611_mipi_input_digital(lt9611, mode); + lt9611_pll_setup(lt9611, mode); + lt9611_pcr_setup(lt9611, mode); + lt9611_mipi_video_setup(lt9611, mode); } static void lt9611_bridge_atomic_post_disable(struct drm_bridge *bridge, struct drm_bridge_state *old_bridge_state) { - struct lt9611 *lt9611 = bridge_to_lt9611(bridge); - - lt9611_sleep_setup(lt9611); } static const struct drm_edid *lt9611_bridge_edid_read(struct drm_bridge *bridge, @@ -979,7 +943,7 @@ static int lt9611_audio_startup(struct device *dev, void *data) { struct lt9611 *lt9611 = data; - regmap_write(lt9611->regmap, 0x82d6, 0x8c); + regmap_write(lt9611->regmap, 0x82d6, 0x8e); regmap_write(lt9611->regmap, 0x82d7, 0x04); regmap_write(lt9611->regmap, 0x8406, 0x08); @@ -1116,7 +1080,7 @@ static int lt9611_probe(struct i2c_client *client) lt9611->bridge.funcs = <9611_bridge_funcs; lt9611->bridge.of_node = client->dev.of_node; lt9611->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | - DRM_BRIDGE_OP_HPD | DRM_BRIDGE_OP_MODES; + DRM_BRIDGE_OP_MODES; lt9611->bridge.type = DRM_MODE_CONNECTOR_HDMIA; drm_bridge_add(<9611->bridge); @@ -1137,6 +1101,8 @@ static int lt9611_probe(struct i2c_client *client) } } + /* Power on to configure IRQ routing for HPD detection */ + lt9611_power_on(lt9611); lt9611_enable_hpd_interrupts(lt9611); ret = lt9611_audio_init(dev, lt9611); diff --git a/drivers/gpu/drm/canaan/Kconfig b/drivers/gpu/drm/canaan/Kconfig new file mode 100644 index 00000000000000..bb0fa297c80ee4 --- /dev/null +++ b/drivers/gpu/drm/canaan/Kconfig @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: GPL-2.0-only +config DRM_CANAAN + bool "DRM Support for Canaan K230 Display Controller" + depends on OF + depends on COMMON_CLK + depends on DRM + select DRM_KMS_HELPER + select DRM_KMS_DMA_HELPER + select DRM_GEM_DMA_HELPER + select VIDEOMODE_HELPERS + default ARCH_CANAAN + help + Choose this option if you have a Canaan K230 soc chipset. + This driver provides kernel mode setting and buffer + management to userspace. This driver does not provide + 2D or 3D acceleration; acceleration is performed by other + IP found on the SoC. + +if DRM_CANAAN + +config DRM_CANAAN_DSI + bool "Canaan K230 MIPI-DSI Controller Support" + select DRM_MIPI_DSI + select DRM_DISPLAY_HELPER + select DRM_BRIDGE_CONNECTOR + default DRM_CANAAN + help + Choose this option if you have a Canaan K230 soc chipset. + This driver provides MIPI-DSI controller for DRM. + This driver does not provide 2D or 3D acceleration; + acceleration is performed by other + IP found on the SoC. + +endif diff --git a/drivers/gpu/drm/canaan/Makefile b/drivers/gpu/drm/canaan/Makefile new file mode 100644 index 00000000000000..e547cca4c4da14 --- /dev/null +++ b/drivers/gpu/drm/canaan/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only +canaan-drm-y := \ + canaan_drv.o \ + canaan_vo.o \ + canaan_crtc.o \ + canaan_plane.o \ + canaan_phy.o + +obj-$(CONFIG_DRM_CANAAN) += canaan-drm.o +obj-$(CONFIG_DRM_CANAAN_DSI) += canaan_dsi.o diff --git a/drivers/gpu/drm/canaan/canaan_crtc.c b/drivers/gpu/drm/canaan/canaan_crtc.c new file mode 100644 index 00000000000000..b815a4d9e833fb --- /dev/null +++ b/drivers/gpu/drm/canaan/canaan_crtc.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022, Canaan Bright Sight Co., Ltd + * + * All enquiries to https://www.canaan-creative.com/ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "canaan_vo.h" +#include "canaan_crtc.h" +#include "canaan_plane.h" + +static int canaan_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct canaan_crtc *canaan_crtc = to_canaan_crtc(crtc); + struct canaan_vo *vo = canaan_crtc->vo; + + DRM_DEBUG_DRIVER("Enable vblank on CRTC:%d\n", crtc->base.id); + return canaan_vo_enable_vblank(vo); +} + +static void canaan_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct canaan_crtc *canaan_crtc = to_canaan_crtc(crtc); + struct canaan_vo *vo = canaan_crtc->vo; + + DRM_DEBUG_DRIVER("Disable vblank on CRTC:%d\n", crtc->base.id); + canaan_vo_disable_vblank(vo); +} + +static void canaan_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *old_crtc_state) +{ + struct canaan_crtc *canaan_crtc = to_canaan_crtc(crtc); + struct canaan_vo *vo = canaan_crtc->vo; + struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; + + DRM_DEBUG_DRIVER("Enable the CRTC:%d\n", crtc->base.id); + + canaan_vo_enable_crtc(vo, canaan_crtc, adjusted_mode); + drm_crtc_vblank_on(crtc); +} + +static void canaan_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *old_crtc_state) +{ + struct canaan_crtc *canaan_crtc = to_canaan_crtc(crtc); + struct canaan_vo *vo = canaan_crtc->vo; + + DRM_DEBUG_DRIVER("Disable the CRTC:%d\n", crtc->base.id); + + /* Send any pending event before disabling vblank */ + if (crtc->state->event) { + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + spin_unlock_irq(&crtc->dev->event_lock); + crtc->state->event = NULL; + } + + drm_crtc_vblank_off(crtc); + canaan_vo_disable_crtc(vo, canaan_crtc); +} + +static void canaan_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_atomic_state *old_crtc_state) +{ + struct canaan_crtc *canaan_crtc = to_canaan_crtc(crtc); + struct canaan_vo *vo = canaan_crtc->vo; + struct drm_pending_vblank_event *event = crtc->state->event; + + DRM_DEBUG_DRIVER("Flush the configuration\n"); + canaan_vo_flush_config(vo); + + if (event) { + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_arm_vblank_event(crtc, event); + spin_unlock_irq(&crtc->dev->event_lock); + crtc->state->event = NULL; + } +} + +static const struct drm_crtc_funcs canaan_crtc_funcs = { + .reset = drm_atomic_helper_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .enable_vblank = canaan_crtc_enable_vblank, + .disable_vblank = canaan_crtc_disable_vblank, +}; + +static const struct drm_crtc_helper_funcs canaan_crtc_helper_funcs = { + .atomic_enable = canaan_crtc_atomic_enable, + .atomic_disable = canaan_crtc_atomic_disable, + .atomic_flush = canaan_crtc_atomic_flush, +}; + +struct canaan_crtc *canaan_crtc_create(struct drm_device *drm_dev, + struct drm_plane *primary, + struct drm_plane *cursor, + struct canaan_vo *vo) +{ + int ret = 0; + struct canaan_crtc *canaan_crtc = NULL; + struct drm_crtc *crtc = NULL; + struct device *dev = vo->dev; + + canaan_crtc = devm_kzalloc(dev, sizeof(struct canaan_crtc), GFP_KERNEL); + if (!canaan_crtc) + return ERR_PTR(-ENOMEM); + canaan_crtc->vo = vo; + crtc = &canaan_crtc->base; + + ret = drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor, + &canaan_crtc_funcs, "canaan_crtc"); + if (ret) { + DRM_DEV_ERROR(dev, "Failed to init CRTC\n"); + return ERR_PTR(ret); + } + + drm_crtc_helper_add(crtc, &canaan_crtc_helper_funcs); + + drm_mode_crtc_set_gamma_size(crtc, 256); + drm_crtc_enable_color_mgmt(crtc, 0, false, 256); + DRM_DEBUG_DRIVER("Create the CRTC:%d\n", crtc->base.id); + + return canaan_crtc; +} diff --git a/drivers/gpu/drm/canaan/canaan_crtc.h b/drivers/gpu/drm/canaan/canaan_crtc.h new file mode 100644 index 00000000000000..8f6af93309ef7e --- /dev/null +++ b/drivers/gpu/drm/canaan/canaan_crtc.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2022, Canaan Bright Sight Co., Ltd + * + * All enquiries to https://www.canaan-creative.com/ + * + */ + +#ifndef __CANAAN_CRTC_H__ +#define __CANAAN_CRTC_H__ + +struct canaan_crtc { + struct drm_crtc base; + struct canaan_vo *vo; + struct drm_pending_vblank_event *event; +}; + +static inline struct canaan_crtc *to_canaan_crtc(struct drm_crtc *crtc) +{ + return container_of(crtc, struct canaan_crtc, base); +} + +struct canaan_crtc *canaan_crtc_create(struct drm_device *drm_dev, + struct drm_plane *primary, + struct drm_plane *cursor, + struct canaan_vo *vo); + +#endif /* __CANAAN_CRTC_H__ */ diff --git a/drivers/gpu/drm/canaan/canaan_drv.c b/drivers/gpu/drm/canaan/canaan_drv.c new file mode 100644 index 00000000000000..3a08488e65a677 --- /dev/null +++ b/drivers/gpu/drm/canaan/canaan_drv.c @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022, Canaan Bright Sight Co., Ltd + * + * All enquiries to https://www.canaan-creative.com/ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "canaan_drv.h" +#include "canaan_vo.h" + +static int canaan_drm_open(struct inode *inode, struct file *filp); +static int canaan_drm_release(struct inode *inode, struct file *filp); + +int canaan_drm_gem_dma_mmap(struct drm_gem_dma_object *dma_obj, struct vm_area_struct *vma) +{ + struct drm_gem_object *obj = &dma_obj->base; + int ret; + + /* + * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the + * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map + * the whole buffer. + */ + vma->vm_pgoff -= drm_vma_node_start(&obj->vma_node); + vm_flags_mod(vma, VM_DONTEXPAND, VM_PFNMAP); + + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + ret = remap_pfn_range( + vma, + vma->vm_start, + dma_obj->dma_addr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot + ); + + if (ret) + drm_gem_vm_close(vma); + + return ret; +} + +static int canaan_drm_gem_dma_object_mmap(struct drm_gem_object *obj, + struct vm_area_struct *vma) +{ + struct drm_gem_dma_object *dma_obj = to_drm_gem_dma_obj(obj); + + return canaan_drm_gem_dma_mmap(dma_obj, vma); +} + +static const struct drm_gem_object_funcs drm_gem_dma_default_funcs = { + .free = drm_gem_dma_object_free, + .print_info = drm_gem_dma_object_print_info, + .get_sg_table = drm_gem_dma_object_get_sg_table, + .vmap = drm_gem_dma_object_vmap, + .mmap = canaan_drm_gem_dma_object_mmap, + .vm_ops = &drm_gem_dma_vm_ops, +}; + +static struct drm_gem_dma_object * +__drm_gem_dma_create(struct drm_device *drm, size_t size, bool private) +{ + struct drm_gem_dma_object *dma_obj; + struct drm_gem_object *gem_obj; + int ret = 0; + + if (drm->driver->gem_create_object) { + gem_obj = drm->driver->gem_create_object(drm, size); + if (IS_ERR(gem_obj)) + return ERR_CAST(gem_obj); + dma_obj = to_drm_gem_dma_obj(gem_obj); + } else { + dma_obj = kzalloc(sizeof(*dma_obj), GFP_KERNEL); + if (!dma_obj) + return ERR_PTR(-ENOMEM); + gem_obj = &dma_obj->base; + } + + if (!gem_obj->funcs) + gem_obj->funcs = &drm_gem_dma_default_funcs; + + if (drm_gem_object_init(drm, gem_obj, size)) + goto error; + + ret = drm_gem_create_mmap_offset(gem_obj); + if (ret) { + drm_gem_object_release(gem_obj); + goto error; + } + + return dma_obj; + +error: + kfree(dma_obj); + return ERR_PTR(ret); +} + +static struct drm_gem_dma_object *canaan_drm_gem_dma_create(struct drm_device *drm, + size_t size) +{ + struct drm_gem_dma_object *dma_obj; + int ret; + + size = round_up(size, PAGE_SIZE); + + dma_obj = __drm_gem_dma_create(drm, size, false); + if (IS_ERR(dma_obj)) + return dma_obj; + + dma_obj->vaddr = dma_alloc_coherent(drm->dev, size, + &dma_obj->dma_addr, + GFP_KERNEL | __GFP_NOWARN); + if (!dma_obj->vaddr) { + drm_dbg(drm, "failed to allocate buffer with size %zu\n", + size); + ret = -ENOMEM; + goto error; + } + + return dma_obj; + +error: + drm_gem_object_put(&dma_obj->base); + return ERR_PTR(ret); +} + +static int canaan_drm_dumb_create(struct drm_file *file_priv, + struct drm_device *drm, + struct drm_mode_create_dumb *args) +{ + struct drm_gem_dma_object *dma_obj; + struct drm_gem_object *gem_obj; + int ret; + + args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8); + args->size = args->pitch * args->height; + + dma_obj = canaan_drm_gem_dma_create(drm, args->size); + if (IS_ERR(dma_obj)) + return PTR_ERR(dma_obj); + + gem_obj = &dma_obj->base; + ret = drm_gem_handle_create(file_priv, gem_obj, &args->handle); + drm_gem_object_put(gem_obj); + + return ret; +} + +static const struct file_operations canaan_drm_fops = { + .owner = THIS_MODULE, + .open = canaan_drm_open, + .release = canaan_drm_release, + .unlocked_ioctl = drm_ioctl, + .compat_ioctl = drm_compat_ioctl, + .poll = drm_poll, + .read = drm_read, + .llseek = noop_llseek, + .mmap = drm_gem_mmap, + .fop_flags = FOP_UNSIGNED_OFFSET, + DRM_GEM_DMA_UNMAPPED_AREA_FOPS +}; + +static struct drm_driver canaan_drm_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, + .dumb_create = canaan_drm_dumb_create, + .gem_prime_import_sg_table = drm_gem_dma_prime_import_sg_table_vmap, + .fops = &canaan_drm_fops, + .name = "canaan-drm", + .desc = "Canaan K230 DRM driver", + .date = "20230501", + .major = 1, + .minor = 0, +}; + +static const struct drm_mode_config_funcs canaan_drm_mode_config_funcs = { + .fb_create = drm_gem_fb_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static const struct drm_mode_config_helper_funcs + canaan_drm_mode_config_helpers = { + .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, + }; + +static struct device *disp_dev; + +static int canaan_drm_open(struct inode *inode, struct file *filp) +{ + int ret; + + pm_runtime_get_sync(disp_dev); + ret = drm_open(inode, filp); + + return ret; +} + +static int canaan_drm_release(struct inode *inode, struct file *filp) +{ + int ret; + + ret = drm_release(inode, filp); + + return ret; +} + +static int canaan_drm_bind(struct device *dev) +{ + int ret = 0; + struct drm_device *drm_dev; + + drm_dev = drm_dev_alloc(&canaan_drm_driver, dev); + if (IS_ERR(drm_dev)) + return PTR_ERR(drm_dev); + dev_set_drvdata(dev, drm_dev); + + drm_mode_config_init(drm_dev); + drm_dev->mode_config.min_width = 16; + drm_dev->mode_config.min_height = 16; + drm_dev->mode_config.max_width = 4096; + drm_dev->mode_config.max_height = 4096; + drm_dev->mode_config.normalize_zpos = true; + drm_dev->mode_config.funcs = &canaan_drm_mode_config_funcs; + drm_dev->mode_config.helper_private = &canaan_drm_mode_config_helpers; + + /* Power on DISP domain before accessing hardware */ + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + DRM_DEV_ERROR(dev, "Failed to power on display: %d\n", ret); + goto cleanup_mode_config; + } + + ret = component_bind_all(dev, drm_dev); + if (ret) { + DRM_DEV_ERROR(dev, "Failed to bind all components\n"); + goto put_runtime; + } + + ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc); + if (ret) { + DRM_DEV_ERROR(dev, "Failed to init vblank\n"); + goto unbind_all; + } + + drm_mode_config_reset(drm_dev); + drm_kms_helper_poll_init(drm_dev); + + ret = drm_dev_register(drm_dev, 0); + if (ret) { + DRM_DEV_ERROR(dev, "Failed to register drm device\n"); + goto finish_poll; + } + + drm_fbdev_dma_setup(drm_dev, 32); + DRM_DEV_INFO(dev, "Canaan K230 DRM driver register successfully\n"); + + return 0; + +finish_poll: + drm_kms_helper_poll_fini(drm_dev); +unbind_all: + component_unbind_all(dev, drm_dev); +put_runtime: + pm_runtime_put(dev); +cleanup_mode_config: + drm_mode_config_cleanup(drm_dev); + + dev_set_drvdata(dev, NULL); + drm_dev_put(drm_dev); + return ret; +} + +static void canaan_drm_unbind(struct device *dev) +{ + struct drm_device *drm_dev = dev_get_drvdata(dev); + + drm_dev_unregister(drm_dev); + drm_kms_helper_poll_fini(drm_dev); + drm_atomic_helper_shutdown(drm_dev); + component_unbind_all(dev, drm_dev); + drm_mode_config_cleanup(drm_dev); + dev_set_drvdata(dev, NULL); + drm_dev_put(drm_dev); + pm_runtime_put(dev); +} + +static const struct component_master_ops canaan_drm_master_ops = { + .bind = canaan_drm_bind, + .unbind = canaan_drm_unbind, +}; + +static struct platform_driver *const component_drivers[] = { + &canaan_vo_driver, + &canaan_dsi_driver, +}; + +static int compare_dev(struct device *dev, void *data) +{ + return dev == (struct device *)data; +} + +static struct component_match *canaan_drm_match_add(struct device *dev) +{ + int i = 0; + struct component_match *match = NULL; + + for (i = 0; i < ARRAY_SIZE(component_drivers); i++) { + struct device_driver *driver = &component_drivers[i]->driver; + struct device *p = NULL, *d; + + while ((d = platform_find_device_by_driver(p, driver))) { + put_device(p); + component_match_add(dev, &match, compare_dev, d); + p = d; + } + put_device(p); + } + + return match ?: ERR_PTR(-ENODEV); +} + +static int canaan_drm_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct component_match *match = NULL; + int ret; + + disp_dev = dev; + pm_runtime_enable(disp_dev); + match = canaan_drm_match_add(dev); + if (IS_ERR(match)) { + pm_runtime_disable(disp_dev); + return PTR_ERR(match); + } + + ret = component_master_add_with_match(dev, &canaan_drm_master_ops, + match); + if (ret) + pm_runtime_disable(disp_dev); + + return ret; +} + +static void canaan_drm_platform_remove(struct platform_device *pdev) +{ + component_master_del(&pdev->dev, &canaan_drm_master_ops); + pm_runtime_disable(&pdev->dev); +} + +static const struct of_device_id canaan_drm_of_table[] = { + { + .compatible = "canaan,display-subsystem", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, canaan_drm_of_table); + +static struct platform_driver canaan_drm_platform_driver = { + .probe = canaan_drm_platform_probe, + .remove = canaan_drm_platform_remove, + .driver = { + .name = "canaan-drm", + .of_match_table = canaan_drm_of_table, + }, +}; + +static int __init canaan_drm_init(void) +{ + int ret = 0; + + ret = platform_register_drivers(component_drivers, + ARRAY_SIZE(component_drivers)); + if (ret) + return ret; + + ret = platform_driver_register(&canaan_drm_platform_driver); + if (ret) + platform_unregister_drivers(component_drivers, + ARRAY_SIZE(component_drivers)); + + return ret; +} + +static void __exit canaan_drm_exit(void) +{ + platform_unregister_drivers(component_drivers, + ARRAY_SIZE(component_drivers)); + platform_driver_unregister(&canaan_drm_platform_driver); +} + +module_init(canaan_drm_init); +module_exit(canaan_drm_exit); + +MODULE_DESCRIPTION("Canaan K230 DRM driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/canaan/canaan_drv.h b/drivers/gpu/drm/canaan/canaan_drv.h new file mode 100644 index 00000000000000..646a25453ec08a --- /dev/null +++ b/drivers/gpu/drm/canaan/canaan_drv.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2022, Canaan Bright Sight Co., Ltd + * + * All enquiries to https://www.canaan-creative.com/ + * + */ + +#ifndef __CANAAN_DRV_H__ +#define __CANAAN_DRV_H__ + +extern struct platform_driver canaan_vo_driver; +extern struct platform_driver canaan_dsi_driver; + +#endif /* __CANAAN_DRV_H__ */ diff --git a/drivers/gpu/drm/canaan/canaan_dsi.c b/drivers/gpu/drm/canaan/canaan_dsi.c new file mode 100644 index 00000000000000..9f3f75c89fcbba --- /dev/null +++ b/drivers/gpu/drm/canaan/canaan_dsi.c @@ -0,0 +1,646 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022, Canaan Bright Sight Co., Ltd + * + * All enquiries to https://www.canaan-creative.com/ + * + */ + +#include "drm/drm_bridge.h" +#include "drm/drm_of.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "canaan_dsi.h" +#include