Hi, Dave and all.
this patch set includes the following features. - add HDMI version 1.4 support. - add mode_fixup feature. . hdmi module would change current mode to driver desired mode properly. - add buffer alloction type. . we can allocate physically continuous or non-continuous memory and also it creates scatterlist for iommu support so allocated memory region can be mapped to iommu page table using scatterlist. - add dma address get/put interface. . this function would be used for drm based 2d acceleration driver to get/put dma address through gem handle. when exynos_drm_get_dma_address is called reference count of gem object would be increased not to be released by gem close and when exynos_drm_put_dma_address is called the reference count of this gem object would be decreased to be released. - add virtual display driver. . this driver would be used for wireless display. - add direct rendering based 2d graphics acceleration module. . exynos SoC chip has fimg2d named 2d graphics accelerator and this driver supports only exynos4x12 series.
this patch set is based on git repository below: git://git.infradead.org/users/kmpark/linux-samsung exynos-drm-fixes commit-id: f6d69d1e77509d71c29f2a7b119214b8fdf86ae2
and exynos-drm-fixes is based on git repository below: git://people.freedesktop.org/~airlied/linux.git drm-fixes commit-id: 38aa4a568ba4c3ccba83e862a01e3e60e3b811ee
P.S. drm-next doesn't reflect updated latest codes of mainline so this patch set should be merged to drm-next after latest mainline codes are merged to drm-next. now is just for review and if latest codes are merged to drm-next then I will merge this patch set to exynos-drm-next for git pull request.
Thanks.
Inki Dae (5): drm/exynos: add HDMI version 1.4 support drm/exynos: added mode_fixup feature and code clean. drm/exynos: added buffer allocation type. drm/exynos: added new funtion to get/put dma address. drm/exynos: added virtual display driver.
Joonyoung Shim (5): drm/exynos: release pending pageflip events when closed drm/exynos: remove module of exynos drm subdrv drm/exynos: add subdrv open/close functions drm/exynos: add is_local member in exynos_drm_subdrv struct drm/exynos: add G2D driver
drivers/gpu/drm/exynos/Kconfig | 20 +- drivers/gpu/drm/exynos/Makefile | 12 +- drivers/gpu/drm/exynos/exynos_ddc.c | 1 - drivers/gpu/drm/exynos/exynos_drm_buf.c | 152 +++- drivers/gpu/drm/exynos/exynos_drm_buf.h | 4 +- drivers/gpu/drm/exynos/exynos_drm_connector.c | 35 +- drivers/gpu/drm/exynos/exynos_drm_core.c | 140 +-- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 12 +- drivers/gpu/drm/exynos/exynos_drm_drv.c | 125 +++- drivers/gpu/drm/exynos/exynos_drm_drv.h | 49 +- drivers/gpu/drm/exynos/exynos_drm_encoder.c | 24 +- drivers/gpu/drm/exynos/exynos_drm_fb.c | 6 - drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 90 +-- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 20 +- drivers/gpu/drm/exynos/exynos_drm_g2d.c | 864 +++++++++++++++++ drivers/gpu/drm/exynos/exynos_drm_g2d.h | 36 + drivers/gpu/drm/exynos/exynos_drm_gem.c | 346 +++++++- drivers/gpu/drm/exynos/exynos_drm_gem.h | 29 +- drivers/gpu/drm/exynos/exynos_drm_hdmi.c | 115 +-- drivers/gpu/drm/exynos/exynos_drm_hdmi.h | 5 + drivers/gpu/drm/exynos/exynos_drm_vidi.c | 677 ++++++++++++++ drivers/gpu/drm/exynos/exynos_drm_vidi.h | 36 + drivers/gpu/drm/exynos/exynos_hdmi.c | 1234 ++++++++++++++++++++++--- drivers/gpu/drm/exynos/exynos_hdmi.h | 10 +- drivers/gpu/drm/exynos/exynos_mixer.c | 7 - drivers/gpu/drm/exynos/regs-hdmi.h | 306 ++++++- include/drm/exynos_drm.h | 84 ++ 27 files changed, 3879 insertions(+), 560 deletions(-) create mode 100644 drivers/gpu/drm/exynos/exynos_drm_g2d.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_g2d.h create mode 100644 drivers/gpu/drm/exynos/exynos_drm_vidi.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_vidi.h
Later Exynos series from Exynos4X12 support HDMI version 1.4. We will distinguish to use which version via platform data.
Signed-off-by: Joonyoung Shim jy0922.shim@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_hdmi.c | 1152 +++++++++++++++++++++++++++++++--- drivers/gpu/drm/exynos/exynos_hdmi.h | 10 +- drivers/gpu/drm/exynos/regs-hdmi.h | 306 ++++++++-- include/drm/exynos_drm.h | 2 + 4 files changed, 1325 insertions(+), 145 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 3429d3f..1cfe86e 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -43,42 +43,43 @@ #define HDMI_OVERLAY_NUMBER 3 #define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev))
-static const u8 hdmiphy_conf27[32] = { +/* HDMI Version 1.3 */ +static const u8 hdmiphy_v13_conf27[32] = { 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40, 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87, 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, };
-static const u8 hdmiphy_conf27_027[32] = { +static const u8 hdmiphy_v13_conf27_027[32] = { 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64, 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87, 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, };
-static const u8 hdmiphy_conf74_175[32] = { +static const u8 hdmiphy_v13_conf74_175[32] = { 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B, 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9, 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00, };
-static const u8 hdmiphy_conf74_25[32] = { +static const u8 hdmiphy_v13_conf74_25[32] = { 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40, 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba, 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0, 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00, };
-static const u8 hdmiphy_conf148_5[32] = { +static const u8 hdmiphy_v13_conf148_5[32] = { 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40, 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba, 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0, 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00, };
-struct hdmi_tg_regs { +struct hdmi_v13_tg_regs { u8 cmd; u8 h_fsz_l; u8 h_fsz_h; @@ -110,7 +111,7 @@ struct hdmi_tg_regs { u8 field_bot_hdmi_h; };
-struct hdmi_core_regs { +struct hdmi_v13_core_regs { u8 h_blank[2]; u8 v_blank[3]; u8 h_v_line[3]; @@ -123,12 +124,21 @@ struct hdmi_core_regs { u8 v_sync_gen3[3]; };
-struct hdmi_preset_conf { - struct hdmi_core_regs core; - struct hdmi_tg_regs tg; +struct hdmi_v13_preset_conf { + struct hdmi_v13_core_regs core; + struct hdmi_v13_tg_regs tg; +}; + +struct hdmi_v13_conf { + int width; + int height; + int vrefresh; + bool interlace; + const u8 *hdmiphy_data; + const struct hdmi_v13_preset_conf *conf; };
-static const struct hdmi_preset_conf hdmi_conf_480p = { +static const struct hdmi_v13_preset_conf hdmi_v13_conf_480p = { .core = { .h_blank = {0x8a, 0x00}, .v_blank = {0x0d, 0x6a, 0x01}, @@ -154,7 +164,7 @@ static const struct hdmi_preset_conf hdmi_conf_480p = { }, };
-static const struct hdmi_preset_conf hdmi_conf_720p60 = { +static const struct hdmi_v13_preset_conf hdmi_v13_conf_720p60 = { .core = { .h_blank = {0x72, 0x01}, .v_blank = {0xee, 0xf2, 0x00}, @@ -182,7 +192,7 @@ static const struct hdmi_preset_conf hdmi_conf_720p60 = { }, };
-static const struct hdmi_preset_conf hdmi_conf_1080i50 = { +static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080i50 = { .core = { .h_blank = {0xd0, 0x02}, .v_blank = {0x32, 0xB2, 0x00}, @@ -210,7 +220,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080i50 = { }, };
-static const struct hdmi_preset_conf hdmi_conf_1080p50 = { +static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080p50 = { .core = { .h_blank = {0xd0, 0x02}, .v_blank = {0x65, 0x6c, 0x01}, @@ -238,7 +248,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080p50 = { }, };
-static const struct hdmi_preset_conf hdmi_conf_1080i60 = { +static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080i60 = { .core = { .h_blank = {0x18, 0x01}, .v_blank = {0x32, 0xB2, 0x00}, @@ -266,7 +276,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080i60 = { }, };
-static const struct hdmi_preset_conf hdmi_conf_1080p60 = { +static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080p60 = { .core = { .h_blank = {0x18, 0x01}, .v_blank = {0x65, 0x6c, 0x01}, @@ -294,13 +304,530 @@ static const struct hdmi_preset_conf hdmi_conf_1080p60 = { }, };
+static const struct hdmi_v13_conf hdmi_v13_confs[] = { + { 1280, 720, 60, false, hdmiphy_v13_conf74_25, &hdmi_v13_conf_720p60 }, + { 1280, 720, 50, false, hdmiphy_v13_conf74_25, &hdmi_v13_conf_720p60 }, + { 720, 480, 60, false, hdmiphy_v13_conf27_027, &hdmi_v13_conf_480p }, + { 1920, 1080, 50, true, hdmiphy_v13_conf74_25, &hdmi_v13_conf_1080i50 }, + { 1920, 1080, 50, false, hdmiphy_v13_conf148_5, + &hdmi_v13_conf_1080p50 }, + { 1920, 1080, 60, true, hdmiphy_v13_conf74_25, &hdmi_v13_conf_1080i60 }, + { 1920, 1080, 60, false, hdmiphy_v13_conf148_5, + &hdmi_v13_conf_1080p60 }, +}; + +/* HDMI Version 1.4 */ +static const u8 hdmiphy_conf27_027[32] = { + 0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08, + 0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80, + 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, + 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, +}; + +static const u8 hdmiphy_conf74_25[32] = { + 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08, + 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80, + 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, + 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, +}; + +static const u8 hdmiphy_conf148_5[32] = { + 0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08, + 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80, + 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, + 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00, +}; + +struct hdmi_tg_regs { + u8 cmd; + u8 h_fsz_l; + u8 h_fsz_h; + u8 hact_st_l; + u8 hact_st_h; + u8 hact_sz_l; + u8 hact_sz_h; + u8 v_fsz_l; + u8 v_fsz_h; + u8 vsync_l; + u8 vsync_h; + u8 vsync2_l; + u8 vsync2_h; + u8 vact_st_l; + u8 vact_st_h; + u8 vact_sz_l; + u8 vact_sz_h; + u8 field_chg_l; + u8 field_chg_h; + u8 vact_st2_l; + u8 vact_st2_h; + u8 vact_st3_l; + u8 vact_st3_h; + u8 vact_st4_l; + u8 vact_st4_h; + u8 vsync_top_hdmi_l; + u8 vsync_top_hdmi_h; + u8 vsync_bot_hdmi_l; + u8 vsync_bot_hdmi_h; + u8 field_top_hdmi_l; + u8 field_top_hdmi_h; + u8 field_bot_hdmi_l; + u8 field_bot_hdmi_h; + u8 tg_3d; +}; + +struct hdmi_core_regs { + u8 h_blank[2]; + u8 v2_blank[2]; + u8 v1_blank[2]; + u8 v_line[2]; + u8 h_line[2]; + u8 hsync_pol[1]; + u8 vsync_pol[1]; + u8 int_pro_mode[1]; + u8 v_blank_f0[2]; + u8 v_blank_f1[2]; + u8 h_sync_start[2]; + u8 h_sync_end[2]; + u8 v_sync_line_bef_2[2]; + u8 v_sync_line_bef_1[2]; + u8 v_sync_line_aft_2[2]; + u8 v_sync_line_aft_1[2]; + u8 v_sync_line_aft_pxl_2[2]; + u8 v_sync_line_aft_pxl_1[2]; + u8 v_blank_f2[2]; /* for 3D mode */ + u8 v_blank_f3[2]; /* for 3D mode */ + u8 v_blank_f4[2]; /* for 3D mode */ + u8 v_blank_f5[2]; /* for 3D mode */ + u8 v_sync_line_aft_3[2]; + u8 v_sync_line_aft_4[2]; + u8 v_sync_line_aft_5[2]; + u8 v_sync_line_aft_6[2]; + u8 v_sync_line_aft_pxl_3[2]; + u8 v_sync_line_aft_pxl_4[2]; + u8 v_sync_line_aft_pxl_5[2]; + u8 v_sync_line_aft_pxl_6[2]; + u8 vact_space_1[2]; + u8 vact_space_2[2]; + u8 vact_space_3[2]; + u8 vact_space_4[2]; + u8 vact_space_5[2]; + u8 vact_space_6[2]; +}; + +struct hdmi_preset_conf { + struct hdmi_core_regs core; + struct hdmi_tg_regs tg; +}; + +struct hdmi_conf { + int width; + int height; + int vrefresh; + bool interlace; + const u8 *hdmiphy_data; + const struct hdmi_preset_conf *conf; +}; + +static const struct hdmi_preset_conf hdmi_conf_480p60 = { + .core = { + .h_blank = {0x8a, 0x00}, + .v2_blank = {0x0d, 0x02}, + .v1_blank = {0x2d, 0x00}, + .v_line = {0x0d, 0x02}, + .h_line = {0x5a, 0x03}, + .hsync_pol = {0x01}, + .vsync_pol = {0x01}, + .int_pro_mode = {0x00}, + .v_blank_f0 = {0xff, 0xff}, + .v_blank_f1 = {0xff, 0xff}, + .h_sync_start = {0x0e, 0x00}, + .h_sync_end = {0x4c, 0x00}, + .v_sync_line_bef_2 = {0x0f, 0x00}, + .v_sync_line_bef_1 = {0x09, 0x00}, + .v_sync_line_aft_2 = {0xff, 0xff}, + .v_sync_line_aft_1 = {0xff, 0xff}, + .v_sync_line_aft_pxl_2 = {0xff, 0xff}, + .v_sync_line_aft_pxl_1 = {0xff, 0xff}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + .vact_space_1 = {0xff, 0xff}, + .vact_space_2 = {0xff, 0xff}, + .vact_space_3 = {0xff, 0xff}, + .vact_space_4 = {0xff, 0xff}, + .vact_space_5 = {0xff, 0xff}, + .vact_space_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0x5a, 0x03, /* h_fsz */ + 0x8a, 0x00, 0xd0, 0x02, /* hact */ + 0x0d, 0x02, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x2d, 0x00, 0xe0, 0x01, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x48, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + +static const struct hdmi_preset_conf hdmi_conf_720p50 = { + .core = { + .h_blank = {0xbc, 0x02}, + .v2_blank = {0xee, 0x02}, + .v1_blank = {0x1e, 0x00}, + .v_line = {0xee, 0x02}, + .h_line = {0xbc, 0x07}, + .hsync_pol = {0x00}, + .vsync_pol = {0x00}, + .int_pro_mode = {0x00}, + .v_blank_f0 = {0xff, 0xff}, + .v_blank_f1 = {0xff, 0xff}, + .h_sync_start = {0xb6, 0x01}, + .h_sync_end = {0xde, 0x01}, + .v_sync_line_bef_2 = {0x0a, 0x00}, + .v_sync_line_bef_1 = {0x05, 0x00}, + .v_sync_line_aft_2 = {0xff, 0xff}, + .v_sync_line_aft_1 = {0xff, 0xff}, + .v_sync_line_aft_pxl_2 = {0xff, 0xff}, + .v_sync_line_aft_pxl_1 = {0xff, 0xff}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + .vact_space_1 = {0xff, 0xff}, + .vact_space_2 = {0xff, 0xff}, + .vact_space_3 = {0xff, 0xff}, + .vact_space_4 = {0xff, 0xff}, + .vact_space_5 = {0xff, 0xff}, + .vact_space_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0xbc, 0x07, /* h_fsz */ + 0xbc, 0x02, 0x00, 0x05, /* hact */ + 0xee, 0x02, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x1e, 0x00, 0xd0, 0x02, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x48, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + +static const struct hdmi_preset_conf hdmi_conf_720p60 = { + .core = { + .h_blank = {0x72, 0x01}, + .v2_blank = {0xee, 0x02}, + .v1_blank = {0x1e, 0x00}, + .v_line = {0xee, 0x02}, + .h_line = {0x72, 0x06}, + .hsync_pol = {0x00}, + .vsync_pol = {0x00}, + .int_pro_mode = {0x00}, + .v_blank_f0 = {0xff, 0xff}, + .v_blank_f1 = {0xff, 0xff}, + .h_sync_start = {0x6c, 0x00}, + .h_sync_end = {0x94, 0x00}, + .v_sync_line_bef_2 = {0x0a, 0x00}, + .v_sync_line_bef_1 = {0x05, 0x00}, + .v_sync_line_aft_2 = {0xff, 0xff}, + .v_sync_line_aft_1 = {0xff, 0xff}, + .v_sync_line_aft_pxl_2 = {0xff, 0xff}, + .v_sync_line_aft_pxl_1 = {0xff, 0xff}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + .vact_space_1 = {0xff, 0xff}, + .vact_space_2 = {0xff, 0xff}, + .vact_space_3 = {0xff, 0xff}, + .vact_space_4 = {0xff, 0xff}, + .vact_space_5 = {0xff, 0xff}, + .vact_space_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0x72, 0x06, /* h_fsz */ + 0x72, 0x01, 0x00, 0x05, /* hact */ + 0xee, 0x02, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x1e, 0x00, 0xd0, 0x02, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x48, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + +static const struct hdmi_preset_conf hdmi_conf_1080i50 = { + .core = { + .h_blank = {0xd0, 0x02}, + .v2_blank = {0x32, 0x02}, + .v1_blank = {0x16, 0x00}, + .v_line = {0x65, 0x04}, + .h_line = {0x50, 0x0a}, + .hsync_pol = {0x00}, + .vsync_pol = {0x00}, + .int_pro_mode = {0x01}, + .v_blank_f0 = {0x49, 0x02}, + .v_blank_f1 = {0x65, 0x04}, + .h_sync_start = {0x0e, 0x02}, + .h_sync_end = {0x3a, 0x02}, + .v_sync_line_bef_2 = {0x07, 0x00}, + .v_sync_line_bef_1 = {0x02, 0x00}, + .v_sync_line_aft_2 = {0x39, 0x02}, + .v_sync_line_aft_1 = {0x34, 0x02}, + .v_sync_line_aft_pxl_2 = {0x38, 0x07}, + .v_sync_line_aft_pxl_1 = {0x38, 0x07}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + .vact_space_1 = {0xff, 0xff}, + .vact_space_2 = {0xff, 0xff}, + .vact_space_3 = {0xff, 0xff}, + .vact_space_4 = {0xff, 0xff}, + .vact_space_5 = {0xff, 0xff}, + .vact_space_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0x50, 0x0a, /* h_fsz */ + 0xd0, 0x02, 0x80, 0x07, /* hact */ + 0x65, 0x04, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x16, 0x00, 0x1c, 0x02, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x49, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + +static const struct hdmi_preset_conf hdmi_conf_1080i60 = { + .core = { + .h_blank = {0x18, 0x01}, + .v2_blank = {0x32, 0x02}, + .v1_blank = {0x16, 0x00}, + .v_line = {0x65, 0x04}, + .h_line = {0x98, 0x08}, + .hsync_pol = {0x00}, + .vsync_pol = {0x00}, + .int_pro_mode = {0x01}, + .v_blank_f0 = {0x49, 0x02}, + .v_blank_f1 = {0x65, 0x04}, + .h_sync_start = {0x56, 0x00}, + .h_sync_end = {0x82, 0x00}, + .v_sync_line_bef_2 = {0x07, 0x00}, + .v_sync_line_bef_1 = {0x02, 0x00}, + .v_sync_line_aft_2 = {0x39, 0x02}, + .v_sync_line_aft_1 = {0x34, 0x02}, + .v_sync_line_aft_pxl_2 = {0xa4, 0x04}, + .v_sync_line_aft_pxl_1 = {0xa4, 0x04}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + .vact_space_1 = {0xff, 0xff}, + .vact_space_2 = {0xff, 0xff}, + .vact_space_3 = {0xff, 0xff}, + .vact_space_4 = {0xff, 0xff}, + .vact_space_5 = {0xff, 0xff}, + .vact_space_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0x98, 0x08, /* h_fsz */ + 0x18, 0x01, 0x80, 0x07, /* hact */ + 0x65, 0x04, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x16, 0x00, 0x1c, 0x02, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x49, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + +static const struct hdmi_preset_conf hdmi_conf_1080p50 = { + .core = { + .h_blank = {0xd0, 0x02}, + .v2_blank = {0x65, 0x04}, + .v1_blank = {0x2d, 0x00}, + .v_line = {0x65, 0x04}, + .h_line = {0x50, 0x0a}, + .hsync_pol = {0x00}, + .vsync_pol = {0x00}, + .int_pro_mode = {0x00}, + .v_blank_f0 = {0xff, 0xff}, + .v_blank_f1 = {0xff, 0xff}, + .h_sync_start = {0x0e, 0x02}, + .h_sync_end = {0x3a, 0x02}, + .v_sync_line_bef_2 = {0x09, 0x00}, + .v_sync_line_bef_1 = {0x04, 0x00}, + .v_sync_line_aft_2 = {0xff, 0xff}, + .v_sync_line_aft_1 = {0xff, 0xff}, + .v_sync_line_aft_pxl_2 = {0xff, 0xff}, + .v_sync_line_aft_pxl_1 = {0xff, 0xff}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + .vact_space_1 = {0xff, 0xff}, + .vact_space_2 = {0xff, 0xff}, + .vact_space_3 = {0xff, 0xff}, + .vact_space_4 = {0xff, 0xff}, + .vact_space_5 = {0xff, 0xff}, + .vact_space_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0x50, 0x0a, /* h_fsz */ + 0xd0, 0x02, 0x80, 0x07, /* hact */ + 0x65, 0x04, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x2d, 0x00, 0x38, 0x04, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x48, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + +static const struct hdmi_preset_conf hdmi_conf_1080p60 = { + .core = { + .h_blank = {0x18, 0x01}, + .v2_blank = {0x65, 0x04}, + .v1_blank = {0x2d, 0x00}, + .v_line = {0x65, 0x04}, + .h_line = {0x98, 0x08}, + .hsync_pol = {0x00}, + .vsync_pol = {0x00}, + .int_pro_mode = {0x00}, + .v_blank_f0 = {0xff, 0xff}, + .v_blank_f1 = {0xff, 0xff}, + .h_sync_start = {0x56, 0x00}, + .h_sync_end = {0x82, 0x00}, + .v_sync_line_bef_2 = {0x09, 0x00}, + .v_sync_line_bef_1 = {0x04, 0x00}, + .v_sync_line_aft_2 = {0xff, 0xff}, + .v_sync_line_aft_1 = {0xff, 0xff}, + .v_sync_line_aft_pxl_2 = {0xff, 0xff}, + .v_sync_line_aft_pxl_1 = {0xff, 0xff}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0x98, 0x08, /* h_fsz */ + 0x18, 0x01, 0x80, 0x07, /* hact */ + 0x65, 0x04, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x2d, 0x00, 0x38, 0x04, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x48, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + static const struct hdmi_conf hdmi_confs[] = { + { 720, 480, 60, false, hdmiphy_conf27_027, &hdmi_conf_480p60 }, + { 1280, 720, 50, false, hdmiphy_conf74_25, &hdmi_conf_720p50 }, { 1280, 720, 60, false, hdmiphy_conf74_25, &hdmi_conf_720p60 }, - { 1280, 720, 50, false, hdmiphy_conf74_25, &hdmi_conf_720p60 }, - { 720, 480, 60, false, hdmiphy_conf27_027, &hdmi_conf_480p }, { 1920, 1080, 50, true, hdmiphy_conf74_25, &hdmi_conf_1080i50 }, - { 1920, 1080, 50, false, hdmiphy_conf148_5, &hdmi_conf_1080p50 }, { 1920, 1080, 60, true, hdmiphy_conf74_25, &hdmi_conf_1080i60 }, + { 1920, 1080, 50, false, hdmiphy_conf148_5, &hdmi_conf_1080p50 }, { 1920, 1080, 60, false, hdmiphy_conf148_5, &hdmi_conf_1080p60 }, };
@@ -324,7 +851,7 @@ static inline void hdmi_reg_writemask(struct hdmi_context *hdata, writel(value, hdata->regs + reg_id); }
-static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) +static void hdmi_v13_regs_dump(struct hdmi_context *hdata, char *prefix) { #define DUMPREG(reg_id) \ DRM_DEBUG_KMS("%s:" #reg_id " = %08x\n", prefix, \ @@ -333,6 +860,101 @@ static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) DUMPREG(HDMI_INTC_FLAG); DUMPREG(HDMI_INTC_CON); DUMPREG(HDMI_HPD_STATUS); + DUMPREG(HDMI_V13_PHY_RSTOUT); + DUMPREG(HDMI_V13_PHY_VPLL); + DUMPREG(HDMI_V13_PHY_CMU); + DUMPREG(HDMI_V13_CORE_RSTOUT); + + DRM_DEBUG_KMS("%s: ---- CORE REGISTERS ----\n", prefix); + DUMPREG(HDMI_CON_0); + DUMPREG(HDMI_CON_1); + DUMPREG(HDMI_CON_2); + DUMPREG(HDMI_SYS_STATUS); + DUMPREG(HDMI_V13_PHY_STATUS); + DUMPREG(HDMI_STATUS_EN); + DUMPREG(HDMI_HPD); + DUMPREG(HDMI_MODE_SEL); + DUMPREG(HDMI_V13_HPD_GEN); + DUMPREG(HDMI_V13_DC_CONTROL); + DUMPREG(HDMI_V13_VIDEO_PATTERN_GEN); + + DRM_DEBUG_KMS("%s: ---- CORE SYNC REGISTERS ----\n", prefix); + DUMPREG(HDMI_H_BLANK_0); + DUMPREG(HDMI_H_BLANK_1); + DUMPREG(HDMI_V13_V_BLANK_0); + DUMPREG(HDMI_V13_V_BLANK_1); + DUMPREG(HDMI_V13_V_BLANK_2); + DUMPREG(HDMI_V13_H_V_LINE_0); + DUMPREG(HDMI_V13_H_V_LINE_1); + DUMPREG(HDMI_V13_H_V_LINE_2); + DUMPREG(HDMI_VSYNC_POL); + DUMPREG(HDMI_INT_PRO_MODE); + DUMPREG(HDMI_V13_V_BLANK_F_0); + DUMPREG(HDMI_V13_V_BLANK_F_1); + DUMPREG(HDMI_V13_V_BLANK_F_2); + DUMPREG(HDMI_V13_H_SYNC_GEN_0); + DUMPREG(HDMI_V13_H_SYNC_GEN_1); + DUMPREG(HDMI_V13_H_SYNC_GEN_2); + DUMPREG(HDMI_V13_V_SYNC_GEN_1_0); + DUMPREG(HDMI_V13_V_SYNC_GEN_1_1); + DUMPREG(HDMI_V13_V_SYNC_GEN_1_2); + DUMPREG(HDMI_V13_V_SYNC_GEN_2_0); + DUMPREG(HDMI_V13_V_SYNC_GEN_2_1); + DUMPREG(HDMI_V13_V_SYNC_GEN_2_2); + DUMPREG(HDMI_V13_V_SYNC_GEN_3_0); + DUMPREG(HDMI_V13_V_SYNC_GEN_3_1); + DUMPREG(HDMI_V13_V_SYNC_GEN_3_2); + + DRM_DEBUG_KMS("%s: ---- TG REGISTERS ----\n", prefix); + DUMPREG(HDMI_TG_CMD); + DUMPREG(HDMI_TG_H_FSZ_L); + DUMPREG(HDMI_TG_H_FSZ_H); + DUMPREG(HDMI_TG_HACT_ST_L); + DUMPREG(HDMI_TG_HACT_ST_H); + DUMPREG(HDMI_TG_HACT_SZ_L); + DUMPREG(HDMI_TG_HACT_SZ_H); + DUMPREG(HDMI_TG_V_FSZ_L); + DUMPREG(HDMI_TG_V_FSZ_H); + DUMPREG(HDMI_TG_VSYNC_L); + DUMPREG(HDMI_TG_VSYNC_H); + DUMPREG(HDMI_TG_VSYNC2_L); + DUMPREG(HDMI_TG_VSYNC2_H); + DUMPREG(HDMI_TG_VACT_ST_L); + DUMPREG(HDMI_TG_VACT_ST_H); + DUMPREG(HDMI_TG_VACT_SZ_L); + DUMPREG(HDMI_TG_VACT_SZ_H); + DUMPREG(HDMI_TG_FIELD_CHG_L); + DUMPREG(HDMI_TG_FIELD_CHG_H); + DUMPREG(HDMI_TG_VACT_ST2_L); + DUMPREG(HDMI_TG_VACT_ST2_H); + DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L); + DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H); + DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L); + DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H); + DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L); + DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H); + DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L); + DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H); +#undef DUMPREG +} + +static void hdmi_v14_regs_dump(struct hdmi_context *hdata, char *prefix) +{ + int i; + +#define DUMPREG(reg_id) \ + DRM_DEBUG_KMS("%s:" #reg_id " = %08x\n", prefix, \ + readl(hdata->regs + reg_id)) + + DRM_DEBUG_KMS("%s: ---- CONTROL REGISTERS ----\n", prefix); + DUMPREG(HDMI_INTC_CON); + DUMPREG(HDMI_INTC_FLAG); + DUMPREG(HDMI_HPD_STATUS); + DUMPREG(HDMI_INTC_CON_1); + DUMPREG(HDMI_INTC_FLAG_1); + DUMPREG(HDMI_PHY_STATUS_0); + DUMPREG(HDMI_PHY_STATUS_PLL); + DUMPREG(HDMI_PHY_CON_0); DUMPREG(HDMI_PHY_RSTOUT); DUMPREG(HDMI_PHY_VPLL); DUMPREG(HDMI_PHY_CMU); @@ -343,40 +965,93 @@ static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) DUMPREG(HDMI_CON_1); DUMPREG(HDMI_CON_2); DUMPREG(HDMI_SYS_STATUS); - DUMPREG(HDMI_PHY_STATUS); + DUMPREG(HDMI_PHY_STATUS_0); DUMPREG(HDMI_STATUS_EN); DUMPREG(HDMI_HPD); DUMPREG(HDMI_MODE_SEL); - DUMPREG(HDMI_HPD_GEN); + DUMPREG(HDMI_ENC_EN); DUMPREG(HDMI_DC_CONTROL); DUMPREG(HDMI_VIDEO_PATTERN_GEN);
DRM_DEBUG_KMS("%s: ---- CORE SYNC REGISTERS ----\n", prefix); DUMPREG(HDMI_H_BLANK_0); DUMPREG(HDMI_H_BLANK_1); - DUMPREG(HDMI_V_BLANK_0); - DUMPREG(HDMI_V_BLANK_1); - DUMPREG(HDMI_V_BLANK_2); - DUMPREG(HDMI_H_V_LINE_0); - DUMPREG(HDMI_H_V_LINE_1); - DUMPREG(HDMI_H_V_LINE_2); + DUMPREG(HDMI_V2_BLANK_0); + DUMPREG(HDMI_V2_BLANK_1); + DUMPREG(HDMI_V1_BLANK_0); + DUMPREG(HDMI_V1_BLANK_1); + DUMPREG(HDMI_V_LINE_0); + DUMPREG(HDMI_V_LINE_1); + DUMPREG(HDMI_H_LINE_0); + DUMPREG(HDMI_H_LINE_1); + DUMPREG(HDMI_HSYNC_POL); + DUMPREG(HDMI_VSYNC_POL); DUMPREG(HDMI_INT_PRO_MODE); - DUMPREG(HDMI_V_BLANK_F_0); - DUMPREG(HDMI_V_BLANK_F_1); - DUMPREG(HDMI_V_BLANK_F_2); - DUMPREG(HDMI_H_SYNC_GEN_0); - DUMPREG(HDMI_H_SYNC_GEN_1); - DUMPREG(HDMI_H_SYNC_GEN_2); - DUMPREG(HDMI_V_SYNC_GEN_1_0); - DUMPREG(HDMI_V_SYNC_GEN_1_1); - DUMPREG(HDMI_V_SYNC_GEN_1_2); - DUMPREG(HDMI_V_SYNC_GEN_2_0); - DUMPREG(HDMI_V_SYNC_GEN_2_1); - DUMPREG(HDMI_V_SYNC_GEN_2_2); - DUMPREG(HDMI_V_SYNC_GEN_3_0); - DUMPREG(HDMI_V_SYNC_GEN_3_1); - DUMPREG(HDMI_V_SYNC_GEN_3_2); + DUMPREG(HDMI_V_BLANK_F0_0); + DUMPREG(HDMI_V_BLANK_F0_1); + DUMPREG(HDMI_V_BLANK_F1_0); + DUMPREG(HDMI_V_BLANK_F1_1); + + DUMPREG(HDMI_H_SYNC_START_0); + DUMPREG(HDMI_H_SYNC_START_1); + DUMPREG(HDMI_H_SYNC_END_0); + DUMPREG(HDMI_H_SYNC_END_1); + + DUMPREG(HDMI_V_SYNC_LINE_BEF_2_0); + DUMPREG(HDMI_V_SYNC_LINE_BEF_2_1); + DUMPREG(HDMI_V_SYNC_LINE_BEF_1_0); + DUMPREG(HDMI_V_SYNC_LINE_BEF_1_1); + + DUMPREG(HDMI_V_SYNC_LINE_AFT_2_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_2_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_1_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_1_1); + + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_2_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_2_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_1_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_1_1); + + DUMPREG(HDMI_V_BLANK_F2_0); + DUMPREG(HDMI_V_BLANK_F2_1); + DUMPREG(HDMI_V_BLANK_F3_0); + DUMPREG(HDMI_V_BLANK_F3_1); + DUMPREG(HDMI_V_BLANK_F4_0); + DUMPREG(HDMI_V_BLANK_F4_1); + DUMPREG(HDMI_V_BLANK_F5_0); + DUMPREG(HDMI_V_BLANK_F5_1); + + DUMPREG(HDMI_V_SYNC_LINE_AFT_3_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_3_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_4_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_4_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_5_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_5_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_6_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_6_1); + + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_3_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_3_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_4_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_4_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_5_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_5_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_6_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_6_1); + + DUMPREG(HDMI_VACT_SPACE_1_0); + DUMPREG(HDMI_VACT_SPACE_1_1); + DUMPREG(HDMI_VACT_SPACE_2_0); + DUMPREG(HDMI_VACT_SPACE_2_1); + DUMPREG(HDMI_VACT_SPACE_3_0); + DUMPREG(HDMI_VACT_SPACE_3_1); + DUMPREG(HDMI_VACT_SPACE_4_0); + DUMPREG(HDMI_VACT_SPACE_4_1); + DUMPREG(HDMI_VACT_SPACE_5_0); + DUMPREG(HDMI_VACT_SPACE_5_1); + DUMPREG(HDMI_VACT_SPACE_6_0); + DUMPREG(HDMI_VACT_SPACE_6_1);
DRM_DEBUG_KMS("%s: ---- TG REGISTERS ----\n", prefix); DUMPREG(HDMI_TG_CMD); @@ -400,6 +1075,10 @@ static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) DUMPREG(HDMI_TG_FIELD_CHG_H); DUMPREG(HDMI_TG_VACT_ST2_L); DUMPREG(HDMI_TG_VACT_ST2_H); + DUMPREG(HDMI_TG_VACT_ST3_L); + DUMPREG(HDMI_TG_VACT_ST3_H); + DUMPREG(HDMI_TG_VACT_ST4_L); + DUMPREG(HDMI_TG_VACT_ST4_H); DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L); DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H); DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L); @@ -408,10 +1087,49 @@ static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H); DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L); DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H); + DUMPREG(HDMI_TG_3D); + + DRM_DEBUG_KMS("%s: ---- PACKET REGISTERS ----\n", prefix); + DUMPREG(HDMI_AVI_CON); + DUMPREG(HDMI_AVI_HEADER0); + DUMPREG(HDMI_AVI_HEADER1); + DUMPREG(HDMI_AVI_HEADER2); + DUMPREG(HDMI_AVI_CHECK_SUM); + DUMPREG(HDMI_VSI_CON); + DUMPREG(HDMI_VSI_HEADER0); + DUMPREG(HDMI_VSI_HEADER1); + DUMPREG(HDMI_VSI_HEADER2); + for (i = 0; i < 7; ++i) + DUMPREG(HDMI_VSI_DATA(i)); + #undef DUMPREG }
-static int hdmi_conf_index(struct drm_display_mode *mode) +static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) +{ + if (hdata->is_v13) + hdmi_v13_regs_dump(hdata, prefix); + else + hdmi_v14_regs_dump(hdata, prefix); +} + +static int hdmi_v13_conf_index(struct drm_display_mode *mode) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hdmi_v13_confs); ++i) + if (hdmi_v13_confs[i].width == mode->hdisplay && + hdmi_v13_confs[i].height == mode->vdisplay && + hdmi_v13_confs[i].vrefresh == mode->vrefresh && + hdmi_v13_confs[i].interlace == + ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? + true : false)) + return i; + + return -1; +} + +static int hdmi_v14_conf_index(struct drm_display_mode *mode) { int i;
@@ -427,6 +1145,15 @@ static int hdmi_conf_index(struct drm_display_mode *mode) return -1; }
+static int hdmi_conf_index(struct hdmi_context *hdata, + struct drm_display_mode *mode) +{ + if (hdata->is_v13) + return hdmi_v13_conf_index(mode); + else + return hdmi_v14_conf_index(mode); +} + static bool hdmi_is_connected(void *ctx) { struct hdmi_context *hdata = (struct hdmi_context *)ctx; @@ -462,16 +1189,25 @@ static int hdmi_get_edid(void *ctx, struct drm_connector *connector, return 0; }
-static int hdmi_check_timing(void *ctx, void *timing) +static int hdmi_v13_check_timing(struct fb_videomode *check_timing) { - struct fb_videomode *check_timing = timing; int i;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + for (i = 0; i < ARRAY_SIZE(hdmi_v13_confs); ++i) + if (hdmi_v13_confs[i].width == check_timing->xres && + hdmi_v13_confs[i].height == check_timing->yres && + hdmi_v13_confs[i].vrefresh == check_timing->refresh && + hdmi_v13_confs[i].interlace == + ((check_timing->vmode & FB_VMODE_INTERLACED) ? + true : false)) + return 0;
- DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", check_timing->xres, - check_timing->yres, check_timing->refresh, - check_timing->vmode); + return -EINVAL; +} + +static int hdmi_v14_check_timing(struct fb_videomode *check_timing) +{ + int i;
for (i = 0; i < ARRAY_SIZE(hdmi_confs); ++i) if (hdmi_confs[i].width == check_timing->xres && @@ -485,6 +1221,23 @@ static int hdmi_check_timing(void *ctx, void *timing) return -EINVAL; }
+static int hdmi_check_timing(void *ctx, void *timing) +{ + struct hdmi_context *hdata = (struct hdmi_context *)ctx; + struct fb_videomode *check_timing = timing; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", check_timing->xres, + check_timing->yres, check_timing->refresh, + check_timing->vmode); + + if (hdata->is_v13) + return hdmi_v13_check_timing(check_timing); + else + return hdmi_v14_check_timing(check_timing); +} + static int hdmi_display_power_on(void *ctx, int mode) { DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); @@ -516,13 +1269,20 @@ static struct exynos_hdmi_display_ops display_ops = {
static void hdmi_conf_reset(struct hdmi_context *hdata) { + u32 reg; + /* disable hpd handle for drm */ hdata->hpd_handle = false;
+ if (hdata->is_v13) + reg = HDMI_V13_CORE_RSTOUT; + else + reg = HDMI_CORE_RSTOUT; + /* resetting HDMI core */ - hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, HDMI_CORE_SW_RSTOUT); + hdmi_reg_writemask(hdata, reg, 0, HDMI_CORE_SW_RSTOUT); mdelay(10); - hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, HDMI_CORE_SW_RSTOUT); + hdmi_reg_writemask(hdata, reg, ~0, HDMI_CORE_SW_RSTOUT); mdelay(10);
/* enable hpd handle for drm */ @@ -546,27 +1306,126 @@ static void hdmi_conf_init(struct hdmi_context *hdata) HDMI_MODE_HDMI_EN, HDMI_MODE_MASK); /* disable bluescreen */ hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN); - /* choose bluescreen (fecal) color */ - hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_0, 0x12); - hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_1, 0x34); - hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_2, 0x56); - /* enable AVI packet every vsync, fixes purple line problem */ - hdmi_reg_writeb(hdata, HDMI_AVI_CON, 0x02); - /* force RGB, look to CEA-861-D, table 7 for more detail */ - hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(0), 0 << 5); - hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5); - - hdmi_reg_writeb(hdata, HDMI_SPD_CON, 0x02); - hdmi_reg_writeb(hdata, HDMI_AUI_CON, 0x02); - hdmi_reg_writeb(hdata, HDMI_ACR_CON, 0x04); + + if (hdata->is_v13) { + /* choose bluescreen (fecal) color */ + hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12); + hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34); + hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_2, 0x56); + + /* enable AVI packet every vsync, fixes purple line problem */ + hdmi_reg_writeb(hdata, HDMI_V13_AVI_CON, 0x02); + /* force RGB, look to CEA-861-D, table 7 for more detail */ + hdmi_reg_writeb(hdata, HDMI_V13_AVI_BYTE(0), 0 << 5); + hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5); + + hdmi_reg_writeb(hdata, HDMI_V13_SPD_CON, 0x02); + hdmi_reg_writeb(hdata, HDMI_V13_AUI_CON, 0x02); + hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04); + } else { + /* enable AVI packet every vsync, fixes purple line problem */ + hdmi_reg_writeb(hdata, HDMI_AVI_CON, 0x02); + hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(1), 2 << 5); + hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5); + }
/* enable hpd handle for drm */ hdata->hpd_handle = true; }
-static void hdmi_timing_apply(struct hdmi_context *hdata, - const struct hdmi_preset_conf *conf) +static void hdmi_v13_timing_apply(struct hdmi_context *hdata) { + const struct hdmi_v13_preset_conf *conf = + hdmi_v13_confs[hdata->cur_conf].conf; + const struct hdmi_v13_core_regs *core = &conf->core; + const struct hdmi_v13_tg_regs *tg = &conf->tg; + int tries; + + /* setting core registers */ + hdmi_reg_writeb(hdata, HDMI_H_BLANK_0, core->h_blank[0]); + hdmi_reg_writeb(hdata, HDMI_H_BLANK_1, core->h_blank[1]); + hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_0, core->v_blank[0]); + hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_1, core->v_blank[1]); + hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_2, core->v_blank[2]); + hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_0, core->h_v_line[0]); + hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_1, core->h_v_line[1]); + hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_2, core->h_v_line[2]); + hdmi_reg_writeb(hdata, HDMI_VSYNC_POL, core->vsync_pol[0]); + hdmi_reg_writeb(hdata, HDMI_INT_PRO_MODE, core->int_pro_mode[0]); + hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_0, core->v_blank_f[0]); + hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_1, core->v_blank_f[1]); + hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_2, core->v_blank_f[2]); + hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_0, core->h_sync_gen[0]); + hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_1, core->h_sync_gen[1]); + hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_2, core->h_sync_gen[2]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_0, core->v_sync_gen1[0]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_1, core->v_sync_gen1[1]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_2, core->v_sync_gen1[2]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_0, core->v_sync_gen2[0]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_1, core->v_sync_gen2[1]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_2, core->v_sync_gen2[2]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_0, core->v_sync_gen3[0]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_1, core->v_sync_gen3[1]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_2, core->v_sync_gen3[2]); + /* Timing generator registers */ + hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz_l); + hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz_h); + hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_L, tg->hact_st_l); + hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_H, tg->hact_st_h); + hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_L, tg->hact_sz_l); + hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_H, tg->hact_sz_h); + hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_L, tg->v_fsz_l); + hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_H, tg->v_fsz_h); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_L, tg->vsync_l); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_H, tg->vsync_h); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_L, tg->vsync2_l); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_H, tg->vsync2_h); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_L, tg->vact_st_l); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_H, tg->vact_st_h); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_L, tg->vact_sz_l); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_H, tg->vact_sz_h); + hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_L, tg->field_chg_l); + hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg_h); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2_l); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2_h); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi_h); + hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi_l); + hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h); + hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l); + hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h); + + /* waiting for HDMIPHY's PLL to get to steady state */ + for (tries = 100; tries; --tries) { + u32 val = hdmi_reg_read(hdata, HDMI_V13_PHY_STATUS); + if (val & HDMI_PHY_STATUS_READY) + break; + mdelay(1); + } + /* steady state not achieved */ + if (tries == 0) { + DRM_ERROR("hdmiphy's pll could not reach steady state.\n"); + hdmi_regs_dump(hdata, "timing apply"); + } + + clk_disable(hdata->res.sclk_hdmi); + clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_hdmiphy); + clk_enable(hdata->res.sclk_hdmi); + + /* enable HDMI and timing generator */ + hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN); + if (core->int_pro_mode[0]) + hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN | + HDMI_FIELD_EN); + else + hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN); +} + +static void hdmi_v14_timing_apply(struct hdmi_context *hdata) +{ + const struct hdmi_preset_conf *conf = hdmi_confs[hdata->cur_conf].conf; const struct hdmi_core_regs *core = &conf->core; const struct hdmi_tg_regs *tg = &conf->tg; int tries; @@ -574,29 +1433,102 @@ static void hdmi_timing_apply(struct hdmi_context *hdata, /* setting core registers */ hdmi_reg_writeb(hdata, HDMI_H_BLANK_0, core->h_blank[0]); hdmi_reg_writeb(hdata, HDMI_H_BLANK_1, core->h_blank[1]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_0, core->v_blank[0]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_1, core->v_blank[1]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_2, core->v_blank[2]); - hdmi_reg_writeb(hdata, HDMI_H_V_LINE_0, core->h_v_line[0]); - hdmi_reg_writeb(hdata, HDMI_H_V_LINE_1, core->h_v_line[1]); - hdmi_reg_writeb(hdata, HDMI_H_V_LINE_2, core->h_v_line[2]); + hdmi_reg_writeb(hdata, HDMI_V2_BLANK_0, core->v2_blank[0]); + hdmi_reg_writeb(hdata, HDMI_V2_BLANK_1, core->v2_blank[1]); + hdmi_reg_writeb(hdata, HDMI_V1_BLANK_0, core->v1_blank[0]); + hdmi_reg_writeb(hdata, HDMI_V1_BLANK_1, core->v1_blank[1]); + hdmi_reg_writeb(hdata, HDMI_V_LINE_0, core->v_line[0]); + hdmi_reg_writeb(hdata, HDMI_V_LINE_1, core->v_line[1]); + hdmi_reg_writeb(hdata, HDMI_H_LINE_0, core->h_line[0]); + hdmi_reg_writeb(hdata, HDMI_H_LINE_1, core->h_line[1]); + hdmi_reg_writeb(hdata, HDMI_HSYNC_POL, core->hsync_pol[0]); hdmi_reg_writeb(hdata, HDMI_VSYNC_POL, core->vsync_pol[0]); hdmi_reg_writeb(hdata, HDMI_INT_PRO_MODE, core->int_pro_mode[0]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_0, core->v_blank_f[0]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_1, core->v_blank_f[1]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_2, core->v_blank_f[2]); - hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_0, core->h_sync_gen[0]); - hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_1, core->h_sync_gen[1]); - hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_2, core->h_sync_gen[2]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_0, core->v_sync_gen1[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_1, core->v_sync_gen1[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_2, core->v_sync_gen1[2]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_0, core->v_sync_gen2[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_1, core->v_sync_gen2[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_2, core->v_sync_gen2[2]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_0, core->v_sync_gen3[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_1, core->v_sync_gen3[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_2, core->v_sync_gen3[2]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F0_0, core->v_blank_f0[0]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F0_1, core->v_blank_f0[1]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F1_0, core->v_blank_f1[0]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F1_1, core->v_blank_f1[1]); + hdmi_reg_writeb(hdata, HDMI_H_SYNC_START_0, core->h_sync_start[0]); + hdmi_reg_writeb(hdata, HDMI_H_SYNC_START_1, core->h_sync_start[1]); + hdmi_reg_writeb(hdata, HDMI_H_SYNC_END_0, core->h_sync_end[0]); + hdmi_reg_writeb(hdata, HDMI_H_SYNC_END_1, core->h_sync_end[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_2_0, + core->v_sync_line_bef_2[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_2_1, + core->v_sync_line_bef_2[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_1_0, + core->v_sync_line_bef_1[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_1_1, + core->v_sync_line_bef_1[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_2_0, + core->v_sync_line_aft_2[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_2_1, + core->v_sync_line_aft_2[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_1_0, + core->v_sync_line_aft_1[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_1_1, + core->v_sync_line_aft_1[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, + core->v_sync_line_aft_pxl_2[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_1, + core->v_sync_line_aft_pxl_2[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, + core->v_sync_line_aft_pxl_1[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_1, + core->v_sync_line_aft_pxl_1[1]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F2_0, core->v_blank_f2[0]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F2_1, core->v_blank_f2[1]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F3_0, core->v_blank_f3[0]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F3_1, core->v_blank_f3[1]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F4_0, core->v_blank_f4[0]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F4_1, core->v_blank_f4[1]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F5_0, core->v_blank_f5[0]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F5_1, core->v_blank_f5[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_3_0, + core->v_sync_line_aft_3[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_3_1, + core->v_sync_line_aft_3[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_4_0, + core->v_sync_line_aft_4[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_4_1, + core->v_sync_line_aft_4[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_5_0, + core->v_sync_line_aft_5[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_5_1, + core->v_sync_line_aft_5[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_6_0, + core->v_sync_line_aft_6[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_6_1, + core->v_sync_line_aft_6[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0, + core->v_sync_line_aft_pxl_3[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_1, + core->v_sync_line_aft_pxl_3[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0, + core->v_sync_line_aft_pxl_4[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_1, + core->v_sync_line_aft_pxl_4[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0, + core->v_sync_line_aft_pxl_5[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_1, + core->v_sync_line_aft_pxl_5[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, + core->v_sync_line_aft_pxl_6[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_1, + core->v_sync_line_aft_pxl_6[1]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_1_0, core->vact_space_1[0]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_1_1, core->vact_space_1[1]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_2_0, core->vact_space_2[0]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_2_1, core->vact_space_2[1]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_3_0, core->vact_space_3[0]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_3_1, core->vact_space_3[1]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_4_0, core->vact_space_4[0]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_4_1, core->vact_space_4[1]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_5_0, core->vact_space_5[0]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_5_1, core->vact_space_5[1]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_6_0, core->vact_space_6[0]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_6_1, core->vact_space_6[1]); + /* Timing generator registers */ hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz_l); hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz_h); @@ -618,6 +1550,10 @@ static void hdmi_timing_apply(struct hdmi_context *hdata, hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg_h); hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2_l); hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2_h); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST3_L, tg->vact_st3_l); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST3_H, tg->vact_st3_h); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST4_L, tg->vact_st4_l); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST4_H, tg->vact_st4_h); hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l); hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h); hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l); @@ -626,10 +1562,11 @@ static void hdmi_timing_apply(struct hdmi_context *hdata, hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h); hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l); hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h); + hdmi_reg_writeb(hdata, HDMI_TG_3D, tg->tg_3d);
/* waiting for HDMIPHY's PLL to get to steady state */ for (tries = 100; tries; --tries) { - u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS); + u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS_0); if (val & HDMI_PHY_STATUS_READY) break; mdelay(1); @@ -653,9 +1590,18 @@ static void hdmi_timing_apply(struct hdmi_context *hdata, hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN); }
+static void hdmi_timing_apply(struct hdmi_context *hdata) +{ + if (hdata->is_v13) + hdmi_v13_timing_apply(hdata); + else + hdmi_v14_timing_apply(hdata); +} + static void hdmiphy_conf_reset(struct hdmi_context *hdata) { u8 buffer[2]; + u32 reg;
clk_disable(hdata->res.sclk_hdmi); clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_pixel); @@ -668,15 +1614,21 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata) if (hdata->hdmiphy_port) i2c_master_send(hdata->hdmiphy_port, buffer, 2);
+ if (hdata->is_v13) + reg = HDMI_V13_PHY_RSTOUT; + else + reg = HDMI_PHY_RSTOUT; + /* reset hdmiphy */ - hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT); + hdmi_reg_writemask(hdata, reg, ~0, HDMI_PHY_SW_RSTOUT); mdelay(10); - hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT); + hdmi_reg_writemask(hdata, reg, 0, HDMI_PHY_SW_RSTOUT); mdelay(10); }
static void hdmiphy_conf_apply(struct hdmi_context *hdata) { + const u8 *hdmiphy_data; u8 buffer[32]; u8 operation[2]; u8 read_buffer[32] = {0, }; @@ -689,7 +1641,12 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata) }
/* pixel clock */ - memcpy(buffer, hdmi_confs[hdata->cur_conf].hdmiphy_data, 32); + if (hdata->is_v13) + hdmiphy_data = hdmi_v13_confs[hdata->cur_conf].hdmiphy_data; + else + hdmiphy_data = hdmi_confs[hdata->cur_conf].hdmiphy_data; + + memcpy(buffer, hdmiphy_data, 32); ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32); if (ret != 32) { DRM_ERROR("failed to configure HDMIPHY via I2C\n"); @@ -721,9 +1678,6 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata)
static void hdmi_conf_apply(struct hdmi_context *hdata) { - const struct hdmi_preset_conf *conf = - hdmi_confs[hdata->cur_conf].conf; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
hdmiphy_conf_reset(hdata); @@ -733,7 +1687,7 @@ static void hdmi_conf_apply(struct hdmi_context *hdata) hdmi_conf_init(hdata);
/* setting core registers */ - hdmi_timing_apply(hdata, conf); + hdmi_timing_apply(hdata);
hdmi_regs_dump(hdata, "start"); } @@ -745,8 +1699,8 @@ static void hdmi_mode_set(void *ctx, void *mode)
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
- conf_idx = hdmi_conf_index(mode); - if (conf_idx >= 0 && conf_idx < ARRAY_SIZE(hdmi_confs)) + conf_idx = hdmi_conf_index(hdata, mode); + if (conf_idx >= 0) hdata->cur_conf = conf_idx; else DRM_DEBUG_KMS("not supported mode\n"); @@ -926,7 +1880,6 @@ static void hdmi_resource_poweron(struct hdmi_context *hdata) hdmiphy_conf_reset(hdata); hdmi_conf_reset(hdata); hdmi_conf_init(hdata); - }
static void hdmi_resource_poweroff(struct hdmi_context *hdata) @@ -1022,6 +1975,7 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, drm_hdmi_ctx);
+ hdata->is_v13 = pdata->is_v13; hdata->default_win = pdata->default_win; hdata->default_timing = &pdata->timing; hdata->default_bpp = pdata->bpp; diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.h b/drivers/gpu/drm/exynos/exynos_hdmi.h index 31d6cf8..040ecad 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.h +++ b/drivers/gpu/drm/exynos/exynos_hdmi.h @@ -28,15 +28,6 @@ #ifndef _EXYNOS_HDMI_H_ #define _EXYNOS_HDMI_H_
-struct hdmi_conf { - int width; - int height; - int vrefresh; - bool interlace; - const u8 *hdmiphy_data; - const struct hdmi_preset_conf *conf; -}; - struct hdmi_resources { struct clk *hdmi; struct clk *sclk_hdmi; @@ -51,6 +42,7 @@ struct hdmi_context { struct device *dev; struct drm_device *drm_dev; struct fb_videomode *default_timing; + unsigned int is_v13:1; unsigned int default_win; unsigned int default_bpp; bool hpd_handle; diff --git a/drivers/gpu/drm/exynos/regs-hdmi.h b/drivers/gpu/drm/exynos/regs-hdmi.h index 72e6b52..6b28715 100644 --- a/drivers/gpu/drm/exynos/regs-hdmi.h +++ b/drivers/gpu/drm/exynos/regs-hdmi.h @@ -19,6 +19,7 @@ * Register part */
+/* HDMI Version 1.3 & Common */ #define HDMI_CTRL_BASE(x) ((x) + 0x00000000) #define HDMI_CORE_BASE(x) ((x) + 0x00010000) #define HDMI_TG_BASE(x) ((x) + 0x00050000) @@ -27,56 +28,57 @@ #define HDMI_INTC_CON HDMI_CTRL_BASE(0x0000) #define HDMI_INTC_FLAG HDMI_CTRL_BASE(0x0004) #define HDMI_HPD_STATUS HDMI_CTRL_BASE(0x000C) -#define HDMI_PHY_RSTOUT HDMI_CTRL_BASE(0x0014) -#define HDMI_PHY_VPLL HDMI_CTRL_BASE(0x0018) -#define HDMI_PHY_CMU HDMI_CTRL_BASE(0x001C) -#define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0020) +#define HDMI_V13_PHY_RSTOUT HDMI_CTRL_BASE(0x0014) +#define HDMI_V13_PHY_VPLL HDMI_CTRL_BASE(0x0018) +#define HDMI_V13_PHY_CMU HDMI_CTRL_BASE(0x001C) +#define HDMI_V13_CORE_RSTOUT HDMI_CTRL_BASE(0x0020)
/* Core registers */ #define HDMI_CON_0 HDMI_CORE_BASE(0x0000) #define HDMI_CON_1 HDMI_CORE_BASE(0x0004) #define HDMI_CON_2 HDMI_CORE_BASE(0x0008) #define HDMI_SYS_STATUS HDMI_CORE_BASE(0x0010) -#define HDMI_PHY_STATUS HDMI_CORE_BASE(0x0014) +#define HDMI_V13_PHY_STATUS HDMI_CORE_BASE(0x0014) #define HDMI_STATUS_EN HDMI_CORE_BASE(0x0020) #define HDMI_HPD HDMI_CORE_BASE(0x0030) #define HDMI_MODE_SEL HDMI_CORE_BASE(0x0040) -#define HDMI_BLUE_SCREEN_0 HDMI_CORE_BASE(0x0050) -#define HDMI_BLUE_SCREEN_1 HDMI_CORE_BASE(0x0054) -#define HDMI_BLUE_SCREEN_2 HDMI_CORE_BASE(0x0058) +#define HDMI_ENC_EN HDMI_CORE_BASE(0x0044) +#define HDMI_V13_BLUE_SCREEN_0 HDMI_CORE_BASE(0x0050) +#define HDMI_V13_BLUE_SCREEN_1 HDMI_CORE_BASE(0x0054) +#define HDMI_V13_BLUE_SCREEN_2 HDMI_CORE_BASE(0x0058) #define HDMI_H_BLANK_0 HDMI_CORE_BASE(0x00A0) #define HDMI_H_BLANK_1 HDMI_CORE_BASE(0x00A4) -#define HDMI_V_BLANK_0 HDMI_CORE_BASE(0x00B0) -#define HDMI_V_BLANK_1 HDMI_CORE_BASE(0x00B4) -#define HDMI_V_BLANK_2 HDMI_CORE_BASE(0x00B8) -#define HDMI_H_V_LINE_0 HDMI_CORE_BASE(0x00C0) -#define HDMI_H_V_LINE_1 HDMI_CORE_BASE(0x00C4) -#define HDMI_H_V_LINE_2 HDMI_CORE_BASE(0x00C8) +#define HDMI_V13_V_BLANK_0 HDMI_CORE_BASE(0x00B0) +#define HDMI_V13_V_BLANK_1 HDMI_CORE_BASE(0x00B4) +#define HDMI_V13_V_BLANK_2 HDMI_CORE_BASE(0x00B8) +#define HDMI_V13_H_V_LINE_0 HDMI_CORE_BASE(0x00C0) +#define HDMI_V13_H_V_LINE_1 HDMI_CORE_BASE(0x00C4) +#define HDMI_V13_H_V_LINE_2 HDMI_CORE_BASE(0x00C8) #define HDMI_VSYNC_POL HDMI_CORE_BASE(0x00E4) #define HDMI_INT_PRO_MODE HDMI_CORE_BASE(0x00E8) -#define HDMI_V_BLANK_F_0 HDMI_CORE_BASE(0x0110) -#define HDMI_V_BLANK_F_1 HDMI_CORE_BASE(0x0114) -#define HDMI_V_BLANK_F_2 HDMI_CORE_BASE(0x0118) -#define HDMI_H_SYNC_GEN_0 HDMI_CORE_BASE(0x0120) -#define HDMI_H_SYNC_GEN_1 HDMI_CORE_BASE(0x0124) -#define HDMI_H_SYNC_GEN_2 HDMI_CORE_BASE(0x0128) -#define HDMI_V_SYNC_GEN_1_0 HDMI_CORE_BASE(0x0130) -#define HDMI_V_SYNC_GEN_1_1 HDMI_CORE_BASE(0x0134) -#define HDMI_V_SYNC_GEN_1_2 HDMI_CORE_BASE(0x0138) -#define HDMI_V_SYNC_GEN_2_0 HDMI_CORE_BASE(0x0140) -#define HDMI_V_SYNC_GEN_2_1 HDMI_CORE_BASE(0x0144) -#define HDMI_V_SYNC_GEN_2_2 HDMI_CORE_BASE(0x0148) -#define HDMI_V_SYNC_GEN_3_0 HDMI_CORE_BASE(0x0150) -#define HDMI_V_SYNC_GEN_3_1 HDMI_CORE_BASE(0x0154) -#define HDMI_V_SYNC_GEN_3_2 HDMI_CORE_BASE(0x0158) -#define HDMI_ACR_CON HDMI_CORE_BASE(0x0180) -#define HDMI_AVI_CON HDMI_CORE_BASE(0x0300) -#define HDMI_AVI_BYTE(n) HDMI_CORE_BASE(0x0320 + 4 * (n)) -#define HDMI_DC_CONTROL HDMI_CORE_BASE(0x05C0) -#define HDMI_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x05C4) -#define HDMI_HPD_GEN HDMI_CORE_BASE(0x05C8) -#define HDMI_AUI_CON HDMI_CORE_BASE(0x0360) -#define HDMI_SPD_CON HDMI_CORE_BASE(0x0400) +#define HDMI_V13_V_BLANK_F_0 HDMI_CORE_BASE(0x0110) +#define HDMI_V13_V_BLANK_F_1 HDMI_CORE_BASE(0x0114) +#define HDMI_V13_V_BLANK_F_2 HDMI_CORE_BASE(0x0118) +#define HDMI_V13_H_SYNC_GEN_0 HDMI_CORE_BASE(0x0120) +#define HDMI_V13_H_SYNC_GEN_1 HDMI_CORE_BASE(0x0124) +#define HDMI_V13_H_SYNC_GEN_2 HDMI_CORE_BASE(0x0128) +#define HDMI_V13_V_SYNC_GEN_1_0 HDMI_CORE_BASE(0x0130) +#define HDMI_V13_V_SYNC_GEN_1_1 HDMI_CORE_BASE(0x0134) +#define HDMI_V13_V_SYNC_GEN_1_2 HDMI_CORE_BASE(0x0138) +#define HDMI_V13_V_SYNC_GEN_2_0 HDMI_CORE_BASE(0x0140) +#define HDMI_V13_V_SYNC_GEN_2_1 HDMI_CORE_BASE(0x0144) +#define HDMI_V13_V_SYNC_GEN_2_2 HDMI_CORE_BASE(0x0148) +#define HDMI_V13_V_SYNC_GEN_3_0 HDMI_CORE_BASE(0x0150) +#define HDMI_V13_V_SYNC_GEN_3_1 HDMI_CORE_BASE(0x0154) +#define HDMI_V13_V_SYNC_GEN_3_2 HDMI_CORE_BASE(0x0158) +#define HDMI_V13_ACR_CON HDMI_CORE_BASE(0x0180) +#define HDMI_V13_AVI_CON HDMI_CORE_BASE(0x0300) +#define HDMI_V13_AVI_BYTE(n) HDMI_CORE_BASE(0x0320 + 4 * (n)) +#define HDMI_V13_DC_CONTROL HDMI_CORE_BASE(0x05C0) +#define HDMI_V13_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x05C4) +#define HDMI_V13_HPD_GEN HDMI_CORE_BASE(0x05C8) +#define HDMI_V13_AUI_CON HDMI_CORE_BASE(0x0360) +#define HDMI_V13_SPD_CON HDMI_CORE_BASE(0x0400)
/* Timing generator registers */ #define HDMI_TG_CMD HDMI_TG_BASE(0x0000) @@ -144,4 +146,234 @@ #define HDMI_TG_EN (1 << 0) #define HDMI_FIELD_EN (1 << 1)
+ +/* HDMI Version 1.4 */ +/* Control registers */ +/* #define HDMI_INTC_CON HDMI_CTRL_BASE(0x0000) */ +/* #define HDMI_INTC_FLAG HDMI_CTRL_BASE(0x0004) */ +#define HDMI_HDCP_KEY_LOAD HDMI_CTRL_BASE(0x0008) +/* #define HDMI_HPD_STATUS HDMI_CTRL_BASE(0x000C) */ +#define HDMI_INTC_CON_1 HDMI_CTRL_BASE(0x0010) +#define HDMI_INTC_FLAG_1 HDMI_CTRL_BASE(0x0014) +#define HDMI_PHY_STATUS_0 HDMI_CTRL_BASE(0x0020) +#define HDMI_PHY_STATUS_CMU HDMI_CTRL_BASE(0x0024) +#define HDMI_PHY_STATUS_PLL HDMI_CTRL_BASE(0x0028) +#define HDMI_PHY_CON_0 HDMI_CTRL_BASE(0x0030) +#define HDMI_HPD_CTRL HDMI_CTRL_BASE(0x0040) +#define HDMI_HPD_ST HDMI_CTRL_BASE(0x0044) +#define HDMI_HPD_TH_X HDMI_CTRL_BASE(0x0050) +#define HDMI_AUDIO_CLKSEL HDMI_CTRL_BASE(0x0070) +#define HDMI_PHY_RSTOUT HDMI_CTRL_BASE(0x0074) +#define HDMI_PHY_VPLL HDMI_CTRL_BASE(0x0078) +#define HDMI_PHY_CMU HDMI_CTRL_BASE(0x007C) +#define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0080) + +/* Video related registers */ +#define HDMI_YMAX HDMI_CORE_BASE(0x0060) +#define HDMI_YMIN HDMI_CORE_BASE(0x0064) +#define HDMI_CMAX HDMI_CORE_BASE(0x0068) +#define HDMI_CMIN HDMI_CORE_BASE(0x006C) + +#define HDMI_V2_BLANK_0 HDMI_CORE_BASE(0x00B0) +#define HDMI_V2_BLANK_1 HDMI_CORE_BASE(0x00B4) +#define HDMI_V1_BLANK_0 HDMI_CORE_BASE(0x00B8) +#define HDMI_V1_BLANK_1 HDMI_CORE_BASE(0x00BC) + +#define HDMI_V_LINE_0 HDMI_CORE_BASE(0x00C0) +#define HDMI_V_LINE_1 HDMI_CORE_BASE(0x00C4) +#define HDMI_H_LINE_0 HDMI_CORE_BASE(0x00C8) +#define HDMI_H_LINE_1 HDMI_CORE_BASE(0x00CC) + +#define HDMI_HSYNC_POL HDMI_CORE_BASE(0x00E0) + +#define HDMI_V_BLANK_F0_0 HDMI_CORE_BASE(0x0110) +#define HDMI_V_BLANK_F0_1 HDMI_CORE_BASE(0x0114) +#define HDMI_V_BLANK_F1_0 HDMI_CORE_BASE(0x0118) +#define HDMI_V_BLANK_F1_1 HDMI_CORE_BASE(0x011C) + +#define HDMI_H_SYNC_START_0 HDMI_CORE_BASE(0x0120) +#define HDMI_H_SYNC_START_1 HDMI_CORE_BASE(0x0124) +#define HDMI_H_SYNC_END_0 HDMI_CORE_BASE(0x0128) +#define HDMI_H_SYNC_END_1 HDMI_CORE_BASE(0x012C) + +#define HDMI_V_SYNC_LINE_BEF_2_0 HDMI_CORE_BASE(0x0130) +#define HDMI_V_SYNC_LINE_BEF_2_1 HDMI_CORE_BASE(0x0134) +#define HDMI_V_SYNC_LINE_BEF_1_0 HDMI_CORE_BASE(0x0138) +#define HDMI_V_SYNC_LINE_BEF_1_1 HDMI_CORE_BASE(0x013C) + +#define HDMI_V_SYNC_LINE_AFT_2_0 HDMI_CORE_BASE(0x0140) +#define HDMI_V_SYNC_LINE_AFT_2_1 HDMI_CORE_BASE(0x0144) +#define HDMI_V_SYNC_LINE_AFT_1_0 HDMI_CORE_BASE(0x0148) +#define HDMI_V_SYNC_LINE_AFT_1_1 HDMI_CORE_BASE(0x014C) + +#define HDMI_V_SYNC_LINE_AFT_PXL_2_0 HDMI_CORE_BASE(0x0150) +#define HDMI_V_SYNC_LINE_AFT_PXL_2_1 HDMI_CORE_BASE(0x0154) +#define HDMI_V_SYNC_LINE_AFT_PXL_1_0 HDMI_CORE_BASE(0x0158) +#define HDMI_V_SYNC_LINE_AFT_PXL_1_1 HDMI_CORE_BASE(0x015C) + +#define HDMI_V_BLANK_F2_0 HDMI_CORE_BASE(0x0160) +#define HDMI_V_BLANK_F2_1 HDMI_CORE_BASE(0x0164) +#define HDMI_V_BLANK_F3_0 HDMI_CORE_BASE(0x0168) +#define HDMI_V_BLANK_F3_1 HDMI_CORE_BASE(0x016C) +#define HDMI_V_BLANK_F4_0 HDMI_CORE_BASE(0x0170) +#define HDMI_V_BLANK_F4_1 HDMI_CORE_BASE(0x0174) +#define HDMI_V_BLANK_F5_0 HDMI_CORE_BASE(0x0178) +#define HDMI_V_BLANK_F5_1 HDMI_CORE_BASE(0x017C) + +#define HDMI_V_SYNC_LINE_AFT_3_0 HDMI_CORE_BASE(0x0180) +#define HDMI_V_SYNC_LINE_AFT_3_1 HDMI_CORE_BASE(0x0184) +#define HDMI_V_SYNC_LINE_AFT_4_0 HDMI_CORE_BASE(0x0188) +#define HDMI_V_SYNC_LINE_AFT_4_1 HDMI_CORE_BASE(0x018C) +#define HDMI_V_SYNC_LINE_AFT_5_0 HDMI_CORE_BASE(0x0190) +#define HDMI_V_SYNC_LINE_AFT_5_1 HDMI_CORE_BASE(0x0194) +#define HDMI_V_SYNC_LINE_AFT_6_0 HDMI_CORE_BASE(0x0198) +#define HDMI_V_SYNC_LINE_AFT_6_1 HDMI_CORE_BASE(0x019C) + +#define HDMI_V_SYNC_LINE_AFT_PXL_3_0 HDMI_CORE_BASE(0x01A0) +#define HDMI_V_SYNC_LINE_AFT_PXL_3_1 HDMI_CORE_BASE(0x01A4) +#define HDMI_V_SYNC_LINE_AFT_PXL_4_0 HDMI_CORE_BASE(0x01A8) +#define HDMI_V_SYNC_LINE_AFT_PXL_4_1 HDMI_CORE_BASE(0x01AC) +#define HDMI_V_SYNC_LINE_AFT_PXL_5_0 HDMI_CORE_BASE(0x01B0) +#define HDMI_V_SYNC_LINE_AFT_PXL_5_1 HDMI_CORE_BASE(0x01B4) +#define HDMI_V_SYNC_LINE_AFT_PXL_6_0 HDMI_CORE_BASE(0x01B8) +#define HDMI_V_SYNC_LINE_AFT_PXL_6_1 HDMI_CORE_BASE(0x01BC) + +#define HDMI_VACT_SPACE_1_0 HDMI_CORE_BASE(0x01C0) +#define HDMI_VACT_SPACE_1_1 HDMI_CORE_BASE(0x01C4) +#define HDMI_VACT_SPACE_2_0 HDMI_CORE_BASE(0x01C8) +#define HDMI_VACT_SPACE_2_1 HDMI_CORE_BASE(0x01CC) +#define HDMI_VACT_SPACE_3_0 HDMI_CORE_BASE(0x01D0) +#define HDMI_VACT_SPACE_3_1 HDMI_CORE_BASE(0x01D4) +#define HDMI_VACT_SPACE_4_0 HDMI_CORE_BASE(0x01D8) +#define HDMI_VACT_SPACE_4_1 HDMI_CORE_BASE(0x01DC) +#define HDMI_VACT_SPACE_5_0 HDMI_CORE_BASE(0x01E0) +#define HDMI_VACT_SPACE_5_1 HDMI_CORE_BASE(0x01E4) +#define HDMI_VACT_SPACE_6_0 HDMI_CORE_BASE(0x01E8) +#define HDMI_VACT_SPACE_6_1 HDMI_CORE_BASE(0x01EC) + +#define HDMI_GCP_CON HDMI_CORE_BASE(0x0200) +#define HDMI_GCP_BYTE1 HDMI_CORE_BASE(0x0210) +#define HDMI_GCP_BYTE2 HDMI_CORE_BASE(0x0214) +#define HDMI_GCP_BYTE3 HDMI_CORE_BASE(0x0218) + +/* Audio related registers */ +#define HDMI_ASP_CON HDMI_CORE_BASE(0x0300) +#define HDMI_ASP_SP_FLAT HDMI_CORE_BASE(0x0304) +#define HDMI_ASP_CHCFG0 HDMI_CORE_BASE(0x0310) +#define HDMI_ASP_CHCFG1 HDMI_CORE_BASE(0x0314) +#define HDMI_ASP_CHCFG2 HDMI_CORE_BASE(0x0318) +#define HDMI_ASP_CHCFG3 HDMI_CORE_BASE(0x031C) + +#define HDMI_ACR_CON HDMI_CORE_BASE(0x0400) +#define HDMI_ACR_MCTS0 HDMI_CORE_BASE(0x0410) +#define HDMI_ACR_MCTS1 HDMI_CORE_BASE(0x0414) +#define HDMI_ACR_MCTS2 HDMI_CORE_BASE(0x0418) +#define HDMI_ACR_N0 HDMI_CORE_BASE(0x0430) +#define HDMI_ACR_N1 HDMI_CORE_BASE(0x0434) +#define HDMI_ACR_N2 HDMI_CORE_BASE(0x0438) + +/* Packet related registers */ +#define HDMI_ACP_CON HDMI_CORE_BASE(0x0500) +#define HDMI_ACP_TYPE HDMI_CORE_BASE(0x0514) +#define HDMI_ACP_DATA(n) HDMI_CORE_BASE(0x0520 + 4 * (n)) + +#define HDMI_ISRC_CON HDMI_CORE_BASE(0x0600) +#define HDMI_ISRC1_HEADER1 HDMI_CORE_BASE(0x0614) +#define HDMI_ISRC1_DATA(n) HDMI_CORE_BASE(0x0620 + 4 * (n)) +#define HDMI_ISRC2_DATA(n) HDMI_CORE_BASE(0x06A0 + 4 * (n)) + +#define HDMI_AVI_CON HDMI_CORE_BASE(0x0700) +#define HDMI_AVI_HEADER0 HDMI_CORE_BASE(0x0710) +#define HDMI_AVI_HEADER1 HDMI_CORE_BASE(0x0714) +#define HDMI_AVI_HEADER2 HDMI_CORE_BASE(0x0718) +#define HDMI_AVI_CHECK_SUM HDMI_CORE_BASE(0x071C) +#define HDMI_AVI_BYTE(n) HDMI_CORE_BASE(0x0720 + 4 * (n)) + +#define HDMI_AUI_CON HDMI_CORE_BASE(0x0800) +#define HDMI_AUI_HEADER0 HDMI_CORE_BASE(0x0810) +#define HDMI_AUI_HEADER1 HDMI_CORE_BASE(0x0814) +#define HDMI_AUI_HEADER2 HDMI_CORE_BASE(0x0818) +#define HDMI_AUI_CHECK_SUM HDMI_CORE_BASE(0x081C) +#define HDMI_AUI_BYTE(n) HDMI_CORE_BASE(0x0820 + 4 * (n)) + +#define HDMI_MPG_CON HDMI_CORE_BASE(0x0900) +#define HDMI_MPG_CHECK_SUM HDMI_CORE_BASE(0x091C) +#define HDMI_MPG_DATA(n) HDMI_CORE_BASE(0x0920 + 4 * (n)) + +#define HDMI_SPD_CON HDMI_CORE_BASE(0x0A00) +#define HDMI_SPD_HEADER0 HDMI_CORE_BASE(0x0A10) +#define HDMI_SPD_HEADER1 HDMI_CORE_BASE(0x0A14) +#define HDMI_SPD_HEADER2 HDMI_CORE_BASE(0x0A18) +#define HDMI_SPD_DATA(n) HDMI_CORE_BASE(0x0A20 + 4 * (n)) + +#define HDMI_GAMUT_CON HDMI_CORE_BASE(0x0B00) +#define HDMI_GAMUT_HEADER0 HDMI_CORE_BASE(0x0B10) +#define HDMI_GAMUT_HEADER1 HDMI_CORE_BASE(0x0B14) +#define HDMI_GAMUT_HEADER2 HDMI_CORE_BASE(0x0B18) +#define HDMI_GAMUT_METADATA(n) HDMI_CORE_BASE(0x0B20 + 4 * (n)) + +#define HDMI_VSI_CON HDMI_CORE_BASE(0x0C00) +#define HDMI_VSI_HEADER0 HDMI_CORE_BASE(0x0C10) +#define HDMI_VSI_HEADER1 HDMI_CORE_BASE(0x0C14) +#define HDMI_VSI_HEADER2 HDMI_CORE_BASE(0x0C18) +#define HDMI_VSI_DATA(n) HDMI_CORE_BASE(0x0C20 + 4 * (n)) + +#define HDMI_DC_CONTROL HDMI_CORE_BASE(0x0D00) +#define HDMI_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x0D04) + +#define HDMI_AN_SEED_SEL HDMI_CORE_BASE(0x0E48) +#define HDMI_AN_SEED_0 HDMI_CORE_BASE(0x0E58) +#define HDMI_AN_SEED_1 HDMI_CORE_BASE(0x0E5C) +#define HDMI_AN_SEED_2 HDMI_CORE_BASE(0x0E60) +#define HDMI_AN_SEED_3 HDMI_CORE_BASE(0x0E64) + +/* HDCP related registers */ +#define HDMI_HDCP_SHA1(n) HDMI_CORE_BASE(0x7000 + 4 * (n)) +#define HDMI_HDCP_KSV_LIST(n) HDMI_CORE_BASE(0x7050 + 4 * (n)) + +#define HDMI_HDCP_KSV_LIST_CON HDMI_CORE_BASE(0x7064) +#define HDMI_HDCP_SHA_RESULT HDMI_CORE_BASE(0x7070) +#define HDMI_HDCP_CTRL1 HDMI_CORE_BASE(0x7080) +#define HDMI_HDCP_CTRL2 HDMI_CORE_BASE(0x7084) +#define HDMI_HDCP_CHECK_RESULT HDMI_CORE_BASE(0x7090) +#define HDMI_HDCP_BKSV(n) HDMI_CORE_BASE(0x70A0 + 4 * (n)) +#define HDMI_HDCP_AKSV(n) HDMI_CORE_BASE(0x70C0 + 4 * (n)) +#define HDMI_HDCP_AN(n) HDMI_CORE_BASE(0x70E0 + 4 * (n)) + +#define HDMI_HDCP_BCAPS HDMI_CORE_BASE(0x7100) +#define HDMI_HDCP_BSTATUS_0 HDMI_CORE_BASE(0x7110) +#define HDMI_HDCP_BSTATUS_1 HDMI_CORE_BASE(0x7114) +#define HDMI_HDCP_RI_0 HDMI_CORE_BASE(0x7140) +#define HDMI_HDCP_RI_1 HDMI_CORE_BASE(0x7144) +#define HDMI_HDCP_I2C_INT HDMI_CORE_BASE(0x7180) +#define HDMI_HDCP_AN_INT HDMI_CORE_BASE(0x7190) +#define HDMI_HDCP_WDT_INT HDMI_CORE_BASE(0x71A0) +#define HDMI_HDCP_RI_INT HDMI_CORE_BASE(0x71B0) +#define HDMI_HDCP_RI_COMPARE_0 HDMI_CORE_BASE(0x71D0) +#define HDMI_HDCP_RI_COMPARE_1 HDMI_CORE_BASE(0x71D4) +#define HDMI_HDCP_FRAME_COUNT HDMI_CORE_BASE(0x71E0) + +#define HDMI_RGB_ROUND_EN HDMI_CORE_BASE(0xD500) +#define HDMI_VACT_SPACE_R_0 HDMI_CORE_BASE(0xD504) +#define HDMI_VACT_SPACE_R_1 HDMI_CORE_BASE(0xD508) +#define HDMI_VACT_SPACE_G_0 HDMI_CORE_BASE(0xD50C) +#define HDMI_VACT_SPACE_G_1 HDMI_CORE_BASE(0xD510) +#define HDMI_VACT_SPACE_B_0 HDMI_CORE_BASE(0xD514) +#define HDMI_VACT_SPACE_B_1 HDMI_CORE_BASE(0xD518) + +#define HDMI_BLUE_SCREEN_B_0 HDMI_CORE_BASE(0xD520) +#define HDMI_BLUE_SCREEN_B_1 HDMI_CORE_BASE(0xD524) +#define HDMI_BLUE_SCREEN_G_0 HDMI_CORE_BASE(0xD528) +#define HDMI_BLUE_SCREEN_G_1 HDMI_CORE_BASE(0xD52C) +#define HDMI_BLUE_SCREEN_R_0 HDMI_CORE_BASE(0xD530) +#define HDMI_BLUE_SCREEN_R_1 HDMI_CORE_BASE(0xD534) + +/* Timing generator registers */ +/* TG configure/status registers */ +#define HDMI_TG_VACT_ST3_L HDMI_TG_BASE(0x0068) +#define HDMI_TG_VACT_ST3_H HDMI_TG_BASE(0x006c) +#define HDMI_TG_VACT_ST4_L HDMI_TG_BASE(0x0070) +#define HDMI_TG_VACT_ST4_H HDMI_TG_BASE(0x0074) +#define HDMI_TG_3D HDMI_TG_BASE(0x00F0) + #endif /* SAMSUNG_REGS_HDMI_H */ diff --git a/include/drm/exynos_drm.h b/include/drm/exynos_drm.h index 1ed3aae..aff2313 100644 --- a/include/drm/exynos_drm.h +++ b/include/drm/exynos_drm.h @@ -147,11 +147,13 @@ struct exynos_drm_common_hdmi_pd { * @timing: default video mode for initializing * @default_win: default window layer number to be used for UI. * @bpp: default bit per pixel. + * @is_v13: set if hdmi version 13 is. */ struct exynos_drm_hdmi_pdata { struct fb_videomode timing; unsigned int default_win; unsigned int bpp; + unsigned int is_v13:1; };
#endif /* __KERNEL__ */
this patch adds mode_fixup feature for hdmi module that specific driver changes current mode to driver desired mode properly.
Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_connector.c | 25 +++++++- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 6 ++- drivers/gpu/drm/exynos/exynos_drm_drv.h | 8 +++ drivers/gpu/drm/exynos/exynos_drm_encoder.c | 17 ++++- drivers/gpu/drm/exynos/exynos_drm_hdmi.c | 28 +++++++++ drivers/gpu/drm/exynos/exynos_drm_hdmi.h | 5 ++ drivers/gpu/drm/exynos/exynos_hdmi.c | 81 ++++++++++++++++++++++-- 7 files changed, 157 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c index 99d5527..303af60 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_connector.c +++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c @@ -225,6 +225,29 @@ static struct drm_connector_helper_funcs exynos_connector_helper_funcs = { .best_encoder = exynos_drm_best_encoder, };
+static int exynos_drm_connector_fill_modes(struct drm_connector *connector, + unsigned int max_width, unsigned int max_height) +{ + struct exynos_drm_connector *exynos_connector = + to_exynos_connector(connector); + struct exynos_drm_manager *manager = exynos_connector->manager; + struct exynos_drm_manager_ops *ops = manager->ops; + unsigned int width, height; + + width = max_width; + height = max_height; + + /* + * if specific driver want to find desired_mode using maxmum + * resolution then get max width and height from that driver. + */ + if (ops && ops->get_max_resol) + ops->get_max_resol(manager->dev, &width, &height); + + return drm_helper_probe_single_connector_modes(connector, width, + height); +} + /* get detection status of display device. */ static enum drm_connector_status exynos_drm_connector_detect(struct drm_connector *connector, bool force) @@ -262,7 +285,7 @@ static void exynos_drm_connector_destroy(struct drm_connector *connector)
static struct drm_connector_funcs exynos_connector_funcs = { .dpms = drm_helper_connector_dpms, - .fill_modes = drm_helper_probe_single_connector_modes, + .fill_modes = exynos_drm_connector_fill_modes, .detect = exynos_drm_connector_detect, .destroy = exynos_drm_connector_destroy, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index de81883..2d9a0e6 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -249,7 +249,11 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, { DRM_DEBUG_KMS("%s\n", __FILE__);
- mode = adjusted_mode; + /* + * copy the mode data adjusted by mode_fixup() into crtc->mode + * so that hardware can be seet to proper mode. + */ + memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
return exynos_drm_crtc_update(crtc); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 13540de..4115a9f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -155,8 +155,10 @@ struct exynos_drm_display_ops { * * @dpms: control device power. * @apply: set timing, vblank and overlay data to registers. + * @mode_fixup: fix mode data comparing to hw specific display mode. * @mode_set: convert drm_display_mode to hw specific display mode and * would be called by encoder->mode_set(). + * @get_max_resol: get maximum resolution to specific hardware. * @commit: set current hw specific display mode to hw. * @enable_vblank: specific driver callback for enabling vblank interrupt. * @disable_vblank: specific driver callback for disabling vblank interrupt. @@ -164,7 +166,13 @@ struct exynos_drm_display_ops { struct exynos_drm_manager_ops { void (*dpms)(struct device *subdrv_dev, int mode); void (*apply)(struct device *subdrv_dev); + void (*mode_fixup)(struct device *subdrv_dev, + struct drm_connector *connector, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); void (*mode_set)(struct device *subdrv_dev, void *mode); + void (*get_max_resol)(struct device *subdrv_dev, unsigned int *width, + unsigned int *height); void (*commit)(struct device *subdrv_dev); int (*enable_vblank)(struct device *subdrv_dev); void (*disable_vblank)(struct device *subdrv_dev); diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c index ef4754f..45ca732 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c @@ -111,9 +111,19 @@ exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { + struct drm_device *dev = encoder->dev; + struct drm_connector *connector; + struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); + struct exynos_drm_manager_ops *manager_ops = manager->ops; + DRM_DEBUG_KMS("%s\n", __FILE__);
- /* drm framework doesn't check NULL. */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder == encoder) + if (manager_ops && manager_ops->mode_fixup) + manager_ops->mode_fixup(manager->dev, connector, + mode, adjusted_mode); + }
return true; } @@ -132,12 +142,11 @@ static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder,
DRM_DEBUG_KMS("%s\n", __FILE__);
- mode = adjusted_mode; - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { if (connector->encoder == encoder) { if (manager_ops && manager_ops->mode_set) - manager_ops->mode_set(manager->dev, mode); + manager_ops->mode_set(manager->dev, + adjusted_mode);
if (overlay_ops && overlay_ops->mode_set) overlay_ops->mode_set(manager->dev, overlay); diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c index ed8a319e..ed86bdd 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c @@ -155,6 +155,20 @@ static void drm_hdmi_disable_vblank(struct device *subdrv_dev) return hdmi_overlay_ops->disable_vblank(ctx->mixer_ctx->ctx); }
+static void drm_hdmi_mode_fixup(struct device *subdrv_dev, + struct drm_connector *connector, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_hdmi_context *ctx = to_context(subdrv_dev); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (hdmi_manager_ops && hdmi_manager_ops->mode_fixup) + hdmi_manager_ops->mode_fixup(ctx->hdmi_ctx->ctx, connector, + mode, adjusted_mode); +} + static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode) { struct drm_hdmi_context *ctx = to_context(subdrv_dev); @@ -165,6 +179,18 @@ static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode) hdmi_manager_ops->mode_set(ctx->hdmi_ctx->ctx, mode); }
+static void drm_hdmi_get_max_resol(struct device *subdrv_dev, + unsigned int *width, unsigned int *height) +{ + struct drm_hdmi_context *ctx = to_context(subdrv_dev); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (hdmi_manager_ops && hdmi_manager_ops->get_max_resol) + hdmi_manager_ops->get_max_resol(ctx->hdmi_ctx->ctx, width, + height); +} + static void drm_hdmi_commit(struct device *subdrv_dev) { struct drm_hdmi_context *ctx = to_context(subdrv_dev); @@ -200,7 +226,9 @@ static struct exynos_drm_manager_ops drm_hdmi_manager_ops = { .dpms = drm_hdmi_dpms, .enable_vblank = drm_hdmi_enable_vblank, .disable_vblank = drm_hdmi_disable_vblank, + .mode_fixup = drm_hdmi_mode_fixup, .mode_set = drm_hdmi_mode_set, + .get_max_resol = drm_hdmi_get_max_resol, .commit = drm_hdmi_commit, };
diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h index 3c29f79..44497cf 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h @@ -47,7 +47,12 @@ struct exynos_hdmi_display_ops { };
struct exynos_hdmi_manager_ops { + void (*mode_fixup)(void *ctx, struct drm_connector *connector, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); void (*mode_set)(void *ctx, void *mode); + void (*get_max_resol)(void *ctx, unsigned int *width, + unsigned int *height); void (*commit)(void *ctx); void (*disable)(void *ctx); }; diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 1cfe86e..6fe1e89 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -41,6 +41,8 @@ #include "exynos_hdmi.h"
#define HDMI_OVERLAY_NUMBER 3 +#define MAX_WIDTH 1920 +#define MAX_HEIGHT 1080 #define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev))
/* HDMI Version 1.3 */ @@ -1126,7 +1128,7 @@ static int hdmi_v13_conf_index(struct drm_display_mode *mode) true : false)) return i;
- return -1; + return -EINVAL; }
static int hdmi_v14_conf_index(struct drm_display_mode *mode) @@ -1142,7 +1144,7 @@ static int hdmi_v14_conf_index(struct drm_display_mode *mode) true : false)) return i;
- return -1; + return -EINVAL; }
static int hdmi_conf_index(struct hdmi_context *hdata, @@ -1150,8 +1152,8 @@ static int hdmi_conf_index(struct hdmi_context *hdata, { if (hdata->is_v13) return hdmi_v13_conf_index(mode); - else - return hdmi_v14_conf_index(mode); + + return hdmi_v14_conf_index(mode); }
static bool hdmi_is_connected(void *ctx) @@ -1193,6 +1195,11 @@ static int hdmi_v13_check_timing(struct fb_videomode *check_timing) { int i;
+ DRM_DEBUG_KMS("valid mode : xres=%d, yres=%d, refresh=%d, intl=%d\n", + check_timing->xres, check_timing->yres, + check_timing->refresh, (check_timing->vmode & + FB_VMODE_INTERLACED) ? true : false); + for (i = 0; i < ARRAY_SIZE(hdmi_v13_confs); ++i) if (hdmi_v13_confs[i].width == check_timing->xres && hdmi_v13_confs[i].height == check_timing->yres && @@ -1200,7 +1207,9 @@ static int hdmi_v13_check_timing(struct fb_videomode *check_timing) hdmi_v13_confs[i].interlace == ((check_timing->vmode & FB_VMODE_INTERLACED) ? true : false)) - return 0; + return 0; + + /* TODO */
return -EINVAL; } @@ -1209,14 +1218,21 @@ static int hdmi_v14_check_timing(struct fb_videomode *check_timing) { int i;
- for (i = 0; i < ARRAY_SIZE(hdmi_confs); ++i) + DRM_DEBUG_KMS("valid mode : xres=%d, yres=%d, refresh=%d, intl=%d\n", + check_timing->xres, check_timing->yres, + check_timing->refresh, (check_timing->vmode & + FB_VMODE_INTERLACED) ? true : false); + + for (i = 0; i < ARRAY_SIZE(hdmi_confs); i++) if (hdmi_confs[i].width == check_timing->xres && hdmi_confs[i].height == check_timing->yres && hdmi_confs[i].vrefresh == check_timing->refresh && hdmi_confs[i].interlace == ((check_timing->vmode & FB_VMODE_INTERLACED) ? true : false)) - return 0; + return 0; + + /* TODO */
return -EINVAL; } @@ -1692,6 +1708,46 @@ static void hdmi_conf_apply(struct hdmi_context *hdata) hdmi_regs_dump(hdata, "start"); }
+static void hdmi_mode_fixup(void *ctx, struct drm_connector *connector, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_display_mode *m; + struct hdmi_context *hdata = (struct hdmi_context *)ctx; + int index; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + drm_mode_set_crtcinfo(adjusted_mode, 0); + + if (hdata->is_v13) + index = hdmi_v13_conf_index(adjusted_mode); + else + index = hdmi_v14_conf_index(adjusted_mode); + + /* just return if user desired mode exists. */ + if (index >= 0) + return; + + /* + * otherwise, find the most suitable mode among modes and change it + * to adjusted_mode. + */ + list_for_each_entry(m, &connector->modes, head) { + if (hdata->is_v13) + index = hdmi_v13_conf_index(m); + else + index = hdmi_v14_conf_index(m); + + if (index >= 0) { + DRM_INFO("desired mode doesn't exist so\n"); + DRM_INFO("use the most suitable mode among modes.\n"); + memcpy(adjusted_mode, m, sizeof(*m)); + break; + } + } +} + static void hdmi_mode_set(void *ctx, void *mode) { struct hdmi_context *hdata = (struct hdmi_context *)ctx; @@ -1706,6 +1762,15 @@ static void hdmi_mode_set(void *ctx, void *mode) DRM_DEBUG_KMS("not supported mode\n"); }
+static void hdmi_get_max_resol(void *ctx, unsigned int *width, + unsigned int *height) +{ + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + *width = MAX_WIDTH; + *height = MAX_HEIGHT; +} + static void hdmi_commit(void *ctx) { struct hdmi_context *hdata = (struct hdmi_context *)ctx; @@ -1730,7 +1795,9 @@ static void hdmi_disable(void *ctx) }
static struct exynos_hdmi_manager_ops manager_ops = { + .mode_fixup = hdmi_mode_fixup, .mode_set = hdmi_mode_set, + .get_max_resol = hdmi_get_max_resol, .commit = hdmi_commit, .disable = hdmi_disable, };
}; diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index de81883..2d9a0e6 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -249,7 +249,11 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, { DRM_DEBUG_KMS("%s\n", __FILE__);
- mode = adjusted_mode;
- /*
- * copy the mode data adjusted by mode_fixup() into crtc->mode
- * so that hardware can be seet to proper mode.
- */
- memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
This just seems wrong on lots of levels, if you get an error how are you going to fallback?
You should be passing the modes into the crtc_update function and doing things like other drivers here.
Dave.
-----Original Message----- From: Dave Airlie [mailto:airlied@gmail.com] Sent: Thursday, March 15, 2012 7:40 PM To: Inki Dae Cc: airlied@linux.ie; dri-devel@lists.freedesktop.org; kyungmin.park@samsung.com; sw0312.kim@samsung.com Subject: Re: [PATCH 02/10] drm/exynos: added mode_fixup feature and code clean.
}; diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index de81883..2d9a0e6 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -249,7 +249,11 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode,
{ DRM_DEBUG_KMS("%s\n", __FILE__);
- mode = adjusted_mode;
- /*
- * copy the mode data adjusted by mode_fixup() into crtc->mode
- * so that hardware can be seet to proper mode.
- */
- memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
This just seems wrong on lots of levels, if you get an error how are you going to fallback?
You should be passing the modes into the crtc_update function and doing things like other drivers here.
Dave.
Got it. I will check return value from crtc_update and if it gets an error then I will handle it properly.
Thanks, Inki Dae
Hi Dave.
2012년 3월 15일 오후 8:21, Inki Dae inki.dae@samsung.com님의 말:
-----Original Message----- From: Dave Airlie [mailto:airlied@gmail.com] Sent: Thursday, March 15, 2012 7:40 PM To: Inki Dae Cc: airlied@linux.ie; dri-devel@lists.freedesktop.org; kyungmin.park@samsung.com; sw0312.kim@samsung.com Subject: Re: [PATCH 02/10] drm/exynos: added mode_fixup feature and code clean.
}; diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index de81883..2d9a0e6 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -249,7 +249,11 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode,
{ DRM_DEBUG_KMS("%s\n", __FILE__);
mode = adjusted_mode;
/*
* copy the mode data adjusted by mode_fixup() into crtc->mode
* so that hardware can be seet to proper mode.
*/
memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
This just seems wrong on lots of levels, if you get an error how are you going to fallback?
You should be passing the modes into the crtc_update function and doing things like other drivers here.
Dave.
I have looked into the part you pointed out but I think it's ok. exynos_drm_crtc_update just sets mode values to overlay structure and overlay structure is common data structure that all hardware can use and exynos crtc has no hardware defendency so we could use it commoly for all hardware such as FIMD, HDMI and Virtual Display. so with exynos_drm_crtc_update call, any hardward isn't updated. if exynos_drm_crtc_update is fail then adjusted_mode would be released and back to saved_mode. only if crtc_func->mode_set is true then hardware would be updated. please let me know if there is my missing point.
Thanks, Inki Dae
Got it. I will check return value from crtc_update and if it gets an error then I will handle it properly.
Thanks, Inki Dae
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
with this patch, we can allocate physically continuous or non-continuous memory and also it creates scatterlist for iommu support so allocated memory region can be mapped to iommu page table using scatterlist.
Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_buf.c | 148 +++++++++++++-- drivers/gpu/drm/exynos/exynos_drm_buf.h | 4 +- drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 4 +- drivers/gpu/drm/exynos/exynos_drm_gem.c | 284 ++++++++++++++++++++++++++--- drivers/gpu/drm/exynos/exynos_drm_gem.h | 11 +- include/drm/exynos_drm.h | 6 + 6 files changed, 407 insertions(+), 50 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.c b/drivers/gpu/drm/exynos/exynos_drm_buf.c index 3cf785c..554f674 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_buf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_buf.c @@ -25,45 +25,151 @@
#include "drmP.h" #include "drm.h" +#include "exynos_drm.h"
#include "exynos_drm_drv.h" #include "exynos_drm_gem.h" #include "exynos_drm_buf.h"
static int lowlevel_buffer_allocate(struct drm_device *dev, - struct exynos_drm_gem_buf *buffer) + unsigned int flags, struct exynos_drm_gem_buf *buf) { + int ret = 0; + DRM_DEBUG_KMS("%s\n", __FILE__);
- buffer->kvaddr = dma_alloc_writecombine(dev->dev, buffer->size, - &buffer->dma_addr, GFP_KERNEL); - if (!buffer->kvaddr) { - DRM_ERROR("failed to allocate buffer.\n"); - return -ENOMEM; + /* + * allocate only physically continuous memory and + * non-continuous memory would be allocated by exynos + * gem framework. + */ + if (!(flags & EXYNOS_BO_NONCONTIG)) { + dma_addr_t start_addr, end_addr; + unsigned int npages, page_size, i = 0; + struct scatterlist *sgl; + + if (buf->dma_addr) { + DRM_DEBUG_KMS("already allocated.\n"); + return -EINVAL; + } + + /* + * according to desired size, it sets page size + * for performance with using iommu so physically + * continuous memory could be mapped to iommu in + * multiple page size. + */ + if (buf->size >= SZ_1M) { + npages = (buf->size >> SECTION_SHIFT) + 1; + page_size = SECTION_SIZE; + } else if (buf->size >= SZ_64K) { + npages = (buf->size >> 16) + 1; + page_size = SZ_64K; + } else { + npages = (buf->size >> PAGE_SHIFT) + 1; + page_size = PAGE_SIZE; + } + + buf->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!buf->sgt) { + DRM_ERROR("failed to allocate sg table.\n"); + return -ENOMEM; + } + + ret = sg_alloc_table(buf->sgt, npages, GFP_KERNEL); + if (ret < 0) { + DRM_ERROR("failed to initialize sg table.\n"); + kfree(buf->sgt); + buf->sgt = NULL; + return -ENOMEM; + } + + buf->kvaddr = dma_alloc_writecombine(dev->dev, buf->size, + &buf->dma_addr, GFP_KERNEL); + if (!buf->kvaddr) { + DRM_ERROR("failed to allocate buffer.\n"); + ret = -ENOMEM; + goto err1; + } + + start_addr = buf->dma_addr; + end_addr = buf->dma_addr + buf->size; + + buf->pages = kzalloc(sizeof(struct page) * npages, GFP_KERNEL); + if (!buf->pages) { + DRM_ERROR("failed to allocate pages.\n"); + ret = -ENOMEM; + goto err2; + } + + sgl = buf->sgt->sgl; + + while (i < npages) { + buf->pages[i] = phys_to_page(start_addr); + sg_set_page(sgl, buf->pages[i], page_size, 0); + sg_dma_address(sgl) = start_addr; + start_addr += page_size; + if (end_addr - start_addr < page_size) + break; + sgl = sg_next(sgl); + i++; + } + + buf->pages[i] = phys_to_page(start_addr); + + sgl = sg_next(sgl); + sg_set_page(sgl, buf->pages[i+1], end_addr - start_addr, 0); }
DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n", - (unsigned long)buffer->kvaddr, - (unsigned long)buffer->dma_addr, - buffer->size); - - return 0; + (unsigned long)buf->kvaddr, + (unsigned long)buf->dma_addr, + buf->size); + + return ret; +err2: + dma_free_writecombine(dev->dev, buf->size, buf->kvaddr, + (dma_addr_t)buf->dma_addr); + buf->dma_addr = (dma_addr_t)NULL; +err1: + sg_free_table(buf->sgt); + kfree(buf->sgt); + buf->sgt = NULL; + + return ret; }
static void lowlevel_buffer_deallocate(struct drm_device *dev, - struct exynos_drm_gem_buf *buffer) + unsigned int flags, struct exynos_drm_gem_buf *buf) { DRM_DEBUG_KMS("%s.\n", __FILE__);
- if (buffer->dma_addr && buffer->size) - dma_free_writecombine(dev->dev, buffer->size, buffer->kvaddr, - (dma_addr_t)buffer->dma_addr); - else - DRM_DEBUG_KMS("buffer data are invalid.\n"); + /* + * release only physically continuous memory and + * non-continuous memory would be released by exynos + * gem framework. + */ + if (!(flags & EXYNOS_BO_NONCONTIG)) { + sg_free_table(buf->sgt); + + kfree(buf->sgt); + buf->sgt = NULL; + + kfree(buf->pages); + buf->pages = NULL; + + if (buf->dma_addr && buf->size) + dma_free_writecombine(dev->dev, buf->size, buf->kvaddr, + (dma_addr_t)buf->dma_addr); + else + DRM_DEBUG_KMS("buffer data are invalid.\n"); + + buf->dma_addr = (dma_addr_t)NULL; + } }
struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev, - unsigned int size) + unsigned int flags, unsigned int size) { struct exynos_drm_gem_buf *buffer;
@@ -82,7 +188,7 @@ struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev, * allocate memory region with size and set the memory information * to vaddr and dma_addr of a buffer object. */ - if (lowlevel_buffer_allocate(dev, buffer) < 0) { + if (lowlevel_buffer_allocate(dev, flags, buffer) < 0) { kfree(buffer); return NULL; } @@ -91,7 +197,7 @@ struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev, }
void exynos_drm_buf_destroy(struct drm_device *dev, - struct exynos_drm_gem_buf *buffer) + unsigned int flags, struct exynos_drm_gem_buf *buffer) { DRM_DEBUG_KMS("%s.\n", __FILE__);
@@ -100,7 +206,7 @@ void exynos_drm_buf_destroy(struct drm_device *dev, return; }
- lowlevel_buffer_deallocate(dev, buffer); + lowlevel_buffer_deallocate(dev, flags, buffer);
kfree(buffer); buffer = NULL; diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.h b/drivers/gpu/drm/exynos/exynos_drm_buf.h index c913f2b..3422267 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_buf.h +++ b/drivers/gpu/drm/exynos/exynos_drm_buf.h @@ -28,10 +28,10 @@
/* allocate physical memory. */ struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev, - unsigned int size); + unsigned int flags, unsigned int size);
/* remove allocated physical memory. */ void exynos_drm_buf_destroy(struct drm_device *dev, - struct exynos_drm_gem_buf *buffer); + unsigned int flags, struct exynos_drm_gem_buf *buffer);
#endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 3508700..4e86d15 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -151,7 +151,9 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, }
size = mode_cmd.pitches[0] * mode_cmd.height; - exynos_gem_obj = exynos_drm_gem_create(dev, size); + + /* 0 means to allocate physically continuous memory */ + exynos_gem_obj = exynos_drm_gem_create(dev, 0, size); if (IS_ERR(exynos_gem_obj)) { ret = PTR_ERR(exynos_gem_obj); goto out; diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 025abb3..36d081d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -26,6 +26,7 @@ #include "drmP.h" #include "drm.h"
+#include <linux/shmem_fs.h> #include <drm/exynos_drm.h>
#include "exynos_drm_drv.h" @@ -55,6 +56,181 @@ static unsigned int convert_to_vm_err_msg(int msg) return out_msg; }
+static unsigned int mask_gem_flags(unsigned int flags) +{ + return flags &= EXYNOS_BO_NONCONTIG; +} + +static struct page **exynos_gem_get_pages(struct drm_gem_object *obj, + gfp_t gfpmask) +{ + struct inode *inode; + struct address_space *mapping; + struct page *p, **pages; + int i, npages; + + /* This is the shared memory object that backs the GEM resource */ + inode = obj->filp->f_path.dentry->d_inode; + mapping = inode->i_mapping; + + npages = obj->size >> PAGE_SHIFT; + + pages = drm_malloc_ab(npages, sizeof(struct page *)); + if (pages == NULL) + return ERR_PTR(-ENOMEM); + + gfpmask |= mapping_gfp_mask(mapping); + + for (i = 0; i < npages; i++) { + p = shmem_read_mapping_page_gfp(mapping, i, gfpmask); + if (IS_ERR(p)) + goto fail; + pages[i] = p; + } + + return pages; + +fail: + while (i--) + page_cache_release(pages[i]); + + drm_free_large(pages); + return ERR_PTR(PTR_ERR(p)); +} + +static void exynos_gem_put_pages(struct drm_gem_object *obj, + struct page **pages, + bool dirty, bool accessed) +{ + int i, npages; + + npages = obj->size >> PAGE_SHIFT; + + for (i = 0; i < npages; i++) { + if (dirty) + set_page_dirty(pages[i]); + + if (accessed) + mark_page_accessed(pages[i]); + + /* Undo the reference we took when populating the table */ + page_cache_release(pages[i]); + } + + drm_free_large(pages); +} + +static int exynos_drm_gem_map_pages(struct drm_gem_object *obj, + struct vm_area_struct *vma, + unsigned long f_vaddr, + pgoff_t page_offset) +{ + struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); + struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer; + unsigned long pfn; + + if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { + unsigned long usize = buf->size; + + if (!buf->pages) + return -EINTR; + + while (usize > 0) { + pfn = page_to_pfn(buf->pages[page_offset++]); + vm_insert_mixed(vma, f_vaddr, pfn); + f_vaddr += PAGE_SIZE; + usize -= PAGE_SIZE; + } + + return 0; + } + + pfn = (buf->dma_addr >> PAGE_SHIFT) + page_offset; + + return vm_insert_mixed(vma, f_vaddr, pfn); +} + +static int exynos_drm_gem_get_pages(struct drm_gem_object *obj) +{ + struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); + struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer; + struct scatterlist *sgl; + struct page **pages; + unsigned int npages, i = 0; + int ret; + + if (buf->pages) { + DRM_DEBUG_KMS("already allocated.\n"); + return -EINVAL; + } + + pages = exynos_gem_get_pages(obj, GFP_KERNEL); + if (IS_ERR(pages)) { + DRM_ERROR("failed to get pages.\n"); + return PTR_ERR(pages); + } + + npages = obj->size >> PAGE_SHIFT; + + buf->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL); + if (!buf->sgt) { + DRM_ERROR("failed to allocate sg table.\n"); + ret = -ENOMEM; + goto err; + } + + ret = sg_alloc_table(buf->sgt, npages, GFP_KERNEL); + if (ret < 0) { + DRM_ERROR("failed to initialize sg table.\n"); + ret = -EFAULT; + goto err1; + } + + sgl = buf->sgt->sgl; + + /* set all pages to sg list. */ + while (i < npages) { + sg_set_page(sgl, pages[i], PAGE_SIZE, 0); + sg_dma_address(sgl) = page_to_phys(pages[i]); + i++; + sgl = sg_next(sgl); + } + + /* add some codes for UNCACHED type here. TODO */ + + buf->pages = pages; + return ret; +err1: + kfree(buf->sgt); + buf->sgt = NULL; +err: + exynos_gem_put_pages(obj, pages, true, false); + return ret; + +} + +static void exynos_drm_gem_put_pages(struct drm_gem_object *obj) +{ + struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); + struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer; + + /* + * if buffer typs is EXYNOS_BO_NONCONTIG then release all pages + * allocated at gem fault handler. + */ + if ((exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) && + buf->pages) { + sg_free_table(buf->sgt); + kfree(buf->sgt); + buf->sgt = NULL; + + exynos_gem_put_pages(obj, buf->pages, true, false); + buf->pages = NULL; + } + + /* add some codes for UNCACHED type here. TODO */ +} + static int exynos_drm_gem_handle_create(struct drm_gem_object *obj, struct drm_file *file_priv, unsigned int *handle) @@ -90,7 +266,10 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)
DRM_DEBUG_KMS("handle count = %d\n", atomic_read(&obj->handle_count));
- exynos_drm_buf_destroy(obj->dev, exynos_gem_obj->buffer); + exynos_drm_gem_put_pages(obj); + + exynos_drm_buf_destroy(obj->dev, exynos_gem_obj->flags, + exynos_gem_obj->buffer);
if (obj->map_list.map) drm_gem_free_mmap_offset(obj); @@ -114,6 +293,7 @@ static struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev, return NULL; }
+ exynos_gem_obj->size = size; obj = &exynos_gem_obj->base;
ret = drm_gem_object_init(dev, obj, size); @@ -129,27 +309,49 @@ static struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev, }
struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, - unsigned long size) + unsigned int flags, + unsigned long size) { struct exynos_drm_gem_buf *buffer; struct exynos_drm_gem_obj *exynos_gem_obj; + int ret;
size = roundup(size, PAGE_SIZE); DRM_DEBUG_KMS("%s: size = 0x%lx\n", __FILE__, size);
- buffer = exynos_drm_buf_create(dev, size); + buffer = exynos_drm_buf_create(dev, flags, size); if (!buffer) return ERR_PTR(-ENOMEM);
exynos_gem_obj = exynos_drm_gem_init(dev, size); if (!exynos_gem_obj) { - exynos_drm_buf_destroy(dev, buffer); + exynos_drm_buf_destroy(dev, flags, buffer); return ERR_PTR(-ENOMEM); }
exynos_gem_obj->buffer = buffer;
+ /* + * allocate all pages as desired size if user wants to allocate + * physically non-continuous memory. + */ + if (flags & EXYNOS_BO_NONCONTIG) { + ret = exynos_drm_gem_get_pages(&exynos_gem_obj->base); + if (ret < 0) + goto err; + } + + /* set memory type and cache attribute from user side. */ + exynos_gem_obj->flags = mask_gem_flags(flags); + + return exynos_gem_obj; + +err: + exynos_drm_buf_destroy(dev, flags, buffer); + drm_gem_object_release(&exynos_gem_obj->base); + kfree(exynos_gem_obj); + return ERR_PTR(ret); }
int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, @@ -161,7 +363,7 @@ int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
DRM_DEBUG_KMS("%s\n", __FILE__);
- exynos_gem_obj = exynos_drm_gem_create(dev, args->size); + exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size); if (IS_ERR(exynos_gem_obj)) return PTR_ERR(exynos_gem_obj);
@@ -200,7 +402,8 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp, struct drm_gem_object *obj = filp->private_data; struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); struct exynos_drm_gem_buf *buffer; - unsigned long pfn, vm_size; + unsigned long pfn, vm_size, usize, uaddr = vma->vm_start; + int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
@@ -210,7 +413,8 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp, vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); vma->vm_file = filp;
- vm_size = vma->vm_end - vma->vm_start; + vm_size = usize = vma->vm_end - vma->vm_start; + /* * a buffer contains information to physically continuous memory * allocated by user request or at framebuffer creation. @@ -221,18 +425,37 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp, if (vm_size > buffer->size) return -EINVAL;
- /* - * get page frame number to physical memory to be mapped - * to user space. - */ - pfn = ((unsigned long)exynos_gem_obj->buffer->dma_addr) >> PAGE_SHIFT; - - DRM_DEBUG_KMS("pfn = 0x%lx\n", pfn); - - if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size, - vma->vm_page_prot)) { - DRM_ERROR("failed to remap pfn range.\n"); - return -EAGAIN; + if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { + int i = 0; + + if (!buffer->pages) + return -EINVAL; + + do { + ret = vm_insert_page(vma, uaddr, buffer->pages[i++]); + if (ret) { + DRM_ERROR("failed to remap user space.\n"); + return ret; + } + + uaddr += PAGE_SIZE; + usize -= PAGE_SIZE; + } while (usize > 0); + } else { + /* + * get page frame number to physical memory to be mapped + * to user space. + */ + pfn = ((unsigned long)exynos_gem_obj->buffer->dma_addr) >> + PAGE_SHIFT; + + DRM_DEBUG_KMS("pfn = 0x%lx\n", pfn); + + if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size, + vma->vm_page_prot)) { + DRM_ERROR("failed to remap pfn range.\n"); + return -EAGAIN; + } }
return 0; @@ -312,9 +535,9 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv, */
args->pitch = args->width * args->bpp >> 3; - args->size = args->pitch * args->height; + args->size = PAGE_ALIGN(args->pitch * args->height);
- exynos_gem_obj = exynos_drm_gem_create(dev, args->size); + exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size); if (IS_ERR(exynos_gem_obj)) return PTR_ERR(exynos_gem_obj);
@@ -398,20 +621,31 @@ int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) struct drm_gem_object *obj = vma->vm_private_data; struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); struct drm_device *dev = obj->dev; - unsigned long pfn; + unsigned long f_vaddr; pgoff_t page_offset; int ret;
page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >> PAGE_SHIFT; + f_vaddr = (unsigned long)vmf->virtual_address;
mutex_lock(&dev->struct_mutex);
- pfn = (((unsigned long)exynos_gem_obj->buffer->dma_addr) >> - PAGE_SHIFT) + page_offset; + /* + * allocate all pages as desired size if user wants to allocate + * physically non-continuous memory. + */ + if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { + ret = exynos_drm_gem_get_pages(obj); + if (ret < 0) + goto err; + }
- ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn); + ret = exynos_drm_gem_map_pages(obj, vma, f_vaddr, page_offset); + if (ret < 0) + DRM_ERROR("failed to map pages.\n");
+err: mutex_unlock(&dev->struct_mutex);
return convert_to_vm_err_msg(ret); diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index 67cdc91..096267d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h @@ -36,11 +36,15 @@ * @dma_addr: bus address(accessed by dma) to allocated memory region. * - this address could be physical address without IOMMU and * device address with IOMMU. + * @sgt: sg table to transfer page data. + * @pages: contain all pages to allocated memory region. * @size: size of allocated memory region. */ struct exynos_drm_gem_buf { void __iomem *kvaddr; dma_addr_t dma_addr; + struct sg_table *sgt; + struct page **pages; unsigned long size; };
@@ -55,6 +59,8 @@ struct exynos_drm_gem_buf { * by user request or at framebuffer creation. * continuous memory region allocated by user request * or at framebuffer creation. + * @size: total memory size to physically non-continuous memory region. + * @flags: indicate memory type to allocated buffer and cache attruibute. * * P.S. this object would be transfered to user as kms_bo.handle so * user can access the buffer through kms_bo.handle. @@ -62,6 +68,8 @@ struct exynos_drm_gem_buf { struct exynos_drm_gem_obj { struct drm_gem_object base; struct exynos_drm_gem_buf *buffer; + unsigned long size; + unsigned int flags; };
/* destroy a buffer with gem object */ @@ -69,7 +77,8 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj);
/* create a new buffer with gem object */ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, - unsigned long size); + unsigned int flags, + unsigned long size);
/* * request gem object creation and buffer allocation as the size diff --git a/include/drm/exynos_drm.h b/include/drm/exynos_drm.h index aff2313..81c9cb7 100644 --- a/include/drm/exynos_drm.h +++ b/include/drm/exynos_drm.h @@ -79,6 +79,12 @@ struct drm_exynos_plane_set_zpos { __s32 zpos; };
+/* memory type definitions. */ +enum e_drm_exynos_gem_mem_type { + /* Physically Non-Continuous memory. */ + EXYNOS_BO_NONCONTIG = 1 << 0 +}; + #define DRM_EXYNOS_GEM_CREATE 0x00 #define DRM_EXYNOS_GEM_MAP_OFFSET 0x01 #define DRM_EXYNOS_GEM_MMAP 0x02
this function would be used for drm based 2d acceleration driver to get/put dma address through gem handle. when exynos_drm_get_dma_address is called reference count of gem object would be increased not to be released by gem close and when exynos_drm_put_dma_address is called the reference count of this gem object would be decreased to be released.
Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_gem.c | 58 +++++++++++++++++++++++++++++++ drivers/gpu/drm/exynos/exynos_drm_gem.h | 18 +++++++++ 2 files changed, 76 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 36d081d..ddcb7e3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -377,6 +377,64 @@ int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, return 0; }
+void *exynos_drm_gem_get_dma_addr(struct drm_device *dev, + unsigned int gem_handle, + struct drm_file *file_priv) +{ + struct exynos_drm_gem_obj *exynos_gem_obj; + struct drm_gem_object *obj; + + obj = drm_gem_object_lookup(dev, file_priv, gem_handle); + if (!obj) { + DRM_ERROR("failed to lookup gem object.\n"); + return ERR_PTR(-EINVAL); + } + + exynos_gem_obj = to_exynos_gem_obj(obj); + + if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { + DRM_DEBUG_KMS("not support NONCONTIG type.\n"); + drm_gem_object_unreference_unlocked(obj); + + /* TODO */ + return ERR_PTR(-EINVAL); + } + + return &exynos_gem_obj->buffer->dma_addr; +} + +void exynos_drm_gem_put_dma_addr(struct drm_device *dev, + unsigned int gem_handle, + struct drm_file *file_priv) +{ + struct exynos_drm_gem_obj *exynos_gem_obj; + struct drm_gem_object *obj; + + obj = drm_gem_object_lookup(dev, file_priv, gem_handle); + if (!obj) { + DRM_ERROR("failed to lookup gem object.\n"); + return; + } + + exynos_gem_obj = to_exynos_gem_obj(obj); + + if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { + DRM_DEBUG_KMS("not support NONCONTIG type.\n"); + drm_gem_object_unreference_unlocked(obj); + + /* TODO */ + return; + } + + drm_gem_object_unreference_unlocked(obj); + + /* + * decrease obj->refcount one more time because we has already + * increased it at exynos_drm_gem_get_dma_addr(). + */ + drm_gem_object_unreference_unlocked(obj); +} + int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index 096267d..e40fbad 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h @@ -88,6 +88,24 @@ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
+/* + * get dma address from gem handle and this function could be used for + * other drivers such as 2d/3d acceleration drivers. + * with this function call, gem object reference count would be increased. + */ +void *exynos_drm_gem_get_dma_addr(struct drm_device *dev, + unsigned int gem_handle, + struct drm_file *file_priv); + +/* + * put dma address from gem handle and this function could be used for + * other drivers such as 2d/3d acceleration drivers. + * with this function call, gem object reference count would be decreased. + */ +void exynos_drm_gem_put_dma_addr(struct drm_device *dev, + unsigned int gem_handle, + struct drm_file *file_priv); + /* get buffer offset to map to user space. */ int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
From: Joonyoung Shim jy0922.shim@samsung.com
We should release pending pageflip events when closed. If not, they will be dangling events.
Signed-off-by: Joonyoung Shim jy0922.shim@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_drv.c | 14 ++++++++++++++ 1 files changed, 14 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 09cc13f..aaa1b40 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -147,8 +147,22 @@ static int exynos_drm_unload(struct drm_device *dev) static void exynos_drm_preclose(struct drm_device *dev, struct drm_file *file) { + struct exynos_drm_private *private = dev->dev_private; + struct drm_pending_vblank_event *e, *t; + unsigned long flags; + DRM_DEBUG_DRIVER("%s\n", __FILE__);
+ /* release events of current file */ + spin_lock_irqsave(&dev->event_lock, flags); + list_for_each_entry_safe(e, t, &private->pageflip_event_list, + base.link) { + if (e->base.file_priv == file) { + list_del(&e->base.link); + e->base.destroy(&e->base); + } + } + spin_unlock_irqrestore(&dev->event_lock, flags); }
static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
From: Joonyoung Shim jy0922.shim@samsung.com
The exynos drm driver has several subdrv. They each can be module but it causes unfixed probe order of exynodr drm driver and each subdrv. It also needs some weird codes such as exynos_drm_fbdev_reinit and exynos_drm_mode_group_reinit. This patch can remove weird codes and clear codes through we doesn't modularity each subdrv.
Also this removes unnecessary codes related module.
Signed-off-by: Joonyoung Shim jy0922.shim@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/Kconfig | 8 +-- drivers/gpu/drm/exynos/Makefile | 10 ++- drivers/gpu/drm/exynos/exynos_ddc.c | 1 - drivers/gpu/drm/exynos/exynos_drm_buf.c | 4 - drivers/gpu/drm/exynos/exynos_drm_connector.c | 6 -- drivers/gpu/drm/exynos/exynos_drm_core.c | 112 ++----------------------- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 6 -- drivers/gpu/drm/exynos/exynos_drm_drv.c | 52 +++++++++++- drivers/gpu/drm/exynos/exynos_drm_drv.h | 12 ++-- drivers/gpu/drm/exynos/exynos_drm_encoder.c | 6 -- drivers/gpu/drm/exynos/exynos_drm_fb.c | 6 -- drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 86 ------------------- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 20 +---- drivers/gpu/drm/exynos/exynos_drm_gem.c | 4 - drivers/gpu/drm/exynos/exynos_drm_hdmi.c | 87 +------------------ drivers/gpu/drm/exynos/exynos_hdmi.c | 9 -- drivers/gpu/drm/exynos/exynos_mixer.c | 7 -- 17 files changed, 77 insertions(+), 359 deletions(-)
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index b9e5266..9a9850a 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -1,7 +1,6 @@ config DRM_EXYNOS tristate "DRM Support for Samsung SoC EXYNOS Series" depends on DRM && PLAT_SAMSUNG - default n select DRM_KMS_HELPER select FB_CFB_FILLRECT select FB_CFB_COPYAREA @@ -12,16 +11,13 @@ config DRM_EXYNOS If M is selected the module will be called exynosdrm.
config DRM_EXYNOS_FIMD - tristate "Exynos DRM FIMD" + bool "Exynos DRM FIMD" depends on DRM_EXYNOS && !FB_S3C - default n help Choose this option if you want to use Exynos FIMD for DRM. - If M is selected, the module will be called exynos_drm_fimd
config DRM_EXYNOS_HDMI - tristate "Exynos DRM HDMI" + bool "Exynos DRM HDMI" depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_TV help Choose this option if you want to use Exynos HDMI for DRM. - If M is selected, the module will be called exynos_drm_hdmi diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index 395e69c..5331fa3 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile @@ -8,7 +8,9 @@ exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \ exynos_drm_buf.o exynos_drm_gem.o exynos_drm_core.o \ exynos_drm_plane.o
-obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o -obj-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o -obj-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o exynos_ddc.o \ - exynos_hdmiphy.o exynos_drm_hdmi.o +exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o +exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o \ + exynos_ddc.o exynos_hdmiphy.o \ + exynos_drm_hdmi.o + +obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o diff --git a/drivers/gpu/drm/exynos/exynos_ddc.c b/drivers/gpu/drm/exynos/exynos_ddc.c index 84b614f..7e1051d 100644 --- a/drivers/gpu/drm/exynos/exynos_ddc.c +++ b/drivers/gpu/drm/exynos/exynos_ddc.c @@ -55,4 +55,3 @@ struct i2c_driver ddc_driver = { .remove = __devexit_p(s5p_ddc_remove), .command = NULL, }; -EXPORT_SYMBOL(ddc_driver); diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.c b/drivers/gpu/drm/exynos/exynos_drm_buf.c index 554f674..0fb5ceb 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_buf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_buf.c @@ -211,7 +211,3 @@ void exynos_drm_buf_destroy(struct drm_device *dev, kfree(buffer); buffer = NULL; } - -MODULE_AUTHOR("Inki Dae inki.dae@samsung.com"); -MODULE_DESCRIPTION("Samsung SoC DRM Buffer Management Module"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c index 303af60..33893a9 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_connector.c +++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c @@ -348,9 +348,3 @@ err_connector: kfree(exynos_connector); return NULL; } - -MODULE_AUTHOR("Inki Dae inki.dae@samsung.com"); -MODULE_AUTHOR("Joonyoung Shim jy0922.shim@samsung.com"); -MODULE_AUTHOR("Seung-Woo Kim sw0312.kim@samsung.com"); -MODULE_DESCRIPTION("Samsung SoC DRM Connector Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c index d08a558..937b059 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_core.c +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c @@ -32,7 +32,6 @@ #include "exynos_drm_connector.h" #include "exynos_drm_fbdev.h"
-static DEFINE_MUTEX(exynos_drm_mutex); static LIST_HEAD(exynos_drm_subdrv_list); static struct drm_device *drm_dev;
@@ -116,13 +115,10 @@ int exynos_drm_device_register(struct drm_device *dev) if (!dev) return -EINVAL;
- if (drm_dev) { - DRM_ERROR("Already drm device were registered\n"); - return -EBUSY; - } + drm_dev = dev;
- mutex_lock(&exynos_drm_mutex); list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) { + subdrv->drm_dev = dev; err = exynos_drm_subdrv_probe(dev, subdrv); if (err) { DRM_DEBUG("exynos drm subdrv probe failed.\n"); @@ -130,9 +126,6 @@ int exynos_drm_device_register(struct drm_device *dev) } }
- drm_dev = dev; - mutex_unlock(&exynos_drm_mutex); - return 0; } EXPORT_SYMBOL_GPL(exynos_drm_device_register); @@ -143,86 +136,28 @@ int exynos_drm_device_unregister(struct drm_device *dev)
DRM_DEBUG_DRIVER("%s\n", __FILE__);
- if (!dev || dev != drm_dev) { + if (!dev) { WARN(1, "Unexpected drm device unregister!\n"); return -EINVAL; }
- mutex_lock(&exynos_drm_mutex); list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) exynos_drm_subdrv_remove(dev, subdrv);
drm_dev = NULL; - mutex_unlock(&exynos_drm_mutex);
return 0; } EXPORT_SYMBOL_GPL(exynos_drm_device_unregister);
-static int exynos_drm_mode_group_reinit(struct drm_device *dev) -{ - struct drm_mode_group *group = &dev->primary->mode_group; - uint32_t *id_list = group->id_list; - int ret; - - DRM_DEBUG_DRIVER("%s\n", __FILE__); - - ret = drm_mode_group_init_legacy_group(dev, group); - if (ret < 0) - return ret; - - kfree(id_list); - return 0; -} - int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv) { - int err; - DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (!subdrv) return -EINVAL;
- mutex_lock(&exynos_drm_mutex); - if (drm_dev) { - err = exynos_drm_subdrv_probe(drm_dev, subdrv); - if (err) { - DRM_ERROR("failed to probe exynos drm subdrv\n"); - mutex_unlock(&exynos_drm_mutex); - return err; - } - - /* setup possible_clones. */ - exynos_drm_encoder_setup(drm_dev); - - /* - * if any specific driver such as fimd or hdmi driver called - * exynos_drm_subdrv_register() later than drm_load(), - * the fb helper should be re-initialized and re-configured. - */ - err = exynos_drm_fbdev_reinit(drm_dev); - if (err) { - DRM_ERROR("failed to reinitialize exynos drm fbdev\n"); - exynos_drm_subdrv_remove(drm_dev, subdrv); - mutex_unlock(&exynos_drm_mutex); - return err; - } - - err = exynos_drm_mode_group_reinit(drm_dev); - if (err) { - DRM_ERROR("failed to reinitialize mode group\n"); - exynos_drm_fbdev_fini(drm_dev); - exynos_drm_subdrv_remove(drm_dev, subdrv); - mutex_unlock(&exynos_drm_mutex); - return err; - } - } - - subdrv->drm_dev = drm_dev; - list_add_tail(&subdrv->list, &exynos_drm_subdrv_list); - mutex_unlock(&exynos_drm_mutex);
return 0; } @@ -230,46 +165,13 @@ EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register);
int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv) { - int ret = -EFAULT; - DRM_DEBUG_DRIVER("%s\n", __FILE__);
- if (!subdrv) { - DRM_DEBUG("Unexpected exynos drm subdrv unregister!\n"); - return ret; - } - - mutex_lock(&exynos_drm_mutex); - if (drm_dev) { - exynos_drm_subdrv_remove(drm_dev, subdrv); - list_del(&subdrv->list); - - /* - * fb helper should be updated once a sub driver is released - * to re-configure crtc and connector and also to re-setup - * drm framebuffer. - */ - ret = exynos_drm_fbdev_reinit(drm_dev); - if (ret < 0) { - DRM_ERROR("failed fb helper reinit.\n"); - goto fail; - } + if (!subdrv) + return -EINVAL;
- ret = exynos_drm_mode_group_reinit(drm_dev); - if (ret < 0) { - DRM_ERROR("failed drm mode group reinit.\n"); - goto fail; - } - } + list_del(&subdrv->list);
-fail: - mutex_unlock(&exynos_drm_mutex); - return ret; + return 0; } EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister); - -MODULE_AUTHOR("Inki Dae inki.dae@samsung.com"); -MODULE_AUTHOR("Joonyoung Shim jy0922.shim@samsung.com"); -MODULE_AUTHOR("Seung-Woo Kim sw0312.kim@samsung.com"); -MODULE_DESCRIPTION("Samsung SoC DRM Core Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 2d9a0e6..3486ffe 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -430,9 +430,3 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc) exynos_drm_fn_encoder(private->crtc[crtc], &crtc, exynos_drm_disable_vblank); } - -MODULE_AUTHOR("Inki Dae inki.dae@samsung.com"); -MODULE_AUTHOR("Joonyoung Shim jy0922.shim@samsung.com"); -MODULE_AUTHOR("Seung-Woo Kim sw0312.kim@samsung.com"); -MODULE_DESCRIPTION("Samsung SoC DRM CRTC Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index aaa1b40..a2f8fae 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -266,9 +266,49 @@ static struct platform_driver exynos_drm_platform_driver = {
static int __init exynos_drm_init(void) { + int ret; + DRM_DEBUG_DRIVER("%s\n", __FILE__);
- return platform_driver_register(&exynos_drm_platform_driver); +#ifdef CONFIG_DRM_EXYNOS_FIMD + ret = platform_driver_register(&fimd_driver); + if (ret < 0) + goto out_fimd; +#endif + +#ifdef CONFIG_DRM_EXYNOS_HDMI + ret = platform_driver_register(&hdmi_driver); + if (ret < 0) + goto out_hdmi; + ret = platform_driver_register(&mixer_driver); + if (ret < 0) + goto out_mixer; + ret = platform_driver_register(&exynos_drm_common_hdmi_driver); + if (ret < 0) + goto out_common_hdmi; +#endif + + ret = platform_driver_register(&exynos_drm_platform_driver); + if (ret < 0) + goto out; + + return 0; + +out: +#ifdef CONFIG_DRM_EXYNOS_HDMI + platform_driver_unregister(&exynos_drm_common_hdmi_driver); +out_common_hdmi: + platform_driver_unregister(&mixer_driver); +out_mixer: + platform_driver_unregister(&hdmi_driver); +out_hdmi: +#endif + +#ifdef CONFIG_DRM_EXYNOS_FIMD + platform_driver_unregister(&fimd_driver); +out_fimd: +#endif + return ret; }
static void __exit exynos_drm_exit(void) @@ -276,6 +316,16 @@ static void __exit exynos_drm_exit(void) DRM_DEBUG_DRIVER("%s\n", __FILE__);
platform_driver_unregister(&exynos_drm_platform_driver); + +#ifdef CONFIG_DRM_EXYNOS_HDMI + platform_driver_unregister(&exynos_drm_common_hdmi_driver); + platform_driver_unregister(&mixer_driver); + platform_driver_unregister(&hdmi_driver); +#endif + +#ifdef CONFIG_DRM_EXYNOS_FIMD + platform_driver_unregister(&fimd_driver); +#endif }
module_init(exynos_drm_init); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 4115a9f..f8bac0e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -262,15 +262,15 @@ int exynos_drm_device_unregister(struct drm_device *dev); * this function would be called by sub drivers such as display controller * or hdmi driver to register this sub driver object to exynos drm driver * and when a sub driver is registered to exynos drm driver a probe callback - * of the sub driver is called and creates its own encoder and connector - * and then fb helper and drm mode group would be re-initialized. + * of the sub driver is called and creates its own encoder and connector. */ int exynos_drm_subdrv_register(struct exynos_drm_subdrv *drm_subdrv);
-/* - * this function removes subdrv list from exynos drm driver and fb helper - * and drm mode group would be re-initialized. - */ +/* this function removes subdrv list from exynos drm driver */ int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *drm_subdrv);
+extern struct platform_driver fimd_driver; +extern struct platform_driver hdmi_driver; +extern struct platform_driver mixer_driver; +extern struct platform_driver exynos_drm_common_hdmi_driver; #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c index 45ca732..2278676 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c @@ -442,9 +442,3 @@ void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data) if (overlay_ops && overlay_ops->disable) overlay_ops->disable(manager->dev, zpos); } - -MODULE_AUTHOR("Inki Dae inki.dae@samsung.com"); -MODULE_AUTHOR("Joonyoung Shim jy0922.shim@samsung.com"); -MODULE_AUTHOR("Seung-Woo Kim sw0312.kim@samsung.com"); -MODULE_DESCRIPTION("Samsung SoC DRM Encoder Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 3733fe6..c38c8f4 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -211,9 +211,3 @@ void exynos_drm_mode_config_init(struct drm_device *dev)
dev->mode_config.funcs = &exynos_drm_mode_config_funcs; } - -MODULE_AUTHOR("Inki Dae inki.dae@samsung.com"); -MODULE_AUTHOR("Joonyoung Shim jy0922.shim@samsung.com"); -MODULE_AUTHOR("Seung-Woo Kim sw0312.kim@samsung.com"); -MODULE_DESCRIPTION("Samsung SoC DRM FB Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 4e86d15..d033c62 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -342,89 +342,3 @@ void exynos_drm_fbdev_restore_mode(struct drm_device *dev)
drm_fb_helper_restore_fbdev_mode(private->fb_helper); } - -int exynos_drm_fbdev_reinit(struct drm_device *dev) -{ - struct exynos_drm_private *private = dev->dev_private; - struct drm_fb_helper *fb_helper; - int ret; - - if (!private) - return -EINVAL; - - /* - * if all sub drivers were unloaded then num_connector is 0 - * so at this time, the framebuffers also should be destroyed. - */ - if (!dev->mode_config.num_connector) { - exynos_drm_fbdev_fini(dev); - return 0; - } - - fb_helper = private->fb_helper; - - if (fb_helper) { - struct list_head temp_list; - - INIT_LIST_HEAD(&temp_list); - - /* - * fb_helper is reintialized but kernel fb is reused - * so kernel_fb_list need to be backuped and restored - */ - if (!list_empty(&fb_helper->kernel_fb_list)) - list_replace_init(&fb_helper->kernel_fb_list, - &temp_list); - - drm_fb_helper_fini(fb_helper); - - ret = drm_fb_helper_init(dev, fb_helper, - dev->mode_config.num_crtc, MAX_CONNECTOR); - if (ret < 0) { - DRM_ERROR("failed to initialize drm fb helper\n"); - return ret; - } - - if (!list_empty(&temp_list)) - list_replace(&temp_list, &fb_helper->kernel_fb_list); - - ret = drm_fb_helper_single_add_all_connectors(fb_helper); - if (ret < 0) { - DRM_ERROR("failed to add fb helper to connectors\n"); - goto err; - } - - ret = drm_fb_helper_initial_config(fb_helper, PREFERRED_BPP); - if (ret < 0) { - DRM_ERROR("failed to set up hw configuration.\n"); - goto err; - } - } else { - /* - * if drm_load() failed whem drm load() was called prior - * to specific drivers, fb_helper must be NULL and so - * this fuction should be called again to re-initialize and - * re-configure the fb helper. it means that this function - * has been called by the specific drivers. - */ - ret = exynos_drm_fbdev_init(dev); - } - - return ret; - -err: - /* - * if drm_load() failed when drm load() was called prior - * to specific drivers, the fb_helper must be NULL and so check it. - */ - if (fb_helper) - drm_fb_helper_fini(fb_helper); - - return ret; -} - -MODULE_AUTHOR("Inki Dae inki.dae@samsung.com"); -MODULE_AUTHOR("Joonyoung Shim jy0922.shim@samsung.com"); -MODULE_AUTHOR("Seung-Woo Kim sw0312.kim@samsung.com"); -MODULE_DESCRIPTION("Samsung SoC DRM FBDEV Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 56458ee..ecb6db2 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -1007,7 +1007,7 @@ static const struct dev_pm_ops fimd_pm_ops = { SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL) };
-static struct platform_driver fimd_driver = { +struct platform_driver fimd_driver = { .probe = fimd_probe, .remove = __devexit_p(fimd_remove), .driver = { @@ -1016,21 +1016,3 @@ static struct platform_driver fimd_driver = { .pm = &fimd_pm_ops, }, }; - -static int __init fimd_init(void) -{ - return platform_driver_register(&fimd_driver); -} - -static void __exit fimd_exit(void) -{ - platform_driver_unregister(&fimd_driver); -} - -module_init(fimd_init); -module_exit(fimd_exit); - -MODULE_AUTHOR("Joonyoung Shim jy0922.shim@samsung.com"); -MODULE_AUTHOR("Inki Dae inki.dae@samsung.com"); -MODULE_DESCRIPTION("Samsung DRM FIMD Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index ddcb7e3..9bd7a7f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -727,7 +727,3 @@ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
return ret; } - -MODULE_AUTHOR("Inki Dae inki.dae@samsung.com"); -MODULE_DESCRIPTION("Samsung SoC DRM GEM Module"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c index ed86bdd..14eb26b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c @@ -38,7 +38,6 @@ struct drm_hdmi_context { struct exynos_drm_subdrv subdrv; struct exynos_drm_hdmi_context *hdmi_ctx; struct exynos_drm_hdmi_context *mixer_ctx; - struct work_struct work; };
void exynos_drm_display_ops_register(struct exynos_hdmi_display_ops @@ -49,7 +48,6 @@ void exynos_drm_display_ops_register(struct exynos_hdmi_display_ops if (display_ops) hdmi_display_ops = display_ops; } -EXPORT_SYMBOL(exynos_drm_display_ops_register);
void exynos_drm_manager_ops_register(struct exynos_hdmi_manager_ops *manager_ops) @@ -59,7 +57,6 @@ void exynos_drm_manager_ops_register(struct exynos_hdmi_manager_ops if (manager_ops) hdmi_manager_ops = manager_ops; } -EXPORT_SYMBOL(exynos_drm_manager_ops_register);
void exynos_drm_overlay_ops_register(struct exynos_hdmi_overlay_ops *overlay_ops) @@ -69,7 +66,6 @@ void exynos_drm_overlay_ops_register(struct exynos_hdmi_overlay_ops if (overlay_ops) hdmi_overlay_ops = overlay_ops; } -EXPORT_SYMBOL(exynos_drm_overlay_ops_register);
static bool drm_hdmi_is_connected(struct device *dev) { @@ -277,7 +273,6 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev, struct drm_hdmi_context *ctx; struct platform_device *pdev = to_platform_device(dev); struct exynos_drm_common_hdmi_pd *pd; - int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
@@ -298,26 +293,13 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev, return -EFAULT; }
- ret = platform_driver_register(&hdmi_driver); - if (ret) { - DRM_DEBUG_KMS("failed to register hdmi driver.\n"); - return ret; - } - - ret = platform_driver_register(&mixer_driver); - if (ret) { - DRM_DEBUG_KMS("failed to register mixer driver.\n"); - goto err_hdmidrv; - } - ctx = get_ctx_from_subdrv(subdrv);
ctx->hdmi_ctx = (struct exynos_drm_hdmi_context *) to_context(pd->hdmi_dev); if (!ctx->hdmi_ctx) { DRM_DEBUG_KMS("hdmi context is null.\n"); - ret = -EFAULT; - goto err_mixerdrv; + return -EFAULT; }
ctx->hdmi_ctx->drm_dev = drm_dev; @@ -326,42 +308,12 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev, to_context(pd->mixer_dev); if (!ctx->mixer_ctx) { DRM_DEBUG_KMS("mixer context is null.\n"); - ret = -EFAULT; - goto err_mixerdrv; + return -EFAULT; }
ctx->mixer_ctx->drm_dev = drm_dev;
return 0; - -err_mixerdrv: - platform_driver_unregister(&mixer_driver); -err_hdmidrv: - platform_driver_unregister(&hdmi_driver); - return ret; -} - -static void hdmi_subdrv_remove(struct drm_device *drm_dev) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - platform_driver_unregister(&hdmi_driver); - platform_driver_unregister(&mixer_driver); -} - -static void exynos_drm_hdmi_late_probe(struct work_struct *work) -{ - struct drm_hdmi_context *ctx = container_of(work, - struct drm_hdmi_context, work); - - /* - * this function calls subdrv->probe() so this must be called - * after probe context. - * - * PS. subdrv->probe() will call platform_driver_register() to probe - * hdmi and mixer driver. - */ - exynos_drm_subdrv_register(&ctx->subdrv); }
static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev) @@ -381,7 +333,6 @@ static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev) subdrv = &ctx->subdrv;
subdrv->probe = hdmi_subdrv_probe; - subdrv->remove = hdmi_subdrv_remove; subdrv->manager.pipe = -1; subdrv->manager.ops = &drm_hdmi_manager_ops; subdrv->manager.overlay_ops = &drm_hdmi_overlay_ops; @@ -390,9 +341,7 @@ static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, subdrv);
- INIT_WORK(&ctx->work, exynos_drm_hdmi_late_probe); - - schedule_work(&ctx->work); + exynos_drm_subdrv_register(subdrv);
return 0; } @@ -428,7 +377,7 @@ static int __devexit exynos_drm_hdmi_remove(struct platform_device *pdev) return 0; }
-static struct platform_driver exynos_drm_common_hdmi_driver = { +struct platform_driver exynos_drm_common_hdmi_driver = { .probe = exynos_drm_hdmi_probe, .remove = __devexit_p(exynos_drm_hdmi_remove), .driver = { @@ -437,31 +386,3 @@ static struct platform_driver exynos_drm_common_hdmi_driver = { .pm = &hdmi_pm_ops, }, }; - -static int __init exynos_drm_hdmi_init(void) -{ - int ret; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - ret = platform_driver_register(&exynos_drm_common_hdmi_driver); - if (ret) { - DRM_DEBUG_KMS("failed to register hdmi common driver.\n"); - return ret; - } - - return ret; -} - -static void __exit exynos_drm_hdmi_exit(void) -{ - platform_driver_unregister(&exynos_drm_common_hdmi_driver); -} - -module_init(exynos_drm_hdmi_init); -module_exit(exynos_drm_hdmi_exit); - -MODULE_AUTHOR("Inki Dae inki.dae@samsung.com"); -MODULE_AUTHOR("Seung-Woo Kim, sw0312.kim@samsung.com"); -MODULE_DESCRIPTION("Samsung SoC DRM HDMI Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 6fe1e89..3fe5b2e 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1998,14 +1998,12 @@ void hdmi_attach_ddc_client(struct i2c_client *ddc) if (ddc) hdmi_ddc = ddc; } -EXPORT_SYMBOL(hdmi_attach_ddc_client);
void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy) { if (hdmiphy) hdmi_hdmiphy = hdmiphy; } -EXPORT_SYMBOL(hdmi_attach_hdmiphy_client);
static int __devinit hdmi_probe(struct platform_device *pdev) { @@ -2188,10 +2186,3 @@ struct platform_driver hdmi_driver = { .pm = &hdmi_pm_ops, }, }; -EXPORT_SYMBOL(hdmi_driver); - -MODULE_AUTHOR("Seung-Woo Kim, sw0312.kim@samsung.com"); -MODULE_AUTHOR("Inki Dae inki.dae@samsung.com"); -MODULE_AUTHOR("Joonyoung Shim jy0922.shim@samsung.com"); -MODULE_DESCRIPTION("Samsung DRM HDMI core Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 93846e8..242cbcd 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -1066,10 +1066,3 @@ struct platform_driver mixer_driver = { .probe = mixer_probe, .remove = __devexit_p(mixer_remove), }; -EXPORT_SYMBOL(mixer_driver); - -MODULE_AUTHOR("Seung-Woo Kim, sw0312.kim@samsung.com"); -MODULE_AUTHOR("Inki Dae inki.dae@samsung.com"); -MODULE_AUTHOR("Joonyoung Shim jy0922.shim@samsung.com"); -MODULE_DESCRIPTION("Samsung DRM HDMI mixer Driver"); -MODULE_LICENSE("GPL");
From: Joonyoung Shim jy0922.shim@samsung.com
Some subdrv need open and close functions call when open and close drm.
Signed-off-by: Joonyoung Shim jy0922.shim@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_core.c | 35 ++++++++++++++++++++++++++++++ drivers/gpu/drm/exynos/exynos_drm_drv.c | 10 ++++++++ drivers/gpu/drm/exynos/exynos_drm_drv.h | 9 +++++++ 3 files changed, 54 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c index 937b059..4e29c71 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_core.c +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c @@ -175,3 +175,38 @@ int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv) return 0; } EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister); + +int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file) +{ + struct exynos_drm_subdrv *subdrv; + int ret; + + list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) { + if (subdrv->open) { + ret = subdrv->open(dev, subdrv->manager.dev, file); + if (ret) + goto err; + } + } + + return 0; + +err: + list_for_each_entry_reverse(subdrv, &subdrv->list, list) { + if (subdrv->close) + subdrv->close(dev, subdrv->manager.dev, file); + } + return ret; +} +EXPORT_SYMBOL_GPL(exynos_drm_subdrv_open); + +void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file) +{ + struct exynos_drm_subdrv *subdrv; + + list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) { + if (subdrv->close) + subdrv->close(dev, subdrv->manager.dev, file); + } +} +EXPORT_SYMBOL_GPL(exynos_drm_subdrv_close); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index a2f8fae..b4e265f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -144,6 +144,13 @@ static int exynos_drm_unload(struct drm_device *dev) return 0; }
+static int exynos_drm_open(struct drm_device *dev, struct drm_file *file) +{ + DRM_DEBUG_DRIVER("%s\n", __FILE__); + + return exynos_drm_subdrv_open(dev, file); +} + static void exynos_drm_preclose(struct drm_device *dev, struct drm_file *file) { @@ -163,6 +170,8 @@ static void exynos_drm_preclose(struct drm_device *dev, } } spin_unlock_irqrestore(&dev->event_lock, flags); + + exynos_drm_subdrv_close(dev, file); }
static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file) @@ -216,6 +225,7 @@ static struct drm_driver exynos_drm_driver = { DRIVER_MODESET | DRIVER_GEM, .load = exynos_drm_load, .unload = exynos_drm_unload, + .open = exynos_drm_open, .preclose = exynos_drm_preclose, .lastclose = exynos_drm_lastclose, .postclose = exynos_drm_postclose, diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index f8bac0e..a412454 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -229,6 +229,8 @@ struct exynos_drm_private { * subdrv is registered to it. * @remove: this callback is used to release resources created * by probe callback. + * @open: this would be called with drm device file open. + * @close: this would be called with drm device file close. * @manager: subdrv has its own manager to control a hardware appropriately * and we can access a hardware drawing on this manager. * @encoder: encoder object owned by this sub driver. @@ -240,6 +242,10 @@ struct exynos_drm_subdrv {
int (*probe)(struct drm_device *drm_dev, struct device *dev); void (*remove)(struct drm_device *dev); + int (*open)(struct drm_device *drm_dev, struct device *dev, + struct drm_file *file); + void (*close)(struct drm_device *drm_dev, struct device *dev, + struct drm_file *file);
struct exynos_drm_manager manager; struct drm_encoder *encoder; @@ -269,6 +275,9 @@ int exynos_drm_subdrv_register(struct exynos_drm_subdrv *drm_subdrv); /* this function removes subdrv list from exynos drm driver */ int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *drm_subdrv);
+int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file); +void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file); + extern struct platform_driver fimd_driver; extern struct platform_driver hdmi_driver; extern struct platform_driver mixer_driver;
From: Joonyoung Shim jy0922.shim@samsung.com
The is_local member indicates unused subdrv such connector and encoder so doesn't make resources for them.
Signed-off-by: Joonyoung Shim jy0922.shim@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_core.c | 3 +++ drivers/gpu/drm/exynos/exynos_drm_drv.h | 2 ++ 2 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c index 4e29c71..411832e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_core.c +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c @@ -59,6 +59,9 @@ static int exynos_drm_subdrv_probe(struct drm_device *dev, return ret; }
+ if (subdrv->is_local) + return 0; + /* create and initialize a encoder for this sub driver. */ encoder = exynos_drm_encoder_create(dev, &subdrv->manager, (1 << MAX_CRTC) - 1); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index a412454..b26c2f4 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -225,6 +225,7 @@ struct exynos_drm_private { * @list: sub driver has its own list object to register to exynos drm driver. * @drm_dev: pointer to drm_device and this pointer would be set * when sub driver calls exynos_drm_subdrv_register(). + * @is_local: appear encoder and connector disrelated device. * @probe: this callback would be called by exynos drm driver after * subdrv is registered to it. * @remove: this callback is used to release resources created @@ -239,6 +240,7 @@ struct exynos_drm_private { struct exynos_drm_subdrv { struct list_head list; struct drm_device *drm_dev; + bool is_local;
int (*probe)(struct drm_device *drm_dev, struct device *dev); void (*remove)(struct drm_device *dev);
From: Joonyoung Shim jy0922.shim@samsung.com
G2D is a 2D graphic accelerator that supports Bit Block Transfer. This G2D driver is exynos drm specific and supports only exynos4x12 series. user application fills command set in cmdlist and once dma start request these cmdlists are parsed and performed by dma.
This adds below three exynos specific ioctl and one event for G2D. - DRM_EXYNOS_G2D_GET_VER - DRM_EXYNOS_G2D_SET_CMDLIST - DRM_EXYNOS_G2D_EXEC - DRM_EXYNOS_G2D_EVENT
Signed-off-by: Joonyoung Shim jy0922.shim@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/Kconfig | 6 + drivers/gpu/drm/exynos/Makefile | 1 + drivers/gpu/drm/exynos/exynos_drm_drv.c | 31 ++ drivers/gpu/drm/exynos/exynos_drm_drv.h | 11 + drivers/gpu/drm/exynos/exynos_drm_g2d.c | 864 +++++++++++++++++++++++++++++++ drivers/gpu/drm/exynos/exynos_drm_g2d.h | 36 ++ include/drm/exynos_drm.h | 56 ++ 7 files changed, 1005 insertions(+), 0 deletions(-) create mode 100644 drivers/gpu/drm/exynos/exynos_drm_g2d.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_g2d.h
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 9a9850a..8493fe9 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -21,3 +21,9 @@ config DRM_EXYNOS_HDMI depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_TV help Choose this option if you want to use Exynos HDMI for DRM. + +config DRM_EXYNOS_G2D + bool "Exynos DRM G2D" + depends on DRM_EXYNOS + help + Choose this option if you want to use Exynos G2D for DRM. diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index 5331fa3..d6c1a3c 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile @@ -12,5 +12,6 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o \ exynos_ddc.o exynos_hdmiphy.o \ exynos_drm_hdmi.o +exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o
obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index b4e265f..2b72c5d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -37,6 +37,7 @@ #include "exynos_drm_fbdev.h" #include "exynos_drm_fb.h" #include "exynos_drm_gem.h" +#include "exynos_drm_g2d.h" #include "exynos_drm_plane.h"
#define DRIVER_NAME "exynos" @@ -146,8 +147,16 @@ static int exynos_drm_unload(struct drm_device *dev)
static int exynos_drm_open(struct drm_device *dev, struct drm_file *file) { + struct drm_exynos_file_private *file_priv; + DRM_DEBUG_DRIVER("%s\n", __FILE__);
+ file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL); + if (!file_priv) + return -ENOMEM; + + file->driver_priv = file_priv; + return exynos_drm_subdrv_open(dev, file); }
@@ -208,6 +217,13 @@ static struct drm_ioctl_desc exynos_ioctls[] = { exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH), DRM_IOCTL_DEF_DRV(EXYNOS_PLANE_SET_ZPOS, exynos_plane_set_zpos_ioctl, DRM_UNLOCKED | DRM_AUTH), + + DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER, + exynos_g2d_get_ver_ioctl, DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(EXYNOS_G2D_SET_CMDLIST, + exynos_g2d_set_cmdlist_ioctl, DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, + exynos_g2d_exec_ioctl, DRM_UNLOCKED | DRM_AUTH), };
static const struct file_operations exynos_drm_driver_fops = { @@ -298,6 +314,12 @@ static int __init exynos_drm_init(void) goto out_common_hdmi; #endif
+#ifdef CONFIG_DRM_EXYNOS_G2D + ret = platform_driver_register(&g2d_driver); + if (ret < 0) + goto out_g2d; +#endif + ret = platform_driver_register(&exynos_drm_platform_driver); if (ret < 0) goto out; @@ -305,6 +327,11 @@ static int __init exynos_drm_init(void) return 0;
out: +#ifdef CONFIG_DRM_EXYNOS_G2D + platform_driver_unregister(&g2d_driver); +out_g2d: +#endif + #ifdef CONFIG_DRM_EXYNOS_HDMI platform_driver_unregister(&exynos_drm_common_hdmi_driver); out_common_hdmi: @@ -327,6 +354,10 @@ static void __exit exynos_drm_exit(void)
platform_driver_unregister(&exynos_drm_platform_driver);
+#ifdef CONFIG_DRM_EXYNOS_G2D + platform_driver_unregister(&g2d_driver); +#endif + #ifdef CONFIG_DRM_EXYNOS_HDMI platform_driver_unregister(&exynos_drm_common_hdmi_driver); platform_driver_unregister(&mixer_driver); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index b26c2f4..d7b50e5 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -203,6 +203,16 @@ struct exynos_drm_manager { struct exynos_drm_display_ops *display_ops; };
+struct exynos_drm_g2d_private { + struct device *dev; + struct list_head inuse_cmdlist; + struct list_head event_list; +}; + +struct drm_exynos_file_private { + struct exynos_drm_g2d_private *g2d_priv; +}; + /* * Exynos drm private structure. */ @@ -284,4 +294,5 @@ extern struct platform_driver fimd_driver; extern struct platform_driver hdmi_driver; extern struct platform_driver mixer_driver; extern struct platform_driver exynos_drm_common_hdmi_driver; +extern struct platform_driver g2d_driver; #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c new file mode 100644 index 0000000..69839f2 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -0,0 +1,864 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Authors: Joonyoung Shim jy0922.shim@samsung.com + * + * 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 Foundationr + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/workqueue.h> + +#include "drmP.h" +#include "exynos_drm.h" +#include "exynos_drm_drv.h" +#include "exynos_drm_gem.h" + +#define G2D_HW_MAJOR_VER 4 +#define G2D_HW_MINOR_VER 1 + +/* Registers */ +#define G2D_SOFT_RESET 0x0000 +#define G2D_INTEN 0x0004 +#define G2D_INTC_PEND 0x000C +#define G2D_DMA_SFR_BASE_ADDR 0x0080 +#define G2D_DMA_COMMAND 0x0084 +#define G2D_DMA_STATUS 0x008C +#define G2D_DMA_HOLD_CMD 0x0090 +#define G2D_BITBLT_START 0x0100 +#define G2D_SRC_BASE_ADDR 0x0304 + +/* G2D_SOFT_RESET */ +#define G2D_SFRCLEAR (1 << 1) +#define G2D_R (1 << 0) + +/* G2D_INTEN */ +#define G2D_INTEN_ACF (1 << 3) +#define G2D_INTEN_UCF (1 << 2) +#define G2D_INTEN_GCF (1 << 1) +#define G2D_INTEN_SCF (1 << 0) + +/* G2D_INTC_PEND */ +#define G2D_INTP_ACMD_FIN (1 << 3) +#define G2D_INTP_UCMD_FIN (1 << 2) +#define G2D_INTP_GCMD_FIN (1 << 1) +#define G2D_INTP_SCMD_FIN (1 << 0) + +/* G2D_DMA_COMMAND */ +#define G2D_DMA_HALT (1 << 2) +#define G2D_DMA_CONTINUE (1 << 1) +#define G2D_DMA_START (1 << 0) + +/* G2D_DMA_STATUS */ +#define G2D_DMA_LIST_DONE_COUNT (0xFF << 17) +#define G2D_DMA_BITBLT_DONE_COUNT (0xFFFF << 1) +#define G2D_DMA_DONE (1 << 0) +#define G2D_DMA_LIST_DONE_COUNT_OFFSET 17 + +/* G2D_DMA_HOLD_CMD */ +#define G2D_USET_HOLD (1 << 2) +#define G2D_LIST_HOLD (1 << 1) +#define G2D_BITBLT_HOLD (1 << 0) + +/* G2D_BITBLT_START */ +#define G2D_START_CASESEL (1 << 2) +#define G2D_START_NHOLT (1 << 1) +#define G2D_START_BITBLT (1 << 0) + +#define G2D_CMDLIST_SIZE (PAGE_SIZE / 4) +#define G2D_CMDLIST_NUM 64 +#define G2D_CMDLIST_POOL_SIZE (G2D_CMDLIST_SIZE * G2D_CMDLIST_NUM) +#define G2D_CMDLIST_DATA_NUM (G2D_CMDLIST_SIZE / sizeof(u32) - 2) + +#define MAX_BUF_ADDR_NR 5 + +/* cmdlist data structure */ +struct g2d_cmdlist { + u32 head; + u32 data[G2D_CMDLIST_DATA_NUM]; + u32 last; /* last data offset */ +}; + +struct drm_exynos_pending_g2d_event { + struct drm_pending_event base; + struct drm_exynos_g2d_event event; +}; + +struct g2d_cmdlist_node { + struct list_head list; + struct g2d_cmdlist *cmdlist; + unsigned int gem_nr; + unsigned int gem_handle[MAX_BUF_ADDR_NR]; + dma_addr_t dma_addr; + + struct drm_exynos_pending_g2d_event *event; +}; + +struct g2d_runqueue_node { + struct list_head list; + struct list_head run_cmdlist; + struct list_head event_list; + struct completion complete; + int async; +}; + +struct g2d_data { + struct device *dev; + struct clk *gate_clk; + struct resource *regs_res; + void __iomem *regs; + int irq; + struct workqueue_struct *g2d_workq; + struct work_struct runqueue_work; + struct exynos_drm_subdrv subdrv; + bool suspended; + + /* cmdlist */ + struct g2d_cmdlist_node *cmdlist_node; + struct list_head free_cmdlist; + struct mutex cmdlist_mutex; + dma_addr_t cmdlist_pool; + void *cmdlist_pool_virt; + + /* runqueue*/ + struct g2d_runqueue_node *runqueue_node; + struct list_head runqueue; + struct mutex runqueue_mutex; + struct kmem_cache *runqueue_slab; +}; + +/* cmdlist functions */ +static void g2d_print_cmdlist(struct device *dev, struct g2d_cmdlist_node *node) +{ + struct g2d_cmdlist *cmdlist = node->cmdlist; + int nr; + int index; + + dev_dbg(dev, "Head: %d\n", cmdlist->head); + for (nr = 0, index = 0; nr < cmdlist->last; nr += 2) { + dev_dbg(dev, "%02d: [0x%08x] 0x%08x\n", index, + cmdlist->data[nr], cmdlist->data[nr + 1]); + index++; + } + dev_dbg(dev, "Tail: 0x%08x\n", cmdlist->data[cmdlist->last]); +} + +static int g2d_init_cmdlist(struct g2d_data *g2d) +{ + struct device *dev = g2d->dev; + struct g2d_cmdlist_node *node = g2d->cmdlist_node; + int nr; + int ret; + + g2d->cmdlist_pool_virt = dma_alloc_coherent(dev, G2D_CMDLIST_POOL_SIZE, + &g2d->cmdlist_pool, GFP_KERNEL); + if (!g2d->cmdlist_pool_virt) { + dev_err(dev, "failed to allocate dma memory\n"); + return -ENOMEM; + } + + node = kcalloc(G2D_CMDLIST_NUM, G2D_CMDLIST_NUM * sizeof(*node), + GFP_KERNEL); + if (!node) { + dev_err(dev, "failed to allocate memory\n"); + ret = -ENOMEM; + goto err; + } + + for (nr = 0; nr < G2D_CMDLIST_NUM; nr++) { + node[nr].cmdlist = + g2d->cmdlist_pool_virt + nr * G2D_CMDLIST_SIZE; + node[nr].dma_addr = + g2d->cmdlist_pool + nr * G2D_CMDLIST_SIZE; + + list_add_tail(&node[nr].list, &g2d->free_cmdlist); + } + + return 0; + +err: + dma_free_coherent(dev, G2D_CMDLIST_POOL_SIZE, g2d->cmdlist_pool_virt, + g2d->cmdlist_pool); + return ret; +} + +static void g2d_fini_cmdlist(struct g2d_data *g2d) +{ + struct device *dev = g2d->dev; + + kfree(g2d->cmdlist_node); + dma_free_coherent(dev, G2D_CMDLIST_POOL_SIZE, g2d->cmdlist_pool_virt, + g2d->cmdlist_pool); +} + +static struct g2d_cmdlist_node *g2d_get_cmdlist(struct g2d_data *g2d) +{ + struct device *dev = g2d->dev; + struct g2d_cmdlist_node *node; + + mutex_lock(&g2d->cmdlist_mutex); + if (list_empty(&g2d->free_cmdlist)) { + dev_err(dev, "there is no free cmdlist\n"); + mutex_unlock(&g2d->cmdlist_mutex); + return NULL; + } + + node = list_first_entry(&g2d->free_cmdlist, struct g2d_cmdlist_node, + list); + list_del_init(&node->list); + mutex_unlock(&g2d->cmdlist_mutex); + + return node; +} + +static void g2d_put_cmdlist(struct g2d_data *g2d, struct g2d_cmdlist_node *node) +{ + mutex_lock(&g2d->cmdlist_mutex); + list_move_tail(&node->list, &g2d->free_cmdlist); + mutex_unlock(&g2d->cmdlist_mutex); +} + +static void g2d_add_cmdlist_to_inuse(struct exynos_drm_g2d_private *g2d_priv, + struct g2d_cmdlist_node *node) +{ + struct g2d_cmdlist_node *lnode; + + if (list_empty(&g2d_priv->inuse_cmdlist)) + goto add_to_list; + + /* this links to base address of new cmdlist */ + lnode = list_entry(g2d_priv->inuse_cmdlist.prev, + struct g2d_cmdlist_node, list); + lnode->cmdlist->data[lnode->cmdlist->last] = node->dma_addr; + +add_to_list: + list_add_tail(&node->list, &g2d_priv->inuse_cmdlist); + + if (node->event) + list_add_tail(&node->event->base.link, &g2d_priv->event_list); +} + +static int g2d_get_cmdlist_gem(struct g2d_cmdlist_node *node, + struct drm_device *drm_dev, + struct drm_file *file) +{ + struct g2d_cmdlist *cmdlist = node->cmdlist; + dma_addr_t *addr; + int offset; + int i; + + for (i = 0; i < node->gem_nr; i++) { + offset = cmdlist->last - (i * 2 + 1); + node->gem_handle[i] = cmdlist->data[offset]; + + addr = exynos_drm_gem_get_dma_addr(drm_dev, node->gem_handle[i], + file); + if (IS_ERR(addr)) { + node->gem_nr = i; + return PTR_ERR(addr); + } + + cmdlist->data[offset] = *addr; + } + + return 0; +} + +static void g2d_put_cmdlist_gem(struct drm_device *drm_dev, + struct g2d_cmdlist_node *node, + struct drm_file *file) +{ + struct g2d_cmdlist *cmdlist = node->cmdlist; + int offset; + int i; + + for (i = 0; i < node->gem_nr; i++) { + offset = cmdlist->last - (i * 2 + 1); + exynos_drm_gem_put_dma_addr(drm_dev, node->gem_handle[i], file); + } +} + +static void g2d_dma_start(struct g2d_data *g2d, + struct g2d_runqueue_node *runqueue_node) +{ + struct g2d_cmdlist_node *node = + list_first_entry(&runqueue_node->run_cmdlist, + struct g2d_cmdlist_node, list); + + pm_runtime_get_sync(g2d->dev); + clk_enable(g2d->gate_clk); + + /* interrupt enable */ + writel_relaxed(G2D_INTEN_ACF | G2D_INTEN_UCF | G2D_INTEN_GCF, + g2d->regs + G2D_INTEN); + + writel_relaxed(node->dma_addr, g2d->regs + G2D_DMA_SFR_BASE_ADDR); + writel_relaxed(G2D_DMA_START, g2d->regs + G2D_DMA_COMMAND); +} + +static struct g2d_runqueue_node *g2d_get_runqueue_node(struct g2d_data *g2d) +{ + struct g2d_runqueue_node *runqueue_node; + + if (list_empty(&g2d->runqueue)) + return NULL; + + runqueue_node = list_first_entry(&g2d->runqueue, + struct g2d_runqueue_node, list); + list_del_init(&runqueue_node->list); + return runqueue_node; +} + +static void g2d_free_runqueue_node(struct g2d_data *g2d, + struct g2d_runqueue_node *runqueue_node) +{ + if (!runqueue_node) + return; + + mutex_lock(&g2d->cmdlist_mutex); + list_splice_tail_init(&runqueue_node->run_cmdlist, &g2d->free_cmdlist); + mutex_unlock(&g2d->cmdlist_mutex); + + kmem_cache_free(g2d->runqueue_slab, runqueue_node); +} + +static void g2d_exec_runqueue(struct g2d_data *g2d) +{ + g2d->runqueue_node = g2d_get_runqueue_node(g2d); + if (g2d->runqueue_node) + g2d_dma_start(g2d, g2d->runqueue_node); +} + +static void g2d_runqueue_worker(struct work_struct *work) +{ + struct g2d_data *g2d = container_of(work, struct g2d_data, + runqueue_work); + + + mutex_lock(&g2d->runqueue_mutex); + clk_disable(g2d->gate_clk); + pm_runtime_put_sync(g2d->dev); + + complete(&g2d->runqueue_node->complete); + if (g2d->runqueue_node->async) + g2d_free_runqueue_node(g2d, g2d->runqueue_node); + + if (g2d->suspended) + g2d->runqueue_node = NULL; + else + g2d_exec_runqueue(g2d); + mutex_unlock(&g2d->runqueue_mutex); +} + +static void g2d_finish_event(struct g2d_data *g2d, u32 cmdlist_no) +{ + struct drm_device *drm_dev = g2d->subdrv.drm_dev; + struct g2d_runqueue_node *runqueue_node = g2d->runqueue_node; + struct drm_exynos_pending_g2d_event *e; + struct timeval now; + unsigned long flags; + + if (list_empty(&runqueue_node->event_list)) + return; + + e = list_first_entry(&runqueue_node->event_list, + struct drm_exynos_pending_g2d_event, base.link); + + do_gettimeofday(&now); + e->event.tv_sec = now.tv_sec; + e->event.tv_usec = now.tv_usec; + e->event.cmdlist_no = cmdlist_no; + + spin_lock_irqsave(&drm_dev->event_lock, flags); + list_move_tail(&e->base.link, &e->base.file_priv->event_list); + wake_up_interruptible(&e->base.file_priv->event_wait); + spin_unlock_irqrestore(&drm_dev->event_lock, flags); +} + +static irqreturn_t g2d_irq_handler(int irq, void *dev_id) +{ + struct g2d_data *g2d = dev_id; + u32 pending; + + pending = readl_relaxed(g2d->regs + G2D_INTC_PEND); + if (pending) + writel_relaxed(pending, g2d->regs + G2D_INTC_PEND); + + if (pending & G2D_INTP_GCMD_FIN) { + u32 cmdlist_no = readl_relaxed(g2d->regs + G2D_DMA_STATUS); + + cmdlist_no = (cmdlist_no & G2D_DMA_LIST_DONE_COUNT) >> 17; + + g2d_finish_event(g2d, cmdlist_no); + + writel_relaxed(0, g2d->regs + G2D_DMA_HOLD_CMD); + if (!(pending & G2D_INTP_ACMD_FIN)) { + writel_relaxed(G2D_DMA_CONTINUE, + g2d->regs + G2D_DMA_COMMAND); + } + } + + if (pending & G2D_INTP_ACMD_FIN) + queue_work(g2d->g2d_workq, &g2d->runqueue_work); + + return IRQ_HANDLED; +} + +/* ioctl functions */ +int exynos_g2d_get_ver_ioctl(struct drm_device *drm_dev, void *data, + struct drm_file *file) +{ + struct drm_exynos_g2d_get_ver *ver = data; + + ver->major = G2D_HW_MAJOR_VER; + ver->minor = G2D_HW_MINOR_VER; + + return 0; +} +EXPORT_SYMBOL_GPL(exynos_g2d_get_ver_ioctl); + +int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; + struct device *dev = g2d_priv->dev; + struct g2d_data *g2d; + struct drm_exynos_g2d_set_cmdlist *req = data; + struct drm_exynos_pending_g2d_event *e; + struct g2d_cmdlist_node *node; + struct g2d_cmdlist *cmdlist; + unsigned long flags; + int ret; + + if (!dev) + return -ENODEV; + + g2d = dev_get_drvdata(dev); + if (!g2d) + return -EFAULT; + + node = g2d_get_cmdlist(g2d); + if (!node) + return -ENOMEM; + + node->event = NULL; + + if (req->event_type != G2D_EVENT_NOT) { + spin_lock_irqsave(&drm_dev->event_lock, flags); + if (file->event_space < sizeof(e->event)) { + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + ret = -ENOMEM; + goto err; + } + file->event_space -= sizeof(e->event); + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + + e = kzalloc(sizeof(*node->event), GFP_KERNEL); + if (!e) { + dev_err(dev, "failed to allocate event\n"); + + spin_lock_irqsave(&drm_dev->event_lock, flags); + file->event_space += sizeof(e->event); + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + + ret = -ENOMEM; + goto err; + } + + e->event.base.type = DRM_EXYNOS_G2D_EVENT; + e->event.base.length = sizeof(e->event); + e->event.user_data = req->user_data; + e->base.event = &e->event.base; + e->base.file_priv = file; + e->base.destroy = (void (*) (struct drm_pending_event *)) kfree; + + node->event = e; + } + + cmdlist = node->cmdlist; + + cmdlist->last = 0; + + /* TODO: check cmdlist size */ + /* + * If don't clear SFR registers, the cmdlist is affected by register + * values of previous cmdlist. G2D hw executes SFR clear command and + * a next command at the same time then the next command is ignored and + * is executed rightly from next next command, so needs a dummy command + * to next command of SFR clear command. + */ + cmdlist->data[cmdlist->last++] = G2D_SOFT_RESET; + cmdlist->data[cmdlist->last++] = G2D_SFRCLEAR; + cmdlist->data[cmdlist->last++] = G2D_SRC_BASE_ADDR; + cmdlist->data[cmdlist->last++] = 0; + + if (node->event) { + cmdlist->data[cmdlist->last++] = G2D_DMA_HOLD_CMD; + cmdlist->data[cmdlist->last++] = G2D_LIST_HOLD; + } + + if (copy_from_user(cmdlist->data + cmdlist->last, + (void __user *)req->cmd, + sizeof(*req->cmd) * req->cmd_nr)) { + ret = -EFAULT; + goto err_free_event; + } + cmdlist->last += req->cmd_nr * 2; + + node->gem_nr = req->cmd_gem_nr; + if (req->cmd_gem_nr) { + struct drm_exynos_g2d_cmd *cmd_gem = req->cmd_gem; + + if (copy_from_user(cmdlist->data + cmdlist->last, + (void __user *)cmd_gem, + sizeof(*cmd_gem) * req->cmd_gem_nr)) { + ret = -EFAULT; + goto err_free_event; + } + cmdlist->last += req->cmd_gem_nr * 2; + + ret = g2d_get_cmdlist_gem(node, drm_dev, file); + if (ret < 0) + goto err_unmap; + } + + cmdlist->data[cmdlist->last++] = G2D_BITBLT_START; + cmdlist->data[cmdlist->last++] = G2D_START_BITBLT; + + /* head */ + cmdlist->head = cmdlist->last / 2; + + /* tail */ + cmdlist->data[cmdlist->last] = 0; + + g2d_add_cmdlist_to_inuse(g2d_priv, node); + + return 0; + +err_unmap: + g2d_put_cmdlist_gem(drm_dev, node, file); +err_free_event: + if (node->event) { + spin_lock_irqsave(&drm_dev->event_lock, flags); + file->event_space += sizeof(e->event); + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + kfree(node->event); + } +err: + g2d_put_cmdlist(g2d, node); + return ret; +} +EXPORT_SYMBOL_GPL(exynos_g2d_set_cmdlist_ioctl); + +int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; + struct device *dev = g2d_priv->dev; + struct g2d_data *g2d; + struct drm_exynos_g2d_exec *req = data; + struct g2d_runqueue_node *runqueue_node; + struct list_head *run_cmdlist; + struct list_head *event_list; + + if (!dev) + return -ENODEV; + + g2d = dev_get_drvdata(dev); + if (!g2d) + return -EFAULT; + + runqueue_node = kmem_cache_alloc(g2d->runqueue_slab, GFP_KERNEL); + if (!runqueue_node) { + dev_err(dev, "failed to allocate memory\n"); + return -ENOMEM; + } + run_cmdlist = &runqueue_node->run_cmdlist; + event_list = &runqueue_node->event_list; + INIT_LIST_HEAD(run_cmdlist); + INIT_LIST_HEAD(event_list); + init_completion(&runqueue_node->complete); + runqueue_node->async = req->async; + + list_splice_init(&g2d_priv->inuse_cmdlist, run_cmdlist); + list_splice_init(&g2d_priv->event_list, event_list); + + if (list_empty(run_cmdlist)) { + dev_err(dev, "there is no inuse cmdlist\n"); + kmem_cache_free(g2d->runqueue_slab, runqueue_node); + return -EPERM; + } + + mutex_lock(&g2d->runqueue_mutex); + list_add_tail(&runqueue_node->list, &g2d->runqueue); + if (!g2d->runqueue_node) + g2d_exec_runqueue(g2d); + mutex_unlock(&g2d->runqueue_mutex); + + if (runqueue_node->async) + goto out; + + wait_for_completion(&runqueue_node->complete); + g2d_free_runqueue_node(g2d, runqueue_node); + +out: + return 0; +} +EXPORT_SYMBOL_GPL(exynos_g2d_exec_ioctl); + +static int g2d_open(struct drm_device *drm_dev, struct device *dev, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_g2d_private *g2d_priv; + + g2d_priv = kzalloc(sizeof(*g2d_priv), GFP_KERNEL); + if (!g2d_priv) { + dev_err(dev, "failed to allocate g2d_priv.\n"); + return -ENOMEM; + } + + g2d_priv->dev = dev; + file_priv->g2d_priv = g2d_priv; + + INIT_LIST_HEAD(&g2d_priv->inuse_cmdlist); + INIT_LIST_HEAD(&g2d_priv->event_list); + + return 0; +} + +static void g2d_close(struct drm_device *drm_dev, struct device *dev, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; + struct g2d_data *g2d; + struct g2d_cmdlist_node *node, *n; + + if (!dev) + return; + + g2d = dev_get_drvdata(dev); + if (!g2d) + return; + + mutex_lock(&g2d->cmdlist_mutex); + list_for_each_entry_safe(node, n, &g2d_priv->inuse_cmdlist, list) + list_move_tail(&node->list, &g2d->free_cmdlist); + mutex_unlock(&g2d->cmdlist_mutex); + + /* TODO: do exynos_drm_gem_put_dma_addr() */ + kfree(file_priv->g2d_priv); +} + +static int g2d_subdrv_probe(struct drm_device *drm_dev, struct device *dev) +{ + return 0; +} + +static int __devinit g2d_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct g2d_data *g2d; + struct exynos_drm_subdrv *subdrv; + int ret; + + g2d = kzalloc(sizeof(*g2d), GFP_KERNEL); + if (!g2d) { + dev_err(dev, "failed to allocate driver data\n"); + return -ENOMEM; + } + + g2d->runqueue_slab = kmem_cache_create("g2d_runqueue_slab", + sizeof(struct g2d_runqueue_node), 0, 0, NULL); + if (!g2d->runqueue_slab) { + ret = -ENOMEM; + goto err_free_mem; + } + + g2d->dev = dev; + + g2d->g2d_workq = create_singlethread_workqueue("g2d"); + if (!g2d->g2d_workq) { + dev_err(dev, "failed to create workqueue\n"); + ret = -EINVAL; + goto err_destroy_slab; + } + + INIT_WORK(&g2d->runqueue_work, g2d_runqueue_worker); + INIT_LIST_HEAD(&g2d->free_cmdlist); + INIT_LIST_HEAD(&g2d->runqueue); + + mutex_init(&g2d->cmdlist_mutex); + mutex_init(&g2d->runqueue_mutex); + + ret = g2d_init_cmdlist(g2d); + if (ret < 0) + goto err_destroy_workqueue; + + g2d->gate_clk = clk_get(dev, "fimg2d"); + if (IS_ERR(g2d->gate_clk)) { + dev_err(dev, "failed to get gate clock\n"); + ret = PTR_ERR(g2d->gate_clk); + goto err_fini_cmdlist; + } + + pm_runtime_enable(dev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "failed to get I/O memory\n"); + ret = -ENOENT; + goto err_put_clk; + } + + g2d->regs_res = request_mem_region(res->start, resource_size(res), + dev_name(dev)); + if (!g2d->regs_res) { + dev_err(dev, "failed to request I/O memory\n"); + ret = -ENOENT; + goto err_put_clk; + } + + g2d->regs = ioremap(res->start, resource_size(res)); + if (!g2d->regs) { + dev_err(dev, "failed to remap I/O memory\n"); + ret = -ENXIO; + goto err_release_res; + } + + g2d->irq = platform_get_irq(pdev, 0); + if (g2d->irq < 0) { + dev_err(dev, "failed to get irq\n"); + ret = g2d->irq; + goto err_unmap_base; + } + + ret = request_irq(g2d->irq, g2d_irq_handler, 0, "drm_g2d", g2d); + if (ret < 0) { + dev_err(dev, "irq request failed\n"); + goto err_unmap_base; + } + + platform_set_drvdata(pdev, g2d); + + subdrv = &g2d->subdrv; + subdrv->is_local = true; + subdrv->probe = g2d_subdrv_probe; + subdrv->open = g2d_open; + subdrv->close = g2d_close; + subdrv->manager.dev = dev; + + ret = exynos_drm_subdrv_register(subdrv); + if (ret < 0) { + dev_err(dev, "failed to register drm g2d device\n"); + goto err_free_irq; + } + + dev_info(dev, "The exynos g2d(ver %d.%d) successfully probed\n", + G2D_HW_MAJOR_VER, G2D_HW_MINOR_VER); + + return 0; + +err_free_irq: + free_irq(g2d->irq, g2d); +err_unmap_base: + iounmap(g2d->regs); +err_release_res: + release_resource(g2d->regs_res); + kfree(g2d->regs_res); +err_put_clk: + pm_runtime_disable(dev); + clk_put(g2d->gate_clk); +err_fini_cmdlist: + g2d_fini_cmdlist(g2d); +err_destroy_workqueue: + destroy_workqueue(g2d->g2d_workq); +err_destroy_slab: + kmem_cache_destroy(g2d->runqueue_slab); +err_free_mem: + kfree(g2d); + return ret; +} + +static int __devexit g2d_remove(struct platform_device *pdev) +{ + struct g2d_data *g2d = platform_get_drvdata(pdev); + + cancel_work_sync(&g2d->runqueue_work); + exynos_drm_subdrv_unregister(&g2d->subdrv); + free_irq(g2d->irq, g2d); + + while (g2d->runqueue_node) { + g2d_free_runqueue_node(g2d, g2d->runqueue_node); + g2d->runqueue_node = g2d_get_runqueue_node(g2d); + } + + iounmap(g2d->regs); + release_resource(g2d->regs_res); + kfree(g2d->regs_res); + + pm_runtime_disable(&pdev->dev); + clk_put(g2d->gate_clk); + + g2d_fini_cmdlist(g2d); + destroy_workqueue(g2d->g2d_workq); + kmem_cache_destroy(g2d->runqueue_slab); + kfree(g2d); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int g2d_suspend(struct device *dev) +{ + struct g2d_data *g2d = dev_get_drvdata(dev); + + mutex_lock(&g2d->runqueue_mutex); + g2d->suspended = true; + mutex_unlock(&g2d->runqueue_mutex); + + while (g2d->runqueue_node) + /* FIXME: good range? */ + usleep_range(500, 1000); + + flush_work_sync(&g2d->runqueue_work); + + return 0; +} + +static int g2d_resume(struct device *dev) +{ + struct g2d_data *g2d = dev_get_drvdata(dev); + + g2d->suspended = false; + g2d_exec_runqueue(g2d); + + return 0; +} +#endif + +SIMPLE_DEV_PM_OPS(g2d_pm_ops, g2d_suspend, g2d_resume); + +struct platform_driver g2d_driver = { + .probe = g2d_probe, + .remove = __devexit_p(g2d_remove), + .driver = { + /* FIXME */ + .name = "s5p-fimg2d", + .owner = THIS_MODULE, + .pm = &g2d_pm_ops, + }, +}; diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.h b/drivers/gpu/drm/exynos/exynos_drm_g2d.h new file mode 100644 index 0000000..1a9c7ca --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Authors: Joonyoung Shim jy0922.shim@samsung.com + * + * 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 Foundationr + */ + +#ifdef CONFIG_DRM_EXYNOS_G2D +extern int exynos_g2d_get_ver_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int exynos_g2d_set_cmdlist_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int exynos_g2d_exec_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +#else +static inline int exynos_g2d_get_ver_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return -ENODEV; +} + +static inline int exynos_g2d_set_cmdlist_ioctl(struct drm_device *dev, + void *data, + struct drm_file *file_priv) +{ + return -ENODEV; +} + +static inline int exynos_g2d_exec_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return -ENODEV; +} +#endif diff --git a/include/drm/exynos_drm.h b/include/drm/exynos_drm.h index 81c9cb7..907daaf 100644 --- a/include/drm/exynos_drm.h +++ b/include/drm/exynos_drm.h @@ -29,6 +29,8 @@ #ifndef _EXYNOS_DRM_H_ #define _EXYNOS_DRM_H_
+#include "drm.h" + /** * User-desired buffer creation information structure. * @@ -85,12 +87,48 @@ enum e_drm_exynos_gem_mem_type { EXYNOS_BO_NONCONTIG = 1 << 0 };
+struct drm_exynos_g2d_get_ver { + __u32 major; + __u32 minor; +}; + +struct drm_exynos_g2d_cmd { + __u32 offset; + __u32 data; +}; + +enum drm_exynos_g2d_event_type { + G2D_EVENT_NOT, + G2D_EVENT_NONSTOP, + G2D_EVENT_STOP, /* not yet */ +}; + +struct drm_exynos_g2d_set_cmdlist { + struct drm_exynos_g2d_cmd *cmd; + struct drm_exynos_g2d_cmd *cmd_gem; + __u32 cmd_nr; + __u32 cmd_gem_nr; + + /* for g2d event */ + enum drm_exynos_g2d_event_type event_type; + __u32 user_data; +}; + +struct drm_exynos_g2d_exec { + __u32 async; +}; + #define DRM_EXYNOS_GEM_CREATE 0x00 #define DRM_EXYNOS_GEM_MAP_OFFSET 0x01 #define DRM_EXYNOS_GEM_MMAP 0x02 /* Reserved 0x03 ~ 0x05 for exynos specific gem ioctl */ #define DRM_EXYNOS_PLANE_SET_ZPOS 0x06
+/* G2D */ +#define DRM_EXYNOS_G2D_GET_VER 0x20 +#define DRM_EXYNOS_G2D_SET_CMDLIST 0x21 +#define DRM_EXYNOS_G2D_EXEC 0x22 + #define DRM_IOCTL_EXYNOS_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \ DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create)
@@ -103,6 +141,24 @@ enum e_drm_exynos_gem_mem_type { #define DRM_IOCTL_EXYNOS_PLANE_SET_ZPOS DRM_IOWR(DRM_COMMAND_BASE + \ DRM_EXYNOS_PLANE_SET_ZPOS, struct drm_exynos_plane_set_zpos)
+#define DRM_IOCTL_EXYNOS_G2D_GET_VER DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_EXYNOS_G2D_GET_VER, struct drm_exynos_g2d_get_ver) +#define DRM_IOCTL_EXYNOS_G2D_SET_CMDLIST DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_EXYNOS_G2D_SET_CMDLIST, struct drm_exynos_g2d_set_cmdlist) +#define DRM_IOCTL_EXYNOS_G2D_EXEC DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_EXYNOS_G2D_EXEC, struct drm_exynos_g2d_exec) + +/* EXYNOS specific events */ +#define DRM_EXYNOS_G2D_EVENT 0x80000000 + +struct drm_exynos_g2d_event { + struct drm_event base; + __u32 tv_sec; + __u32 tv_usec; + __u32 cmdlist_no; + __u32 user_data; +}; + #ifdef __KERNEL__
/**
G2D is a 2D graphic accelerator that supports Bit Block Transfer. This G2D driver is exynos drm specific and supports only exynos4x12 series. user application fills command set in cmdlist and once dma start request these cmdlists are parsed and performed by dma.
Where is this block documented or a pointer to the corresponding open userspace user code?
Again the ioctls look like they need to be depointered and use __u64.
It would be nice if you could document how the command stream and engine work.
How does userspace pass addresses to it? straight or via gem objects? how does it stop userspace blt to places it shouldn't etc.
I'm getting the feeling this accel enabled driver needs a lot more of a commit message and lot more documentation on what it can be used for.
Dave.
On 03/15/2012 07:50 PM, Dave Airlie wrote:
G2D is a 2D graphic accelerator that supports Bit Block Transfer. This G2D driver is exynos drm specific and supports only exynos4x12 series. user application fills command set in cmdlist and once dma start request these cmdlists are parsed and performed by dma.
Where is this block documented or a pointer to the corresponding open userspace user code?
Again the ioctls look like they need to be depointered and use __u64.
It would be nice if you could document how the command stream and engine work.
How does userspace pass addresses to it? straight or via gem objects? how does it stop userspace blt to places it shouldn't etc.
I'm getting the feeling this accel enabled driver needs a lot more of a commit message and lot more documentation on what it can be used for.
OK, i will update the more documentation and simple user space program.
Thanks.
On 03/15/2012 07:50 PM, Dave Airlie wrote:
G2D is a 2D graphic accelerator that supports Bit Block Transfer. This G2D driver is exynos drm specific and supports only exynos4x12 series. user application fills command set in cmdlist and once dma start request these cmdlists are parsed and performed by dma.
Where is this block documented or a pointer to the corresponding open userspace user code?
I attach simple g2dtest program patch. The base of this patch is master branch of lastest libdrm git.
This user progrem can test just solid color fill example. I will cleanup and update it for more operations later.
Thanks.
Again the ioctls look like they need to be depointered and use __u64.
It would be nice if you could document how the command stream and engine work.
How does userspace pass addresses to it? straight or via gem objects? how does it stop userspace blt to places it shouldn't etc.
I'm getting the feeling this accel enabled driver needs a lot more of a commit message and lot more documentation on what it can be used for.
Dave. _______________________________________________ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
I don't suppose you could send a libdrm patch to the list with an up to date version of the g2dtest code so we can get it in libdrm tree?
BR, -R
On Fri, Mar 16, 2012 at 1:28 AM, Joonyoung Shim jy0922.shim@samsung.com wrote:
On 03/15/2012 07:50 PM, Dave Airlie wrote:
G2D is a 2D graphic accelerator that supports Bit Block Transfer. This G2D driver is exynos drm specific and supports only exynos4x12 series. user application fills command set in cmdlist and once dma start request these cmdlists are parsed and performed by dma.
Where is this block documented or a pointer to the corresponding open userspace user code?
I attach simple g2dtest program patch. The base of this patch is master branch of lastest libdrm git.
This user progrem can test just solid color fill example. I will cleanup and update it for more operations later.
Thanks.
Again the ioctls look like they need to be depointered and use __u64.
It would be nice if you could document how the command stream and engine work.
How does userspace pass addresses to it? straight or via gem objects? how does it stop userspace blt to places it shouldn't etc.
I'm getting the feeling this accel enabled driver needs a lot more of a commit message and lot more documentation on what it can be used for.
Dave. _______________________________________________ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
2013/1/21 Rob Clark robdclark@gmail.com:
I don't suppose you could send a libdrm patch to the list with an up to date version of the g2dtest code so we can get it in libdrm tree?
We are planning on updating exynos drm for libdrm. At that time, the up to date version of the g2dtest will be posted. Joonyoung, let it post. And I will post other things except g2dtest.
Thanks, Inki Dae
BR, -R
On Fri, Mar 16, 2012 at 1:28 AM, Joonyoung Shim jy0922.shim@samsung.com wrote:
On 03/15/2012 07:50 PM, Dave Airlie wrote:
G2D is a 2D graphic accelerator that supports Bit Block Transfer. This G2D driver is exynos drm specific and supports only exynos4x12 series. user application fills command set in cmdlist and once dma start request these cmdlists are parsed and performed by dma.
Where is this block documented or a pointer to the corresponding open userspace user code?
I attach simple g2dtest program patch. The base of this patch is master branch of lastest libdrm git.
This user progrem can test just solid color fill example. I will cleanup and update it for more operations later.
Thanks.
Again the ioctls look like they need to be depointered and use __u64.
It would be nice if you could document how the command stream and engine work.
How does userspace pass addresses to it? straight or via gem objects? how does it stop userspace blt to places it shouldn't etc.
I'm getting the feeling this accel enabled driver needs a lot more of a commit message and lot more documentation on what it can be used for.
Dave. _______________________________________________ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
On Mon, Jan 21, 2013 at 7:33 PM, Inki Dae inki.dae@samsung.com wrote:
2013/1/21 Rob Clark robdclark@gmail.com:
I don't suppose you could send a libdrm patch to the list with an up to date version of the g2dtest code so we can get it in libdrm tree?
We are planning on updating exynos drm for libdrm. At that time, the up to date version of the g2dtest will be posted. Joonyoung, let it post. And I will post other things except g2dtest.
thanks, I just want to make sure this doesn't get lost
BR, -R
Thanks, Inki Dae
BR, -R
On Fri, Mar 16, 2012 at 1:28 AM, Joonyoung Shim jy0922.shim@samsung.com wrote:
On 03/15/2012 07:50 PM, Dave Airlie wrote:
G2D is a 2D graphic accelerator that supports Bit Block Transfer. This G2D driver is exynos drm specific and supports only exynos4x12 series. user application fills command set in cmdlist and once dma start request these cmdlists are parsed and performed by dma.
Where is this block documented or a pointer to the corresponding open userspace user code?
I attach simple g2dtest program patch. The base of this patch is master branch of lastest libdrm git.
This user progrem can test just solid color fill example. I will cleanup and update it for more operations later.
Thanks.
Again the ioctls look like they need to be depointered and use __u64.
It would be nice if you could document how the command stream and engine work.
How does userspace pass addresses to it? straight or via gem objects? how does it stop userspace blt to places it shouldn't etc.
I'm getting the feeling this accel enabled driver needs a lot more of a commit message and lot more documentation on what it can be used for.
Dave. _______________________________________________ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
this driver would be used for wireless display. virtual display driver has independent crtc, encoder and connector and to use this driver, user application should send edid data to this driver from wireless display.
Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/Kconfig | 6 + drivers/gpu/drm/exynos/Makefile | 1 + drivers/gpu/drm/exynos/exynos_drm_connector.c | 4 + drivers/gpu/drm/exynos/exynos_drm_drv.c | 18 + drivers/gpu/drm/exynos/exynos_drm_drv.h | 7 +- drivers/gpu/drm/exynos/exynos_drm_encoder.c | 1 + drivers/gpu/drm/exynos/exynos_drm_vidi.c | 677 +++++++++++++++++++++++++ drivers/gpu/drm/exynos/exynos_drm_vidi.h | 36 ++ include/drm/exynos_drm.h | 20 + 9 files changed, 768 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/exynos/exynos_drm_vidi.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_vidi.h
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 8493fe9..76aed3a 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -22,6 +22,12 @@ config DRM_EXYNOS_HDMI help Choose this option if you want to use Exynos HDMI for DRM.
+config DRM_EXYNOS_VIDI + bool "Samsung DRM Virtual Display" + depends on DRM_EXYNOS + help + Choose this option if you want to use Samsung VIDI for DRM. + config DRM_EXYNOS_G2D bool "Exynos DRM G2D" depends on DRM_EXYNOS diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index d6c1a3c..8ee8fb4 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile @@ -12,6 +12,7 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o \ exynos_ddc.o exynos_hdmiphy.o \ exynos_drm_hdmi.o +exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o
obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c index 33893a9..bf791fa 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_connector.c +++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c @@ -315,6 +315,10 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev, connector->interlace_allowed = true; connector->polled = DRM_CONNECTOR_POLL_HPD; break; + case EXYNOS_DISPLAY_TYPE_VIDI: + type = DRM_MODE_CONNECTOR_VIRTUAL; + connector->polled = DRM_CONNECTOR_POLL_HPD; + break; default: type = DRM_MODE_CONNECTOR_Unknown; break; diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 2b72c5d..76524f4 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -39,6 +39,7 @@ #include "exynos_drm_gem.h" #include "exynos_drm_g2d.h" #include "exynos_drm_plane.h" +#include "exynos_drm_vidi.h"
#define DRIVER_NAME "exynos" #define DRIVER_DESC "Samsung SoC DRM" @@ -217,6 +218,8 @@ static struct drm_ioctl_desc exynos_ioctls[] = { exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH), DRM_IOCTL_DEF_DRV(EXYNOS_PLANE_SET_ZPOS, exynos_plane_set_zpos_ioctl, DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION, + vidi_connection_ioctl, DRM_UNLOCKED | DRM_AUTH),
DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER, exynos_g2d_get_ver_ioctl, DRM_UNLOCKED | DRM_AUTH), @@ -314,6 +317,12 @@ static int __init exynos_drm_init(void) goto out_common_hdmi; #endif
+#ifdef CONFIG_DRM_EXYNOS_VIDI + ret = platform_driver_register(&vidi_driver); + if (ret < 0) + goto out_vidi; +#endif + #ifdef CONFIG_DRM_EXYNOS_G2D ret = platform_driver_register(&g2d_driver); if (ret < 0) @@ -332,6 +341,11 @@ out: out_g2d: #endif
+#ifdef CONFIG_DRM_EXYNOS_VIDI + platform_driver_unregister(&vidi_driver); +out_vidi: +#endif + #ifdef CONFIG_DRM_EXYNOS_HDMI platform_driver_unregister(&exynos_drm_common_hdmi_driver); out_common_hdmi: @@ -358,6 +372,10 @@ static void __exit exynos_drm_exit(void) platform_driver_unregister(&g2d_driver); #endif
+#ifdef CONFIG_DRM_EXYNOS_VIDI + platform_driver_unregister(&vidi_driver); +#endif + #ifdef CONFIG_DRM_EXYNOS_HDMI platform_driver_unregister(&exynos_drm_common_hdmi_driver); platform_driver_unregister(&mixer_driver); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index d7b50e5..943853a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -32,9 +32,9 @@ #include <linux/module.h> #include "drm.h"
-#define MAX_CRTC 2 +#define MAX_CRTC 3 #define MAX_PLANE 5 -#define MAX_FB_BUFFER 3 +#define MAX_FB_BUFFER 4 #define DEFAULT_ZPOS -1
struct drm_device; @@ -50,6 +50,8 @@ enum exynos_drm_output_type { EXYNOS_DISPLAY_TYPE_LCD, /* HDMI Interface. */ EXYNOS_DISPLAY_TYPE_HDMI, + /* Virtual Display Interface. */ + EXYNOS_DISPLAY_TYPE_VIDI, };
/* @@ -294,5 +296,6 @@ extern struct platform_driver fimd_driver; extern struct platform_driver hdmi_driver; extern struct platform_driver mixer_driver; extern struct platform_driver exynos_drm_common_hdmi_driver; +extern struct platform_driver vidi_driver; extern struct platform_driver g2d_driver; #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c index 2278676..6e9ac7b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c @@ -218,6 +218,7 @@ static unsigned int exynos_drm_encoder_clones(struct drm_encoder *encoder) switch (display_ops->type) { case EXYNOS_DISPLAY_TYPE_LCD: case EXYNOS_DISPLAY_TYPE_HDMI: + case EXYNOS_DISPLAY_TYPE_VIDI: clone_mask |= (1 << (cnt++)); break; default: diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c new file mode 100644 index 0000000..338dc59 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -0,0 +1,677 @@ +/* exynos_drm_vidi.c + * + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Authors: + * Inki Dae inki.dae@samsung.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include "drmP.h" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <drm/exynos_drm.h> + +#include "drm_edid.h" +#include "drm_crtc_helper.h" + +#include "exynos_drm_drv.h" +#include "exynos_drm_crtc.h" +#include "exynos_drm_encoder.h" + +/* vidi has totally three virtual windows. */ +#define WINDOWS_NR 3 + +#define get_vidi_context(dev) platform_get_drvdata(to_platform_device(dev)) + +struct vidi_win_data { + unsigned int offset_x; + unsigned int offset_y; + unsigned int ovl_width; + unsigned int ovl_height; + unsigned int fb_width; + unsigned int fb_height; + unsigned int bpp; + dma_addr_t dma_addr; + void __iomem *vaddr; + unsigned int buf_offsize; + unsigned int line_size; /* bytes */ + bool enabled; +}; + +struct vidi_context { + struct exynos_drm_subdrv subdrv; + struct drm_crtc *crtc; + struct vidi_win_data win_data[WINDOWS_NR]; + struct edid *raw_edid; + unsigned int clkdiv; + unsigned int default_win; + unsigned long irq_flags; + unsigned int connected; + bool vblank_on; + bool suspended; + struct work_struct work; + struct mutex lock; +}; + +static const char fake_edid_info[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x4c, 0x2d, 0x05, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x12, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78, + 0x0a, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0xbd, + 0xee, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x66, 0x21, 0x50, 0xb0, 0x51, 0x00, + 0x1b, 0x30, 0x40, 0x70, 0x36, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e, + 0x01, 0x1d, 0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00, + 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, + 0x4b, 0x1a, 0x44, 0x17, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x53, 0x41, 0x4d, 0x53, 0x55, 0x4e, 0x47, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xbc, 0x02, 0x03, 0x1e, 0xf1, + 0x46, 0x84, 0x05, 0x03, 0x10, 0x20, 0x22, 0x23, 0x09, 0x07, 0x07, 0x83, + 0x01, 0x00, 0x00, 0xe2, 0x00, 0x0f, 0x67, 0x03, 0x0c, 0x00, 0x10, 0x00, + 0xb8, 0x2d, 0x01, 0x1d, 0x80, 0x18, 0x71, 0x1c, 0x16, 0x20, 0x58, 0x2c, + 0x25, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x9e, 0x8c, 0x0a, 0xd0, 0x8a, + 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, 0xa0, 0x5a, 0x00, 0x00, + 0x00, 0x18, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, + 0x45, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06 +}; + +static void vidi_fake_vblank_handler(struct work_struct *work); + +static bool vidi_display_is_connected(struct device *dev) +{ + struct vidi_context *ctx = get_vidi_context(dev); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* + * connection request would come from user side + * to do hotplug through specific ioctl. + */ + return ctx->connected ? true : false; +} + +static int vidi_get_edid(struct device *dev, struct drm_connector *connector, + u8 *edid, int len) +{ + struct vidi_context *ctx = get_vidi_context(dev); + struct edid *raw_edid; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* + * the edid data comes from user side and it would be set + * to ctx->raw_edid through specific ioctl. + */ + if (!ctx->raw_edid) { + DRM_DEBUG_KMS("raw_edid is null.\n"); + return -EFAULT; + } + + raw_edid = kzalloc(len, GFP_KERNEL); + if (!raw_edid) { + DRM_DEBUG_KMS("failed to allocate raw_edid.\n"); + return -ENOMEM; + } + + memcpy(raw_edid, ctx->raw_edid, min((1 + ctx->raw_edid->extensions) + * EDID_LENGTH, len)); + + /* attach the edid data to connector. */ + connector->display_info.raw_edid = (char *)raw_edid; + + memcpy(edid, ctx->raw_edid, min((1 + ctx->raw_edid->extensions) + * EDID_LENGTH, len)); + + return 0; +} + +static void *vidi_get_panel(struct device *dev) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* TODO. */ + + return NULL; +} + +static int vidi_check_timing(struct device *dev, void *timing) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* TODO. */ + + return 0; +} + +static int vidi_display_power_on(struct device *dev, int mode) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* TODO */ + + return 0; +} + +static struct exynos_drm_display_ops vidi_display_ops = { + .type = EXYNOS_DISPLAY_TYPE_VIDI, + .is_connected = vidi_display_is_connected, + .get_edid = vidi_get_edid, + .get_panel = vidi_get_panel, + .check_timing = vidi_check_timing, + .power_on = vidi_display_power_on, +}; + +static void vidi_dpms(struct device *subdrv_dev, int mode) +{ + struct vidi_context *ctx = get_vidi_context(subdrv_dev); + + DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode); + + mutex_lock(&ctx->lock); + + switch (mode) { + case DRM_MODE_DPMS_ON: + /* TODO. */ + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + /* TODO. */ + break; + default: + DRM_DEBUG_KMS("unspecified mode %d\n", mode); + break; + } + + mutex_unlock(&ctx->lock); +} + +static void vidi_apply(struct device *subdrv_dev) +{ + struct vidi_context *ctx = get_vidi_context(subdrv_dev); + struct exynos_drm_manager *mgr = &ctx->subdrv.manager; + struct exynos_drm_manager_ops *mgr_ops = mgr->ops; + struct exynos_drm_overlay_ops *ovl_ops = mgr->overlay_ops; + struct vidi_win_data *win_data; + int i; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + for (i = 0; i < WINDOWS_NR; i++) { + win_data = &ctx->win_data[i]; + if (win_data->enabled && (ovl_ops && ovl_ops->commit)) + ovl_ops->commit(subdrv_dev, i); + } + + if (mgr_ops && mgr_ops->commit) + mgr_ops->commit(subdrv_dev); +} + +static void vidi_commit(struct device *dev) +{ + struct vidi_context *ctx = get_vidi_context(dev); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (ctx->suspended) + return; +} + +static int vidi_enable_vblank(struct device *dev) +{ + struct vidi_context *ctx = get_vidi_context(dev); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (ctx->suspended) + return -EPERM; + + if (!test_and_set_bit(0, &ctx->irq_flags)) + ctx->vblank_on = true; + + return 0; +} + +static void vidi_disable_vblank(struct device *dev) +{ + struct vidi_context *ctx = get_vidi_context(dev); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (ctx->suspended) + return; + + if (test_and_clear_bit(0, &ctx->irq_flags)) + ctx->vblank_on = false; +} + +static struct exynos_drm_manager_ops vidi_manager_ops = { + .dpms = vidi_dpms, + .apply = vidi_apply, + .commit = vidi_commit, + .enable_vblank = vidi_enable_vblank, + .disable_vblank = vidi_disable_vblank, +}; + +static void vidi_win_mode_set(struct device *dev, + struct exynos_drm_overlay *overlay) +{ + struct vidi_context *ctx = get_vidi_context(dev); + struct vidi_win_data *win_data; + int win; + unsigned long offset; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (!overlay) { + dev_err(dev, "overlay is NULL\n"); + return; + } + + win = overlay->zpos; + if (win == DEFAULT_ZPOS) + win = ctx->default_win; + + if (win < 0 || win > WINDOWS_NR) + return; + + offset = overlay->fb_x * (overlay->bpp >> 3); + offset += overlay->fb_y * overlay->pitch; + + DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch); + + win_data = &ctx->win_data[win]; + + win_data->offset_x = overlay->crtc_x; + win_data->offset_y = overlay->crtc_y; + win_data->ovl_width = overlay->crtc_width; + win_data->ovl_height = overlay->crtc_height; + win_data->fb_width = overlay->fb_width; + win_data->fb_height = overlay->fb_height; + win_data->dma_addr = overlay->dma_addr[0] + offset; + win_data->vaddr = overlay->vaddr[0] + offset; + win_data->bpp = overlay->bpp; + win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) * + (overlay->bpp >> 3); + win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3); + + /* + * some parts of win_data should be transferred to user side + * through specific ioctl. + */ + + DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n", + win_data->offset_x, win_data->offset_y); + DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", + win_data->ovl_width, win_data->ovl_height); + DRM_DEBUG_KMS("paddr = 0x%lx, vaddr = 0x%lx\n", + (unsigned long)win_data->dma_addr, + (unsigned long)win_data->vaddr); + DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n", + overlay->fb_width, overlay->crtc_width); +} + +static void vidi_win_commit(struct device *dev, int zpos) +{ + struct vidi_context *ctx = get_vidi_context(dev); + struct vidi_win_data *win_data; + int win = zpos; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (ctx->suspended) + return; + + if (win == DEFAULT_ZPOS) + win = ctx->default_win; + + if (win < 0 || win > WINDOWS_NR) + return; + + win_data = &ctx->win_data[win]; + + win_data->enabled = true; + + DRM_DEBUG_KMS("dma_addr = 0x%x\n", win_data->dma_addr); + + if (ctx->vblank_on) + schedule_work(&ctx->work); +} + +static void vidi_win_disable(struct device *dev, int zpos) +{ + struct vidi_context *ctx = get_vidi_context(dev); + struct vidi_win_data *win_data; + int win = zpos; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (win == DEFAULT_ZPOS) + win = ctx->default_win; + + if (win < 0 || win > WINDOWS_NR) + return; + + win_data = &ctx->win_data[win]; + win_data->enabled = false; + + /* TODO. */ +} + +static struct exynos_drm_overlay_ops vidi_overlay_ops = { + .mode_set = vidi_win_mode_set, + .commit = vidi_win_commit, + .disable = vidi_win_disable, +}; + +static void vidi_finish_pageflip(struct drm_device *drm_dev, int crtc) +{ + struct exynos_drm_private *dev_priv = drm_dev->dev_private; + struct drm_pending_vblank_event *e, *t; + struct timeval now; + unsigned long flags; + bool is_checked = false; + + spin_lock_irqsave(&drm_dev->event_lock, flags); + + list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list, + base.link) { + /* if event's pipe isn't same as crtc then ignore it. */ + if (crtc != e->pipe) + continue; + + is_checked = true; + + do_gettimeofday(&now); + e->event.sequence = 0; + e->event.tv_sec = now.tv_sec; + e->event.tv_usec = now.tv_usec; + + list_move_tail(&e->base.link, &e->base.file_priv->event_list); + wake_up_interruptible(&e->base.file_priv->event_wait); + } + + if (is_checked) { + /* + * call drm_vblank_put only in case that drm_vblank_get was + * called. + */ + if (atomic_read(&drm_dev->vblank_refcount[crtc]) > 0) + drm_vblank_put(drm_dev, crtc); + + /* + * don't off vblank if vblank_disable_allowed is 1, + * because vblank would be off by timer handler. + */ + if (!drm_dev->vblank_disable_allowed) + drm_vblank_off(drm_dev, crtc); + } + + spin_unlock_irqrestore(&drm_dev->event_lock, flags); +} + +static void vidi_fake_vblank_handler(struct work_struct *work) +{ + struct vidi_context *ctx = container_of(work, struct vidi_context, + work); + struct exynos_drm_subdrv *subdrv = &ctx->subdrv; + struct exynos_drm_manager *manager = &subdrv->manager; + + if (manager->pipe < 0) + return; + + /* refresh rate is about 50Hz. */ + usleep_range(16000, 20000); + + drm_handle_vblank(subdrv->drm_dev, manager->pipe); + vidi_finish_pageflip(subdrv->drm_dev, manager->pipe); +} + +static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* + * enable drm irq mode. + * - with irq_enabled = 1, we can use the vblank feature. + * + * P.S. note that we wouldn't use drm irq handler but + * just specific driver own one instead because + * drm framework supports only one irq handler. + */ + drm_dev->irq_enabled = 1; + + /* + * with vblank_disable_allowed = 1, vblank interrupt will be disabled + * by drm timer once a current process gives up ownership of + * vblank event.(after drm_vblank_put function is called) + */ + drm_dev->vblank_disable_allowed = 1; + + return 0; +} + +static void vidi_subdrv_remove(struct drm_device *drm_dev) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* TODO. */ +} + +static int vidi_power_on(struct vidi_context *ctx, bool enable) +{ + struct exynos_drm_subdrv *subdrv = &ctx->subdrv; + struct device *dev = subdrv->manager.dev; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (enable != false && enable != true) + return -EINVAL; + + if (enable) { + ctx->suspended = false; + + /* if vblank was enabled status, enable it again. */ + if (test_and_clear_bit(0, &ctx->irq_flags)) + vidi_enable_vblank(dev); + + vidi_apply(dev); + } else { + ctx->suspended = true; + } + + return 0; +} + +static int vidi_show_connection(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int rc; + struct vidi_context *ctx = get_vidi_context(dev); + + mutex_lock(&ctx->lock); + + rc = sprintf(buf, "%d\n", ctx->connected); + + mutex_unlock(&ctx->lock); + + return rc; +} + +static int vidi_store_connection(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct vidi_context *ctx = get_vidi_context(dev); + int ret; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + ret = kstrtoint(buf, 0, &ctx->connected); + if (ret) + return ret; + + if (ctx->connected > 1) + return -EINVAL; + + DRM_DEBUG_KMS("requested connection.\n"); + + drm_helper_hpd_irq_event(ctx->subdrv.drm_dev); + + return len; +} + +static DEVICE_ATTR(connection, 0644, vidi_show_connection, + vidi_store_connection); + +int vidi_connection_ioctl(struct drm_device *drm_dev, void *data, + struct drm_file *file_priv) +{ + struct vidi_context *ctx = NULL; + struct drm_encoder *encoder; + struct exynos_drm_manager *manager; + struct exynos_drm_display_ops *display_ops; + struct drm_exynos_vidi_connection *vidi = data; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (!vidi) { + DRM_DEBUG_KMS("user data for vidi is null.\n"); + return -EINVAL; + } + + if (!vidi->edid) { + DRM_DEBUG_KMS("edid data is null.\n"); + return -EINVAL; + } + + if (vidi->connection > 1) { + DRM_DEBUG_KMS("connection should be 0 or 1.\n"); + return -EINVAL; + } + + list_for_each_entry(encoder, &drm_dev->mode_config.encoder_list, + head) { + manager = exynos_drm_get_manager(encoder); + display_ops = manager->display_ops; + + if (display_ops->type == EXYNOS_DISPLAY_TYPE_VIDI) { + ctx = get_vidi_context(manager->dev); + break; + } + } + + if (!ctx) { + DRM_DEBUG_KMS("not found virtual device type encoder.\n"); + return -EINVAL; + } + + if (ctx->connected == vidi->connection) { + DRM_DEBUG_KMS("same connection request.\n"); + return -EINVAL; + } + + if (vidi->connection) + ctx->raw_edid = vidi->edid; + + ctx->connected = vidi->connection; + drm_helper_hpd_irq_event(ctx->subdrv.drm_dev); + + return 0; +} + +static int __devinit vidi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct vidi_context *ctx; + struct exynos_drm_subdrv *subdrv; + int ret; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->default_win = 0; + + INIT_WORK(&ctx->work, vidi_fake_vblank_handler); + + /* for test */ + ctx->raw_edid = (struct edid *)fake_edid_info; + + subdrv = &ctx->subdrv; + subdrv->probe = vidi_subdrv_probe; + subdrv->remove = vidi_subdrv_remove; + subdrv->manager.pipe = -1; + subdrv->manager.ops = &vidi_manager_ops; + subdrv->manager.overlay_ops = &vidi_overlay_ops; + subdrv->manager.display_ops = &vidi_display_ops; + subdrv->manager.dev = dev; + + mutex_init(&ctx->lock); + + platform_set_drvdata(pdev, ctx); + + /* this sysfs would be used for test. */ + ret = device_create_file(&pdev->dev, &dev_attr_connection); + if (ret < 0) + DRM_INFO("failed to create connection sysfs.\n"); + + exynos_drm_subdrv_register(subdrv); + + return 0; +} + +static int __devexit vidi_remove(struct platform_device *pdev) +{ + struct vidi_context *ctx = platform_get_drvdata(pdev); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + exynos_drm_subdrv_unregister(&ctx->subdrv); + + kfree(ctx); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int vidi_suspend(struct device *dev) +{ + struct vidi_context *ctx = get_vidi_context(dev); + + return vidi_power_on(ctx, false); +} + +static int vidi_resume(struct device *dev) +{ + struct vidi_context *ctx = get_vidi_context(dev); + + return vidi_power_on(ctx, true); +} +#endif + +static const struct dev_pm_ops vidi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(vidi_suspend, vidi_resume) +}; + +struct platform_driver vidi_driver = { + .probe = vidi_probe, + .remove = __devexit_p(vidi_remove), + .driver = { + .name = "exynos-drm-vidi", + .owner = THIS_MODULE, + .pm = &vidi_pm_ops, + }, +}; diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.h b/drivers/gpu/drm/exynos/exynos_drm_vidi.h new file mode 100644 index 0000000..a4babe4 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.h @@ -0,0 +1,36 @@ +/* exynos_drm_vidi.h + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * Author: Inki Dae inki.dae@samsung.com + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _EXYNOS_DRM_VIDI_H_ +#define _EXYNOS_DRM_VIDI_H_ + +#ifdef CONFIG_DRM_EXYNOS_VIDI +int vidi_connection_ioctl(struct drm_device *drm_dev, void *data, + struct drm_file *file_priv); +#else +#define vidi_connection_ioctl NULL +#endif + +#endif diff --git a/include/drm/exynos_drm.h b/include/drm/exynos_drm.h index 907daaf..1123342 100644 --- a/include/drm/exynos_drm.h +++ b/include/drm/exynos_drm.h @@ -76,6 +76,22 @@ struct drm_exynos_gem_mmap { uint64_t mapped; };
+/** + * A structure for user connection request of virtual display. + * + * @connection: indicate whether doing connetion or not by user. + * @extensions: if this value is 1 then the vidi driver would need additional + * 128bytes edid data. + * @pad: just padding to be 64-bit aligned. + * @edid: the edid data pointer from user side. + */ +struct drm_exynos_vidi_connection { + unsigned int connection; + unsigned int extensions; + unsigned int pad; + void *edid; +}; + struct drm_exynos_plane_set_zpos { __u32 plane_id; __s32 zpos; @@ -123,6 +139,7 @@ struct drm_exynos_g2d_exec { #define DRM_EXYNOS_GEM_MMAP 0x02 /* Reserved 0x03 ~ 0x05 for exynos specific gem ioctl */ #define DRM_EXYNOS_PLANE_SET_ZPOS 0x06 +#define DRM_EXYNOS_VIDI_CONNECTION 0x07
/* G2D */ #define DRM_EXYNOS_G2D_GET_VER 0x20 @@ -141,6 +158,9 @@ struct drm_exynos_g2d_exec { #define DRM_IOCTL_EXYNOS_PLANE_SET_ZPOS DRM_IOWR(DRM_COMMAND_BASE + \ DRM_EXYNOS_PLANE_SET_ZPOS, struct drm_exynos_plane_set_zpos)
+#define DRM_IOCTL_EXYNOS_VIDI_CONNECTION DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_EXYNOS_VIDI_CONNECTION, struct drm_exynos_vidi_connection) + #define DRM_IOCTL_EXYNOS_G2D_GET_VER DRM_IOWR(DRM_COMMAND_BASE + \ DRM_EXYNOS_G2D_GET_VER, struct drm_exynos_g2d_get_ver) #define DRM_IOCTL_EXYNOS_G2D_SET_CMDLIST DRM_IOWR(DRM_COMMAND_BASE + \
diff --git a/include/drm/exynos_drm.h b/include/drm/exynos_drm.h index 907daaf..1123342 100644 --- a/include/drm/exynos_drm.h +++ b/include/drm/exynos_drm.h @@ -76,6 +76,22 @@ struct drm_exynos_gem_mmap { uint64_t mapped; };
+/**
- A structure for user connection request of virtual display.
- @connection: indicate whether doing connetion or not by user.
- @extensions: if this value is 1 then the vidi driver would need additional
- 128bytes edid data.
- @pad: just padding to be 64-bit aligned.
- @edid: the edid data pointer from user side.
- */
+struct drm_exynos_vidi_connection {
- unsigned int connection;
- unsigned int extensions;
- unsigned int pad;
- void *edid;
+};
No void * in ioctl structs use u64, also not sure why you have a 32-bit pad since you probably want it padded to 64-bit.
Dave.
-----Original Message----- From: Dave Airlie [mailto:airlied@gmail.com] Sent: Thursday, March 15, 2012 7:44 PM To: Inki Dae Cc: dri-devel@lists.freedesktop.org; kyungmin.park@samsung.com; sw0312.kim@samsung.com Subject: Re: [PATCH 10/10] drm/exynos: added virtual display driver.
diff --git a/include/drm/exynos_drm.h b/include/drm/exynos_drm.h index 907daaf..1123342 100644 --- a/include/drm/exynos_drm.h +++ b/include/drm/exynos_drm.h @@ -76,6 +76,22 @@ struct drm_exynos_gem_mmap { uint64_t mapped; };
+/**
- A structure for user connection request of virtual display.
- @connection: indicate whether doing connetion or not by user.
- @extensions: if this value is 1 then the vidi driver would need
additional
- 128bytes edid data.
- @pad: just padding to be 64-bit aligned.
- @edid: the edid data pointer from user side.
- */
+struct drm_exynos_vidi_connection {
- unsigned int connection;
- unsigned int extensions;
- unsigned int pad;
- void *edid;
+};
No void * in ioctl structs use u64, also not sure why you have a 32-bit pad since you probably want it padded to 64-bit.
Dave.
Yes, right. I wanted it to be padded to 64-bit. and edid would point to buffer containing edit data and it would be passed from user to kernel side so for this, is it right to use u64? I will change variable type to "void __user *" if your missing point. please let me know if there is any problem.
Thanks, Inki Dae
----- Original Message -----
From: "Inki Dae" inki.dae@samsung.com To: "Dave Airlie" airlied@gmail.com Cc: "kyungmin park" kyungmin.park@samsung.com, "sw0312 kim" sw0312.kim@samsung.com, dri-devel@lists.freedesktop.org Sent: Thursday, 15 March, 2012 11:36:14 AM Subject: RE: [PATCH 10/10] drm/exynos: added virtual display driver.
-----Original Message----- From: Dave Airlie [mailto:airlied@gmail.com] Sent: Thursday, March 15, 2012 7:44 PM To: Inki Dae Cc: dri-devel@lists.freedesktop.org; kyungmin.park@samsung.com; sw0312.kim@samsung.com Subject: Re: [PATCH 10/10] drm/exynos: added virtual display driver.
diff --git a/include/drm/exynos_drm.h b/include/drm/exynos_drm.h index 907daaf..1123342 100644 --- a/include/drm/exynos_drm.h +++ b/include/drm/exynos_drm.h @@ -76,6 +76,22 @@ struct drm_exynos_gem_mmap { uint64_t mapped; };
+/**
- A structure for user connection request of virtual display.
- @connection: indicate whether doing connetion or not by user.
- @extensions: if this value is 1 then the vidi driver would
need
additional
- 128bytes edid data.
- @pad: just padding to be 64-bit aligned.
- @edid: the edid data pointer from user side.
- */
+struct drm_exynos_vidi_connection {
- unsigned int connection;
- unsigned int extensions;
- unsigned int pad;
- void *edid;
+};
No void * in ioctl structs use u64, also not sure why you have a 32-bit pad since you probably want it padded to 64-bit.
Dave.
Yes, right. I wanted it to be padded to 64-bit. and edid would point to buffer containing edit data and it would be passed from user to kernel side so for this, is it right to use u64? I will change variable type to "void __user *" if your missing point. please let me know if there is any problem.
You need to use __u64 instead of a void, since void * isn't a fixed length across 32/64-bit.
You'll notice this done a few places in the drm kms interfaces.
Dave.
-----Original Message----- From: David Airlie [mailto:airlied@redhat.com] Sent: Thursday, March 15, 2012 8:44 PM To: Inki Dae Cc: kyungmin park; sw0312 kim; dri-devel@lists.freedesktop.org; Dave Airlie Subject: Re: [PATCH 10/10] drm/exynos: added virtual display driver.
----- Original Message -----
From: "Inki Dae" inki.dae@samsung.com To: "Dave Airlie" airlied@gmail.com Cc: "kyungmin park" kyungmin.park@samsung.com, "sw0312 kim"
dri-devel@lists.freedesktop.org Sent: Thursday, 15 March, 2012 11:36:14 AM Subject: RE: [PATCH 10/10] drm/exynos: added virtual display driver.
-----Original Message----- From: Dave Airlie [mailto:airlied@gmail.com] Sent: Thursday, March 15, 2012 7:44 PM To: Inki Dae Cc: dri-devel@lists.freedesktop.org; kyungmin.park@samsung.com; sw0312.kim@samsung.com Subject: Re: [PATCH 10/10] drm/exynos: added virtual display driver.
diff --git a/include/drm/exynos_drm.h b/include/drm/exynos_drm.h index 907daaf..1123342 100644 --- a/include/drm/exynos_drm.h +++ b/include/drm/exynos_drm.h @@ -76,6 +76,22 @@ struct drm_exynos_gem_mmap { uint64_t mapped; };
+/**
- A structure for user connection request of virtual display.
- @connection: indicate whether doing connetion or not by user.
- @extensions: if this value is 1 then the vidi driver would
need
additional
128bytes edid data.
- @pad: just padding to be 64-bit aligned.
- @edid: the edid data pointer from user side.
- */
+struct drm_exynos_vidi_connection {
unsigned int connection;
unsigned int extensions;
unsigned int pad;
void *edid;
+};
No void * in ioctl structs use u64, also not sure why you have a 32-bit pad since you probably want it padded to 64-bit.
Dave.
Yes, right. I wanted it to be padded to 64-bit. and edid would point to buffer containing edit data and it would be passed from user to kernel side so for this, is it right to use u64? I will change variable type to "void __user *" if your missing point. please let me know if there is any problem.
You need to use __u64 instead of a void, since void * isn't a fixed length across 32/64-bit.
You'll notice this done a few places in the drm kms interfaces.
Dave.
Ok, got it. thanks for your comment.
Inki Dae
From: Joonyoung Shim jy0922.shim@samsung.com
Later Exynos series from Exynos4X12 support HDMI version 1.4. We will distinguish to use which version via platform data.
Signed-off-by: Joonyoung Shim jy0922.shim@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_hdmi.c | 1152 +++++++++++++++++++++++++++++++--- drivers/gpu/drm/exynos/exynos_hdmi.h | 10 +- drivers/gpu/drm/exynos/regs-hdmi.h | 306 ++++++++-- include/drm/exynos_drm.h | 2 + 4 files changed, 1325 insertions(+), 145 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 3429d3f..1cfe86e 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -43,42 +43,43 @@ #define HDMI_OVERLAY_NUMBER 3 #define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev))
-static const u8 hdmiphy_conf27[32] = { +/* HDMI Version 1.3 */ +static const u8 hdmiphy_v13_conf27[32] = { 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40, 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87, 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, };
-static const u8 hdmiphy_conf27_027[32] = { +static const u8 hdmiphy_v13_conf27_027[32] = { 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64, 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87, 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, };
-static const u8 hdmiphy_conf74_175[32] = { +static const u8 hdmiphy_v13_conf74_175[32] = { 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B, 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9, 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00, };
-static const u8 hdmiphy_conf74_25[32] = { +static const u8 hdmiphy_v13_conf74_25[32] = { 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40, 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba, 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0, 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00, };
-static const u8 hdmiphy_conf148_5[32] = { +static const u8 hdmiphy_v13_conf148_5[32] = { 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40, 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba, 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0, 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00, };
-struct hdmi_tg_regs { +struct hdmi_v13_tg_regs { u8 cmd; u8 h_fsz_l; u8 h_fsz_h; @@ -110,7 +111,7 @@ struct hdmi_tg_regs { u8 field_bot_hdmi_h; };
-struct hdmi_core_regs { +struct hdmi_v13_core_regs { u8 h_blank[2]; u8 v_blank[3]; u8 h_v_line[3]; @@ -123,12 +124,21 @@ struct hdmi_core_regs { u8 v_sync_gen3[3]; };
-struct hdmi_preset_conf { - struct hdmi_core_regs core; - struct hdmi_tg_regs tg; +struct hdmi_v13_preset_conf { + struct hdmi_v13_core_regs core; + struct hdmi_v13_tg_regs tg; +}; + +struct hdmi_v13_conf { + int width; + int height; + int vrefresh; + bool interlace; + const u8 *hdmiphy_data; + const struct hdmi_v13_preset_conf *conf; };
-static const struct hdmi_preset_conf hdmi_conf_480p = { +static const struct hdmi_v13_preset_conf hdmi_v13_conf_480p = { .core = { .h_blank = {0x8a, 0x00}, .v_blank = {0x0d, 0x6a, 0x01}, @@ -154,7 +164,7 @@ static const struct hdmi_preset_conf hdmi_conf_480p = { }, };
-static const struct hdmi_preset_conf hdmi_conf_720p60 = { +static const struct hdmi_v13_preset_conf hdmi_v13_conf_720p60 = { .core = { .h_blank = {0x72, 0x01}, .v_blank = {0xee, 0xf2, 0x00}, @@ -182,7 +192,7 @@ static const struct hdmi_preset_conf hdmi_conf_720p60 = { }, };
-static const struct hdmi_preset_conf hdmi_conf_1080i50 = { +static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080i50 = { .core = { .h_blank = {0xd0, 0x02}, .v_blank = {0x32, 0xB2, 0x00}, @@ -210,7 +220,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080i50 = { }, };
-static const struct hdmi_preset_conf hdmi_conf_1080p50 = { +static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080p50 = { .core = { .h_blank = {0xd0, 0x02}, .v_blank = {0x65, 0x6c, 0x01}, @@ -238,7 +248,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080p50 = { }, };
-static const struct hdmi_preset_conf hdmi_conf_1080i60 = { +static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080i60 = { .core = { .h_blank = {0x18, 0x01}, .v_blank = {0x32, 0xB2, 0x00}, @@ -266,7 +276,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080i60 = { }, };
-static const struct hdmi_preset_conf hdmi_conf_1080p60 = { +static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080p60 = { .core = { .h_blank = {0x18, 0x01}, .v_blank = {0x65, 0x6c, 0x01}, @@ -294,13 +304,530 @@ static const struct hdmi_preset_conf hdmi_conf_1080p60 = { }, };
+static const struct hdmi_v13_conf hdmi_v13_confs[] = { + { 1280, 720, 60, false, hdmiphy_v13_conf74_25, &hdmi_v13_conf_720p60 }, + { 1280, 720, 50, false, hdmiphy_v13_conf74_25, &hdmi_v13_conf_720p60 }, + { 720, 480, 60, false, hdmiphy_v13_conf27_027, &hdmi_v13_conf_480p }, + { 1920, 1080, 50, true, hdmiphy_v13_conf74_25, &hdmi_v13_conf_1080i50 }, + { 1920, 1080, 50, false, hdmiphy_v13_conf148_5, + &hdmi_v13_conf_1080p50 }, + { 1920, 1080, 60, true, hdmiphy_v13_conf74_25, &hdmi_v13_conf_1080i60 }, + { 1920, 1080, 60, false, hdmiphy_v13_conf148_5, + &hdmi_v13_conf_1080p60 }, +}; + +/* HDMI Version 1.4 */ +static const u8 hdmiphy_conf27_027[32] = { + 0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08, + 0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80, + 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, + 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, +}; + +static const u8 hdmiphy_conf74_25[32] = { + 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08, + 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80, + 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, + 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, +}; + +static const u8 hdmiphy_conf148_5[32] = { + 0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08, + 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80, + 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, + 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00, +}; + +struct hdmi_tg_regs { + u8 cmd; + u8 h_fsz_l; + u8 h_fsz_h; + u8 hact_st_l; + u8 hact_st_h; + u8 hact_sz_l; + u8 hact_sz_h; + u8 v_fsz_l; + u8 v_fsz_h; + u8 vsync_l; + u8 vsync_h; + u8 vsync2_l; + u8 vsync2_h; + u8 vact_st_l; + u8 vact_st_h; + u8 vact_sz_l; + u8 vact_sz_h; + u8 field_chg_l; + u8 field_chg_h; + u8 vact_st2_l; + u8 vact_st2_h; + u8 vact_st3_l; + u8 vact_st3_h; + u8 vact_st4_l; + u8 vact_st4_h; + u8 vsync_top_hdmi_l; + u8 vsync_top_hdmi_h; + u8 vsync_bot_hdmi_l; + u8 vsync_bot_hdmi_h; + u8 field_top_hdmi_l; + u8 field_top_hdmi_h; + u8 field_bot_hdmi_l; + u8 field_bot_hdmi_h; + u8 tg_3d; +}; + +struct hdmi_core_regs { + u8 h_blank[2]; + u8 v2_blank[2]; + u8 v1_blank[2]; + u8 v_line[2]; + u8 h_line[2]; + u8 hsync_pol[1]; + u8 vsync_pol[1]; + u8 int_pro_mode[1]; + u8 v_blank_f0[2]; + u8 v_blank_f1[2]; + u8 h_sync_start[2]; + u8 h_sync_end[2]; + u8 v_sync_line_bef_2[2]; + u8 v_sync_line_bef_1[2]; + u8 v_sync_line_aft_2[2]; + u8 v_sync_line_aft_1[2]; + u8 v_sync_line_aft_pxl_2[2]; + u8 v_sync_line_aft_pxl_1[2]; + u8 v_blank_f2[2]; /* for 3D mode */ + u8 v_blank_f3[2]; /* for 3D mode */ + u8 v_blank_f4[2]; /* for 3D mode */ + u8 v_blank_f5[2]; /* for 3D mode */ + u8 v_sync_line_aft_3[2]; + u8 v_sync_line_aft_4[2]; + u8 v_sync_line_aft_5[2]; + u8 v_sync_line_aft_6[2]; + u8 v_sync_line_aft_pxl_3[2]; + u8 v_sync_line_aft_pxl_4[2]; + u8 v_sync_line_aft_pxl_5[2]; + u8 v_sync_line_aft_pxl_6[2]; + u8 vact_space_1[2]; + u8 vact_space_2[2]; + u8 vact_space_3[2]; + u8 vact_space_4[2]; + u8 vact_space_5[2]; + u8 vact_space_6[2]; +}; + +struct hdmi_preset_conf { + struct hdmi_core_regs core; + struct hdmi_tg_regs tg; +}; + +struct hdmi_conf { + int width; + int height; + int vrefresh; + bool interlace; + const u8 *hdmiphy_data; + const struct hdmi_preset_conf *conf; +}; + +static const struct hdmi_preset_conf hdmi_conf_480p60 = { + .core = { + .h_blank = {0x8a, 0x00}, + .v2_blank = {0x0d, 0x02}, + .v1_blank = {0x2d, 0x00}, + .v_line = {0x0d, 0x02}, + .h_line = {0x5a, 0x03}, + .hsync_pol = {0x01}, + .vsync_pol = {0x01}, + .int_pro_mode = {0x00}, + .v_blank_f0 = {0xff, 0xff}, + .v_blank_f1 = {0xff, 0xff}, + .h_sync_start = {0x0e, 0x00}, + .h_sync_end = {0x4c, 0x00}, + .v_sync_line_bef_2 = {0x0f, 0x00}, + .v_sync_line_bef_1 = {0x09, 0x00}, + .v_sync_line_aft_2 = {0xff, 0xff}, + .v_sync_line_aft_1 = {0xff, 0xff}, + .v_sync_line_aft_pxl_2 = {0xff, 0xff}, + .v_sync_line_aft_pxl_1 = {0xff, 0xff}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + .vact_space_1 = {0xff, 0xff}, + .vact_space_2 = {0xff, 0xff}, + .vact_space_3 = {0xff, 0xff}, + .vact_space_4 = {0xff, 0xff}, + .vact_space_5 = {0xff, 0xff}, + .vact_space_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0x5a, 0x03, /* h_fsz */ + 0x8a, 0x00, 0xd0, 0x02, /* hact */ + 0x0d, 0x02, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x2d, 0x00, 0xe0, 0x01, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x48, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + +static const struct hdmi_preset_conf hdmi_conf_720p50 = { + .core = { + .h_blank = {0xbc, 0x02}, + .v2_blank = {0xee, 0x02}, + .v1_blank = {0x1e, 0x00}, + .v_line = {0xee, 0x02}, + .h_line = {0xbc, 0x07}, + .hsync_pol = {0x00}, + .vsync_pol = {0x00}, + .int_pro_mode = {0x00}, + .v_blank_f0 = {0xff, 0xff}, + .v_blank_f1 = {0xff, 0xff}, + .h_sync_start = {0xb6, 0x01}, + .h_sync_end = {0xde, 0x01}, + .v_sync_line_bef_2 = {0x0a, 0x00}, + .v_sync_line_bef_1 = {0x05, 0x00}, + .v_sync_line_aft_2 = {0xff, 0xff}, + .v_sync_line_aft_1 = {0xff, 0xff}, + .v_sync_line_aft_pxl_2 = {0xff, 0xff}, + .v_sync_line_aft_pxl_1 = {0xff, 0xff}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + .vact_space_1 = {0xff, 0xff}, + .vact_space_2 = {0xff, 0xff}, + .vact_space_3 = {0xff, 0xff}, + .vact_space_4 = {0xff, 0xff}, + .vact_space_5 = {0xff, 0xff}, + .vact_space_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0xbc, 0x07, /* h_fsz */ + 0xbc, 0x02, 0x00, 0x05, /* hact */ + 0xee, 0x02, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x1e, 0x00, 0xd0, 0x02, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x48, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + +static const struct hdmi_preset_conf hdmi_conf_720p60 = { + .core = { + .h_blank = {0x72, 0x01}, + .v2_blank = {0xee, 0x02}, + .v1_blank = {0x1e, 0x00}, + .v_line = {0xee, 0x02}, + .h_line = {0x72, 0x06}, + .hsync_pol = {0x00}, + .vsync_pol = {0x00}, + .int_pro_mode = {0x00}, + .v_blank_f0 = {0xff, 0xff}, + .v_blank_f1 = {0xff, 0xff}, + .h_sync_start = {0x6c, 0x00}, + .h_sync_end = {0x94, 0x00}, + .v_sync_line_bef_2 = {0x0a, 0x00}, + .v_sync_line_bef_1 = {0x05, 0x00}, + .v_sync_line_aft_2 = {0xff, 0xff}, + .v_sync_line_aft_1 = {0xff, 0xff}, + .v_sync_line_aft_pxl_2 = {0xff, 0xff}, + .v_sync_line_aft_pxl_1 = {0xff, 0xff}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + .vact_space_1 = {0xff, 0xff}, + .vact_space_2 = {0xff, 0xff}, + .vact_space_3 = {0xff, 0xff}, + .vact_space_4 = {0xff, 0xff}, + .vact_space_5 = {0xff, 0xff}, + .vact_space_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0x72, 0x06, /* h_fsz */ + 0x72, 0x01, 0x00, 0x05, /* hact */ + 0xee, 0x02, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x1e, 0x00, 0xd0, 0x02, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x48, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + +static const struct hdmi_preset_conf hdmi_conf_1080i50 = { + .core = { + .h_blank = {0xd0, 0x02}, + .v2_blank = {0x32, 0x02}, + .v1_blank = {0x16, 0x00}, + .v_line = {0x65, 0x04}, + .h_line = {0x50, 0x0a}, + .hsync_pol = {0x00}, + .vsync_pol = {0x00}, + .int_pro_mode = {0x01}, + .v_blank_f0 = {0x49, 0x02}, + .v_blank_f1 = {0x65, 0x04}, + .h_sync_start = {0x0e, 0x02}, + .h_sync_end = {0x3a, 0x02}, + .v_sync_line_bef_2 = {0x07, 0x00}, + .v_sync_line_bef_1 = {0x02, 0x00}, + .v_sync_line_aft_2 = {0x39, 0x02}, + .v_sync_line_aft_1 = {0x34, 0x02}, + .v_sync_line_aft_pxl_2 = {0x38, 0x07}, + .v_sync_line_aft_pxl_1 = {0x38, 0x07}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + .vact_space_1 = {0xff, 0xff}, + .vact_space_2 = {0xff, 0xff}, + .vact_space_3 = {0xff, 0xff}, + .vact_space_4 = {0xff, 0xff}, + .vact_space_5 = {0xff, 0xff}, + .vact_space_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0x50, 0x0a, /* h_fsz */ + 0xd0, 0x02, 0x80, 0x07, /* hact */ + 0x65, 0x04, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x16, 0x00, 0x1c, 0x02, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x49, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + +static const struct hdmi_preset_conf hdmi_conf_1080i60 = { + .core = { + .h_blank = {0x18, 0x01}, + .v2_blank = {0x32, 0x02}, + .v1_blank = {0x16, 0x00}, + .v_line = {0x65, 0x04}, + .h_line = {0x98, 0x08}, + .hsync_pol = {0x00}, + .vsync_pol = {0x00}, + .int_pro_mode = {0x01}, + .v_blank_f0 = {0x49, 0x02}, + .v_blank_f1 = {0x65, 0x04}, + .h_sync_start = {0x56, 0x00}, + .h_sync_end = {0x82, 0x00}, + .v_sync_line_bef_2 = {0x07, 0x00}, + .v_sync_line_bef_1 = {0x02, 0x00}, + .v_sync_line_aft_2 = {0x39, 0x02}, + .v_sync_line_aft_1 = {0x34, 0x02}, + .v_sync_line_aft_pxl_2 = {0xa4, 0x04}, + .v_sync_line_aft_pxl_1 = {0xa4, 0x04}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + .vact_space_1 = {0xff, 0xff}, + .vact_space_2 = {0xff, 0xff}, + .vact_space_3 = {0xff, 0xff}, + .vact_space_4 = {0xff, 0xff}, + .vact_space_5 = {0xff, 0xff}, + .vact_space_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0x98, 0x08, /* h_fsz */ + 0x18, 0x01, 0x80, 0x07, /* hact */ + 0x65, 0x04, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x16, 0x00, 0x1c, 0x02, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x49, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + +static const struct hdmi_preset_conf hdmi_conf_1080p50 = { + .core = { + .h_blank = {0xd0, 0x02}, + .v2_blank = {0x65, 0x04}, + .v1_blank = {0x2d, 0x00}, + .v_line = {0x65, 0x04}, + .h_line = {0x50, 0x0a}, + .hsync_pol = {0x00}, + .vsync_pol = {0x00}, + .int_pro_mode = {0x00}, + .v_blank_f0 = {0xff, 0xff}, + .v_blank_f1 = {0xff, 0xff}, + .h_sync_start = {0x0e, 0x02}, + .h_sync_end = {0x3a, 0x02}, + .v_sync_line_bef_2 = {0x09, 0x00}, + .v_sync_line_bef_1 = {0x04, 0x00}, + .v_sync_line_aft_2 = {0xff, 0xff}, + .v_sync_line_aft_1 = {0xff, 0xff}, + .v_sync_line_aft_pxl_2 = {0xff, 0xff}, + .v_sync_line_aft_pxl_1 = {0xff, 0xff}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + .vact_space_1 = {0xff, 0xff}, + .vact_space_2 = {0xff, 0xff}, + .vact_space_3 = {0xff, 0xff}, + .vact_space_4 = {0xff, 0xff}, + .vact_space_5 = {0xff, 0xff}, + .vact_space_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0x50, 0x0a, /* h_fsz */ + 0xd0, 0x02, 0x80, 0x07, /* hact */ + 0x65, 0x04, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x2d, 0x00, 0x38, 0x04, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x48, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + +static const struct hdmi_preset_conf hdmi_conf_1080p60 = { + .core = { + .h_blank = {0x18, 0x01}, + .v2_blank = {0x65, 0x04}, + .v1_blank = {0x2d, 0x00}, + .v_line = {0x65, 0x04}, + .h_line = {0x98, 0x08}, + .hsync_pol = {0x00}, + .vsync_pol = {0x00}, + .int_pro_mode = {0x00}, + .v_blank_f0 = {0xff, 0xff}, + .v_blank_f1 = {0xff, 0xff}, + .h_sync_start = {0x56, 0x00}, + .h_sync_end = {0x82, 0x00}, + .v_sync_line_bef_2 = {0x09, 0x00}, + .v_sync_line_bef_1 = {0x04, 0x00}, + .v_sync_line_aft_2 = {0xff, 0xff}, + .v_sync_line_aft_1 = {0xff, 0xff}, + .v_sync_line_aft_pxl_2 = {0xff, 0xff}, + .v_sync_line_aft_pxl_1 = {0xff, 0xff}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0x98, 0x08, /* h_fsz */ + 0x18, 0x01, 0x80, 0x07, /* hact */ + 0x65, 0x04, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x2d, 0x00, 0x38, 0x04, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x48, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + static const struct hdmi_conf hdmi_confs[] = { + { 720, 480, 60, false, hdmiphy_conf27_027, &hdmi_conf_480p60 }, + { 1280, 720, 50, false, hdmiphy_conf74_25, &hdmi_conf_720p50 }, { 1280, 720, 60, false, hdmiphy_conf74_25, &hdmi_conf_720p60 }, - { 1280, 720, 50, false, hdmiphy_conf74_25, &hdmi_conf_720p60 }, - { 720, 480, 60, false, hdmiphy_conf27_027, &hdmi_conf_480p }, { 1920, 1080, 50, true, hdmiphy_conf74_25, &hdmi_conf_1080i50 }, - { 1920, 1080, 50, false, hdmiphy_conf148_5, &hdmi_conf_1080p50 }, { 1920, 1080, 60, true, hdmiphy_conf74_25, &hdmi_conf_1080i60 }, + { 1920, 1080, 50, false, hdmiphy_conf148_5, &hdmi_conf_1080p50 }, { 1920, 1080, 60, false, hdmiphy_conf148_5, &hdmi_conf_1080p60 }, };
@@ -324,7 +851,7 @@ static inline void hdmi_reg_writemask(struct hdmi_context *hdata, writel(value, hdata->regs + reg_id); }
-static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) +static void hdmi_v13_regs_dump(struct hdmi_context *hdata, char *prefix) { #define DUMPREG(reg_id) \ DRM_DEBUG_KMS("%s:" #reg_id " = %08x\n", prefix, \ @@ -333,6 +860,101 @@ static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) DUMPREG(HDMI_INTC_FLAG); DUMPREG(HDMI_INTC_CON); DUMPREG(HDMI_HPD_STATUS); + DUMPREG(HDMI_V13_PHY_RSTOUT); + DUMPREG(HDMI_V13_PHY_VPLL); + DUMPREG(HDMI_V13_PHY_CMU); + DUMPREG(HDMI_V13_CORE_RSTOUT); + + DRM_DEBUG_KMS("%s: ---- CORE REGISTERS ----\n", prefix); + DUMPREG(HDMI_CON_0); + DUMPREG(HDMI_CON_1); + DUMPREG(HDMI_CON_2); + DUMPREG(HDMI_SYS_STATUS); + DUMPREG(HDMI_V13_PHY_STATUS); + DUMPREG(HDMI_STATUS_EN); + DUMPREG(HDMI_HPD); + DUMPREG(HDMI_MODE_SEL); + DUMPREG(HDMI_V13_HPD_GEN); + DUMPREG(HDMI_V13_DC_CONTROL); + DUMPREG(HDMI_V13_VIDEO_PATTERN_GEN); + + DRM_DEBUG_KMS("%s: ---- CORE SYNC REGISTERS ----\n", prefix); + DUMPREG(HDMI_H_BLANK_0); + DUMPREG(HDMI_H_BLANK_1); + DUMPREG(HDMI_V13_V_BLANK_0); + DUMPREG(HDMI_V13_V_BLANK_1); + DUMPREG(HDMI_V13_V_BLANK_2); + DUMPREG(HDMI_V13_H_V_LINE_0); + DUMPREG(HDMI_V13_H_V_LINE_1); + DUMPREG(HDMI_V13_H_V_LINE_2); + DUMPREG(HDMI_VSYNC_POL); + DUMPREG(HDMI_INT_PRO_MODE); + DUMPREG(HDMI_V13_V_BLANK_F_0); + DUMPREG(HDMI_V13_V_BLANK_F_1); + DUMPREG(HDMI_V13_V_BLANK_F_2); + DUMPREG(HDMI_V13_H_SYNC_GEN_0); + DUMPREG(HDMI_V13_H_SYNC_GEN_1); + DUMPREG(HDMI_V13_H_SYNC_GEN_2); + DUMPREG(HDMI_V13_V_SYNC_GEN_1_0); + DUMPREG(HDMI_V13_V_SYNC_GEN_1_1); + DUMPREG(HDMI_V13_V_SYNC_GEN_1_2); + DUMPREG(HDMI_V13_V_SYNC_GEN_2_0); + DUMPREG(HDMI_V13_V_SYNC_GEN_2_1); + DUMPREG(HDMI_V13_V_SYNC_GEN_2_2); + DUMPREG(HDMI_V13_V_SYNC_GEN_3_0); + DUMPREG(HDMI_V13_V_SYNC_GEN_3_1); + DUMPREG(HDMI_V13_V_SYNC_GEN_3_2); + + DRM_DEBUG_KMS("%s: ---- TG REGISTERS ----\n", prefix); + DUMPREG(HDMI_TG_CMD); + DUMPREG(HDMI_TG_H_FSZ_L); + DUMPREG(HDMI_TG_H_FSZ_H); + DUMPREG(HDMI_TG_HACT_ST_L); + DUMPREG(HDMI_TG_HACT_ST_H); + DUMPREG(HDMI_TG_HACT_SZ_L); + DUMPREG(HDMI_TG_HACT_SZ_H); + DUMPREG(HDMI_TG_V_FSZ_L); + DUMPREG(HDMI_TG_V_FSZ_H); + DUMPREG(HDMI_TG_VSYNC_L); + DUMPREG(HDMI_TG_VSYNC_H); + DUMPREG(HDMI_TG_VSYNC2_L); + DUMPREG(HDMI_TG_VSYNC2_H); + DUMPREG(HDMI_TG_VACT_ST_L); + DUMPREG(HDMI_TG_VACT_ST_H); + DUMPREG(HDMI_TG_VACT_SZ_L); + DUMPREG(HDMI_TG_VACT_SZ_H); + DUMPREG(HDMI_TG_FIELD_CHG_L); + DUMPREG(HDMI_TG_FIELD_CHG_H); + DUMPREG(HDMI_TG_VACT_ST2_L); + DUMPREG(HDMI_TG_VACT_ST2_H); + DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L); + DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H); + DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L); + DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H); + DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L); + DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H); + DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L); + DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H); +#undef DUMPREG +} + +static void hdmi_v14_regs_dump(struct hdmi_context *hdata, char *prefix) +{ + int i; + +#define DUMPREG(reg_id) \ + DRM_DEBUG_KMS("%s:" #reg_id " = %08x\n", prefix, \ + readl(hdata->regs + reg_id)) + + DRM_DEBUG_KMS("%s: ---- CONTROL REGISTERS ----\n", prefix); + DUMPREG(HDMI_INTC_CON); + DUMPREG(HDMI_INTC_FLAG); + DUMPREG(HDMI_HPD_STATUS); + DUMPREG(HDMI_INTC_CON_1); + DUMPREG(HDMI_INTC_FLAG_1); + DUMPREG(HDMI_PHY_STATUS_0); + DUMPREG(HDMI_PHY_STATUS_PLL); + DUMPREG(HDMI_PHY_CON_0); DUMPREG(HDMI_PHY_RSTOUT); DUMPREG(HDMI_PHY_VPLL); DUMPREG(HDMI_PHY_CMU); @@ -343,40 +965,93 @@ static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) DUMPREG(HDMI_CON_1); DUMPREG(HDMI_CON_2); DUMPREG(HDMI_SYS_STATUS); - DUMPREG(HDMI_PHY_STATUS); + DUMPREG(HDMI_PHY_STATUS_0); DUMPREG(HDMI_STATUS_EN); DUMPREG(HDMI_HPD); DUMPREG(HDMI_MODE_SEL); - DUMPREG(HDMI_HPD_GEN); + DUMPREG(HDMI_ENC_EN); DUMPREG(HDMI_DC_CONTROL); DUMPREG(HDMI_VIDEO_PATTERN_GEN);
DRM_DEBUG_KMS("%s: ---- CORE SYNC REGISTERS ----\n", prefix); DUMPREG(HDMI_H_BLANK_0); DUMPREG(HDMI_H_BLANK_1); - DUMPREG(HDMI_V_BLANK_0); - DUMPREG(HDMI_V_BLANK_1); - DUMPREG(HDMI_V_BLANK_2); - DUMPREG(HDMI_H_V_LINE_0); - DUMPREG(HDMI_H_V_LINE_1); - DUMPREG(HDMI_H_V_LINE_2); + DUMPREG(HDMI_V2_BLANK_0); + DUMPREG(HDMI_V2_BLANK_1); + DUMPREG(HDMI_V1_BLANK_0); + DUMPREG(HDMI_V1_BLANK_1); + DUMPREG(HDMI_V_LINE_0); + DUMPREG(HDMI_V_LINE_1); + DUMPREG(HDMI_H_LINE_0); + DUMPREG(HDMI_H_LINE_1); + DUMPREG(HDMI_HSYNC_POL); + DUMPREG(HDMI_VSYNC_POL); DUMPREG(HDMI_INT_PRO_MODE); - DUMPREG(HDMI_V_BLANK_F_0); - DUMPREG(HDMI_V_BLANK_F_1); - DUMPREG(HDMI_V_BLANK_F_2); - DUMPREG(HDMI_H_SYNC_GEN_0); - DUMPREG(HDMI_H_SYNC_GEN_1); - DUMPREG(HDMI_H_SYNC_GEN_2); - DUMPREG(HDMI_V_SYNC_GEN_1_0); - DUMPREG(HDMI_V_SYNC_GEN_1_1); - DUMPREG(HDMI_V_SYNC_GEN_1_2); - DUMPREG(HDMI_V_SYNC_GEN_2_0); - DUMPREG(HDMI_V_SYNC_GEN_2_1); - DUMPREG(HDMI_V_SYNC_GEN_2_2); - DUMPREG(HDMI_V_SYNC_GEN_3_0); - DUMPREG(HDMI_V_SYNC_GEN_3_1); - DUMPREG(HDMI_V_SYNC_GEN_3_2); + DUMPREG(HDMI_V_BLANK_F0_0); + DUMPREG(HDMI_V_BLANK_F0_1); + DUMPREG(HDMI_V_BLANK_F1_0); + DUMPREG(HDMI_V_BLANK_F1_1); + + DUMPREG(HDMI_H_SYNC_START_0); + DUMPREG(HDMI_H_SYNC_START_1); + DUMPREG(HDMI_H_SYNC_END_0); + DUMPREG(HDMI_H_SYNC_END_1); + + DUMPREG(HDMI_V_SYNC_LINE_BEF_2_0); + DUMPREG(HDMI_V_SYNC_LINE_BEF_2_1); + DUMPREG(HDMI_V_SYNC_LINE_BEF_1_0); + DUMPREG(HDMI_V_SYNC_LINE_BEF_1_1); + + DUMPREG(HDMI_V_SYNC_LINE_AFT_2_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_2_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_1_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_1_1); + + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_2_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_2_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_1_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_1_1); + + DUMPREG(HDMI_V_BLANK_F2_0); + DUMPREG(HDMI_V_BLANK_F2_1); + DUMPREG(HDMI_V_BLANK_F3_0); + DUMPREG(HDMI_V_BLANK_F3_1); + DUMPREG(HDMI_V_BLANK_F4_0); + DUMPREG(HDMI_V_BLANK_F4_1); + DUMPREG(HDMI_V_BLANK_F5_0); + DUMPREG(HDMI_V_BLANK_F5_1); + + DUMPREG(HDMI_V_SYNC_LINE_AFT_3_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_3_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_4_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_4_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_5_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_5_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_6_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_6_1); + + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_3_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_3_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_4_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_4_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_5_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_5_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_6_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_6_1); + + DUMPREG(HDMI_VACT_SPACE_1_0); + DUMPREG(HDMI_VACT_SPACE_1_1); + DUMPREG(HDMI_VACT_SPACE_2_0); + DUMPREG(HDMI_VACT_SPACE_2_1); + DUMPREG(HDMI_VACT_SPACE_3_0); + DUMPREG(HDMI_VACT_SPACE_3_1); + DUMPREG(HDMI_VACT_SPACE_4_0); + DUMPREG(HDMI_VACT_SPACE_4_1); + DUMPREG(HDMI_VACT_SPACE_5_0); + DUMPREG(HDMI_VACT_SPACE_5_1); + DUMPREG(HDMI_VACT_SPACE_6_0); + DUMPREG(HDMI_VACT_SPACE_6_1);
DRM_DEBUG_KMS("%s: ---- TG REGISTERS ----\n", prefix); DUMPREG(HDMI_TG_CMD); @@ -400,6 +1075,10 @@ static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) DUMPREG(HDMI_TG_FIELD_CHG_H); DUMPREG(HDMI_TG_VACT_ST2_L); DUMPREG(HDMI_TG_VACT_ST2_H); + DUMPREG(HDMI_TG_VACT_ST3_L); + DUMPREG(HDMI_TG_VACT_ST3_H); + DUMPREG(HDMI_TG_VACT_ST4_L); + DUMPREG(HDMI_TG_VACT_ST4_H); DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L); DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H); DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L); @@ -408,10 +1087,49 @@ static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H); DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L); DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H); + DUMPREG(HDMI_TG_3D); + + DRM_DEBUG_KMS("%s: ---- PACKET REGISTERS ----\n", prefix); + DUMPREG(HDMI_AVI_CON); + DUMPREG(HDMI_AVI_HEADER0); + DUMPREG(HDMI_AVI_HEADER1); + DUMPREG(HDMI_AVI_HEADER2); + DUMPREG(HDMI_AVI_CHECK_SUM); + DUMPREG(HDMI_VSI_CON); + DUMPREG(HDMI_VSI_HEADER0); + DUMPREG(HDMI_VSI_HEADER1); + DUMPREG(HDMI_VSI_HEADER2); + for (i = 0; i < 7; ++i) + DUMPREG(HDMI_VSI_DATA(i)); + #undef DUMPREG }
-static int hdmi_conf_index(struct drm_display_mode *mode) +static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) +{ + if (hdata->is_v13) + hdmi_v13_regs_dump(hdata, prefix); + else + hdmi_v14_regs_dump(hdata, prefix); +} + +static int hdmi_v13_conf_index(struct drm_display_mode *mode) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hdmi_v13_confs); ++i) + if (hdmi_v13_confs[i].width == mode->hdisplay && + hdmi_v13_confs[i].height == mode->vdisplay && + hdmi_v13_confs[i].vrefresh == mode->vrefresh && + hdmi_v13_confs[i].interlace == + ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? + true : false)) + return i; + + return -1; +} + +static int hdmi_v14_conf_index(struct drm_display_mode *mode) { int i;
@@ -427,6 +1145,15 @@ static int hdmi_conf_index(struct drm_display_mode *mode) return -1; }
+static int hdmi_conf_index(struct hdmi_context *hdata, + struct drm_display_mode *mode) +{ + if (hdata->is_v13) + return hdmi_v13_conf_index(mode); + else + return hdmi_v14_conf_index(mode); +} + static bool hdmi_is_connected(void *ctx) { struct hdmi_context *hdata = (struct hdmi_context *)ctx; @@ -462,16 +1189,25 @@ static int hdmi_get_edid(void *ctx, struct drm_connector *connector, return 0; }
-static int hdmi_check_timing(void *ctx, void *timing) +static int hdmi_v13_check_timing(struct fb_videomode *check_timing) { - struct fb_videomode *check_timing = timing; int i;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + for (i = 0; i < ARRAY_SIZE(hdmi_v13_confs); ++i) + if (hdmi_v13_confs[i].width == check_timing->xres && + hdmi_v13_confs[i].height == check_timing->yres && + hdmi_v13_confs[i].vrefresh == check_timing->refresh && + hdmi_v13_confs[i].interlace == + ((check_timing->vmode & FB_VMODE_INTERLACED) ? + true : false)) + return 0;
- DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", check_timing->xres, - check_timing->yres, check_timing->refresh, - check_timing->vmode); + return -EINVAL; +} + +static int hdmi_v14_check_timing(struct fb_videomode *check_timing) +{ + int i;
for (i = 0; i < ARRAY_SIZE(hdmi_confs); ++i) if (hdmi_confs[i].width == check_timing->xres && @@ -485,6 +1221,23 @@ static int hdmi_check_timing(void *ctx, void *timing) return -EINVAL; }
+static int hdmi_check_timing(void *ctx, void *timing) +{ + struct hdmi_context *hdata = (struct hdmi_context *)ctx; + struct fb_videomode *check_timing = timing; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", check_timing->xres, + check_timing->yres, check_timing->refresh, + check_timing->vmode); + + if (hdata->is_v13) + return hdmi_v13_check_timing(check_timing); + else + return hdmi_v14_check_timing(check_timing); +} + static int hdmi_display_power_on(void *ctx, int mode) { DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); @@ -516,13 +1269,20 @@ static struct exynos_hdmi_display_ops display_ops = {
static void hdmi_conf_reset(struct hdmi_context *hdata) { + u32 reg; + /* disable hpd handle for drm */ hdata->hpd_handle = false;
+ if (hdata->is_v13) + reg = HDMI_V13_CORE_RSTOUT; + else + reg = HDMI_CORE_RSTOUT; + /* resetting HDMI core */ - hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, HDMI_CORE_SW_RSTOUT); + hdmi_reg_writemask(hdata, reg, 0, HDMI_CORE_SW_RSTOUT); mdelay(10); - hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, HDMI_CORE_SW_RSTOUT); + hdmi_reg_writemask(hdata, reg, ~0, HDMI_CORE_SW_RSTOUT); mdelay(10);
/* enable hpd handle for drm */ @@ -546,27 +1306,126 @@ static void hdmi_conf_init(struct hdmi_context *hdata) HDMI_MODE_HDMI_EN, HDMI_MODE_MASK); /* disable bluescreen */ hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN); - /* choose bluescreen (fecal) color */ - hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_0, 0x12); - hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_1, 0x34); - hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_2, 0x56); - /* enable AVI packet every vsync, fixes purple line problem */ - hdmi_reg_writeb(hdata, HDMI_AVI_CON, 0x02); - /* force RGB, look to CEA-861-D, table 7 for more detail */ - hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(0), 0 << 5); - hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5); - - hdmi_reg_writeb(hdata, HDMI_SPD_CON, 0x02); - hdmi_reg_writeb(hdata, HDMI_AUI_CON, 0x02); - hdmi_reg_writeb(hdata, HDMI_ACR_CON, 0x04); + + if (hdata->is_v13) { + /* choose bluescreen (fecal) color */ + hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12); + hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34); + hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_2, 0x56); + + /* enable AVI packet every vsync, fixes purple line problem */ + hdmi_reg_writeb(hdata, HDMI_V13_AVI_CON, 0x02); + /* force RGB, look to CEA-861-D, table 7 for more detail */ + hdmi_reg_writeb(hdata, HDMI_V13_AVI_BYTE(0), 0 << 5); + hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5); + + hdmi_reg_writeb(hdata, HDMI_V13_SPD_CON, 0x02); + hdmi_reg_writeb(hdata, HDMI_V13_AUI_CON, 0x02); + hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04); + } else { + /* enable AVI packet every vsync, fixes purple line problem */ + hdmi_reg_writeb(hdata, HDMI_AVI_CON, 0x02); + hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(1), 2 << 5); + hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5); + }
/* enable hpd handle for drm */ hdata->hpd_handle = true; }
-static void hdmi_timing_apply(struct hdmi_context *hdata, - const struct hdmi_preset_conf *conf) +static void hdmi_v13_timing_apply(struct hdmi_context *hdata) { + const struct hdmi_v13_preset_conf *conf = + hdmi_v13_confs[hdata->cur_conf].conf; + const struct hdmi_v13_core_regs *core = &conf->core; + const struct hdmi_v13_tg_regs *tg = &conf->tg; + int tries; + + /* setting core registers */ + hdmi_reg_writeb(hdata, HDMI_H_BLANK_0, core->h_blank[0]); + hdmi_reg_writeb(hdata, HDMI_H_BLANK_1, core->h_blank[1]); + hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_0, core->v_blank[0]); + hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_1, core->v_blank[1]); + hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_2, core->v_blank[2]); + hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_0, core->h_v_line[0]); + hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_1, core->h_v_line[1]); + hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_2, core->h_v_line[2]); + hdmi_reg_writeb(hdata, HDMI_VSYNC_POL, core->vsync_pol[0]); + hdmi_reg_writeb(hdata, HDMI_INT_PRO_MODE, core->int_pro_mode[0]); + hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_0, core->v_blank_f[0]); + hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_1, core->v_blank_f[1]); + hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_2, core->v_blank_f[2]); + hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_0, core->h_sync_gen[0]); + hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_1, core->h_sync_gen[1]); + hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_2, core->h_sync_gen[2]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_0, core->v_sync_gen1[0]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_1, core->v_sync_gen1[1]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_2, core->v_sync_gen1[2]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_0, core->v_sync_gen2[0]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_1, core->v_sync_gen2[1]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_2, core->v_sync_gen2[2]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_0, core->v_sync_gen3[0]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_1, core->v_sync_gen3[1]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_2, core->v_sync_gen3[2]); + /* Timing generator registers */ + hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz_l); + hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz_h); + hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_L, tg->hact_st_l); + hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_H, tg->hact_st_h); + hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_L, tg->hact_sz_l); + hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_H, tg->hact_sz_h); + hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_L, tg->v_fsz_l); + hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_H, tg->v_fsz_h); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_L, tg->vsync_l); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_H, tg->vsync_h); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_L, tg->vsync2_l); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_H, tg->vsync2_h); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_L, tg->vact_st_l); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_H, tg->vact_st_h); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_L, tg->vact_sz_l); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_H, tg->vact_sz_h); + hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_L, tg->field_chg_l); + hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg_h); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2_l); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2_h); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi_h); + hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi_l); + hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h); + hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l); + hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h); + + /* waiting for HDMIPHY's PLL to get to steady state */ + for (tries = 100; tries; --tries) { + u32 val = hdmi_reg_read(hdata, HDMI_V13_PHY_STATUS); + if (val & HDMI_PHY_STATUS_READY) + break; + mdelay(1); + } + /* steady state not achieved */ + if (tries == 0) { + DRM_ERROR("hdmiphy's pll could not reach steady state.\n"); + hdmi_regs_dump(hdata, "timing apply"); + } + + clk_disable(hdata->res.sclk_hdmi); + clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_hdmiphy); + clk_enable(hdata->res.sclk_hdmi); + + /* enable HDMI and timing generator */ + hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN); + if (core->int_pro_mode[0]) + hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN | + HDMI_FIELD_EN); + else + hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN); +} + +static void hdmi_v14_timing_apply(struct hdmi_context *hdata) +{ + const struct hdmi_preset_conf *conf = hdmi_confs[hdata->cur_conf].conf; const struct hdmi_core_regs *core = &conf->core; const struct hdmi_tg_regs *tg = &conf->tg; int tries; @@ -574,29 +1433,102 @@ static void hdmi_timing_apply(struct hdmi_context *hdata, /* setting core registers */ hdmi_reg_writeb(hdata, HDMI_H_BLANK_0, core->h_blank[0]); hdmi_reg_writeb(hdata, HDMI_H_BLANK_1, core->h_blank[1]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_0, core->v_blank[0]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_1, core->v_blank[1]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_2, core->v_blank[2]); - hdmi_reg_writeb(hdata, HDMI_H_V_LINE_0, core->h_v_line[0]); - hdmi_reg_writeb(hdata, HDMI_H_V_LINE_1, core->h_v_line[1]); - hdmi_reg_writeb(hdata, HDMI_H_V_LINE_2, core->h_v_line[2]); + hdmi_reg_writeb(hdata, HDMI_V2_BLANK_0, core->v2_blank[0]); + hdmi_reg_writeb(hdata, HDMI_V2_BLANK_1, core->v2_blank[1]); + hdmi_reg_writeb(hdata, HDMI_V1_BLANK_0, core->v1_blank[0]); + hdmi_reg_writeb(hdata, HDMI_V1_BLANK_1, core->v1_blank[1]); + hdmi_reg_writeb(hdata, HDMI_V_LINE_0, core->v_line[0]); + hdmi_reg_writeb(hdata, HDMI_V_LINE_1, core->v_line[1]); + hdmi_reg_writeb(hdata, HDMI_H_LINE_0, core->h_line[0]); + hdmi_reg_writeb(hdata, HDMI_H_LINE_1, core->h_line[1]); + hdmi_reg_writeb(hdata, HDMI_HSYNC_POL, core->hsync_pol[0]); hdmi_reg_writeb(hdata, HDMI_VSYNC_POL, core->vsync_pol[0]); hdmi_reg_writeb(hdata, HDMI_INT_PRO_MODE, core->int_pro_mode[0]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_0, core->v_blank_f[0]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_1, core->v_blank_f[1]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_2, core->v_blank_f[2]); - hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_0, core->h_sync_gen[0]); - hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_1, core->h_sync_gen[1]); - hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_2, core->h_sync_gen[2]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_0, core->v_sync_gen1[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_1, core->v_sync_gen1[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_2, core->v_sync_gen1[2]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_0, core->v_sync_gen2[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_1, core->v_sync_gen2[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_2, core->v_sync_gen2[2]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_0, core->v_sync_gen3[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_1, core->v_sync_gen3[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_2, core->v_sync_gen3[2]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F0_0, core->v_blank_f0[0]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F0_1, core->v_blank_f0[1]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F1_0, core->v_blank_f1[0]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F1_1, core->v_blank_f1[1]); + hdmi_reg_writeb(hdata, HDMI_H_SYNC_START_0, core->h_sync_start[0]); + hdmi_reg_writeb(hdata, HDMI_H_SYNC_START_1, core->h_sync_start[1]); + hdmi_reg_writeb(hdata, HDMI_H_SYNC_END_0, core->h_sync_end[0]); + hdmi_reg_writeb(hdata, HDMI_H_SYNC_END_1, core->h_sync_end[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_2_0, + core->v_sync_line_bef_2[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_2_1, + core->v_sync_line_bef_2[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_1_0, + core->v_sync_line_bef_1[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_1_1, + core->v_sync_line_bef_1[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_2_0, + core->v_sync_line_aft_2[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_2_1, + core->v_sync_line_aft_2[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_1_0, + core->v_sync_line_aft_1[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_1_1, + core->v_sync_line_aft_1[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, + core->v_sync_line_aft_pxl_2[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_1, + core->v_sync_line_aft_pxl_2[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, + core->v_sync_line_aft_pxl_1[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_1, + core->v_sync_line_aft_pxl_1[1]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F2_0, core->v_blank_f2[0]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F2_1, core->v_blank_f2[1]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F3_0, core->v_blank_f3[0]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F3_1, core->v_blank_f3[1]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F4_0, core->v_blank_f4[0]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F4_1, core->v_blank_f4[1]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F5_0, core->v_blank_f5[0]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F5_1, core->v_blank_f5[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_3_0, + core->v_sync_line_aft_3[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_3_1, + core->v_sync_line_aft_3[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_4_0, + core->v_sync_line_aft_4[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_4_1, + core->v_sync_line_aft_4[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_5_0, + core->v_sync_line_aft_5[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_5_1, + core->v_sync_line_aft_5[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_6_0, + core->v_sync_line_aft_6[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_6_1, + core->v_sync_line_aft_6[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0, + core->v_sync_line_aft_pxl_3[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_1, + core->v_sync_line_aft_pxl_3[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0, + core->v_sync_line_aft_pxl_4[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_1, + core->v_sync_line_aft_pxl_4[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0, + core->v_sync_line_aft_pxl_5[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_1, + core->v_sync_line_aft_pxl_5[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, + core->v_sync_line_aft_pxl_6[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_1, + core->v_sync_line_aft_pxl_6[1]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_1_0, core->vact_space_1[0]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_1_1, core->vact_space_1[1]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_2_0, core->vact_space_2[0]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_2_1, core->vact_space_2[1]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_3_0, core->vact_space_3[0]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_3_1, core->vact_space_3[1]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_4_0, core->vact_space_4[0]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_4_1, core->vact_space_4[1]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_5_0, core->vact_space_5[0]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_5_1, core->vact_space_5[1]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_6_0, core->vact_space_6[0]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_6_1, core->vact_space_6[1]); + /* Timing generator registers */ hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz_l); hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz_h); @@ -618,6 +1550,10 @@ static void hdmi_timing_apply(struct hdmi_context *hdata, hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg_h); hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2_l); hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2_h); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST3_L, tg->vact_st3_l); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST3_H, tg->vact_st3_h); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST4_L, tg->vact_st4_l); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST4_H, tg->vact_st4_h); hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l); hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h); hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l); @@ -626,10 +1562,11 @@ static void hdmi_timing_apply(struct hdmi_context *hdata, hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h); hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l); hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h); + hdmi_reg_writeb(hdata, HDMI_TG_3D, tg->tg_3d);
/* waiting for HDMIPHY's PLL to get to steady state */ for (tries = 100; tries; --tries) { - u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS); + u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS_0); if (val & HDMI_PHY_STATUS_READY) break; mdelay(1); @@ -653,9 +1590,18 @@ static void hdmi_timing_apply(struct hdmi_context *hdata, hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN); }
+static void hdmi_timing_apply(struct hdmi_context *hdata) +{ + if (hdata->is_v13) + hdmi_v13_timing_apply(hdata); + else + hdmi_v14_timing_apply(hdata); +} + static void hdmiphy_conf_reset(struct hdmi_context *hdata) { u8 buffer[2]; + u32 reg;
clk_disable(hdata->res.sclk_hdmi); clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_pixel); @@ -668,15 +1614,21 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata) if (hdata->hdmiphy_port) i2c_master_send(hdata->hdmiphy_port, buffer, 2);
+ if (hdata->is_v13) + reg = HDMI_V13_PHY_RSTOUT; + else + reg = HDMI_PHY_RSTOUT; + /* reset hdmiphy */ - hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT); + hdmi_reg_writemask(hdata, reg, ~0, HDMI_PHY_SW_RSTOUT); mdelay(10); - hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT); + hdmi_reg_writemask(hdata, reg, 0, HDMI_PHY_SW_RSTOUT); mdelay(10); }
static void hdmiphy_conf_apply(struct hdmi_context *hdata) { + const u8 *hdmiphy_data; u8 buffer[32]; u8 operation[2]; u8 read_buffer[32] = {0, }; @@ -689,7 +1641,12 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata) }
/* pixel clock */ - memcpy(buffer, hdmi_confs[hdata->cur_conf].hdmiphy_data, 32); + if (hdata->is_v13) + hdmiphy_data = hdmi_v13_confs[hdata->cur_conf].hdmiphy_data; + else + hdmiphy_data = hdmi_confs[hdata->cur_conf].hdmiphy_data; + + memcpy(buffer, hdmiphy_data, 32); ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32); if (ret != 32) { DRM_ERROR("failed to configure HDMIPHY via I2C\n"); @@ -721,9 +1678,6 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata)
static void hdmi_conf_apply(struct hdmi_context *hdata) { - const struct hdmi_preset_conf *conf = - hdmi_confs[hdata->cur_conf].conf; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
hdmiphy_conf_reset(hdata); @@ -733,7 +1687,7 @@ static void hdmi_conf_apply(struct hdmi_context *hdata) hdmi_conf_init(hdata);
/* setting core registers */ - hdmi_timing_apply(hdata, conf); + hdmi_timing_apply(hdata);
hdmi_regs_dump(hdata, "start"); } @@ -745,8 +1699,8 @@ static void hdmi_mode_set(void *ctx, void *mode)
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
- conf_idx = hdmi_conf_index(mode); - if (conf_idx >= 0 && conf_idx < ARRAY_SIZE(hdmi_confs)) + conf_idx = hdmi_conf_index(hdata, mode); + if (conf_idx >= 0) hdata->cur_conf = conf_idx; else DRM_DEBUG_KMS("not supported mode\n"); @@ -926,7 +1880,6 @@ static void hdmi_resource_poweron(struct hdmi_context *hdata) hdmiphy_conf_reset(hdata); hdmi_conf_reset(hdata); hdmi_conf_init(hdata); - }
static void hdmi_resource_poweroff(struct hdmi_context *hdata) @@ -1022,6 +1975,7 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, drm_hdmi_ctx);
+ hdata->is_v13 = pdata->is_v13; hdata->default_win = pdata->default_win; hdata->default_timing = &pdata->timing; hdata->default_bpp = pdata->bpp; diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.h b/drivers/gpu/drm/exynos/exynos_hdmi.h index 31d6cf8..040ecad 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.h +++ b/drivers/gpu/drm/exynos/exynos_hdmi.h @@ -28,15 +28,6 @@ #ifndef _EXYNOS_HDMI_H_ #define _EXYNOS_HDMI_H_
-struct hdmi_conf { - int width; - int height; - int vrefresh; - bool interlace; - const u8 *hdmiphy_data; - const struct hdmi_preset_conf *conf; -}; - struct hdmi_resources { struct clk *hdmi; struct clk *sclk_hdmi; @@ -51,6 +42,7 @@ struct hdmi_context { struct device *dev; struct drm_device *drm_dev; struct fb_videomode *default_timing; + unsigned int is_v13:1; unsigned int default_win; unsigned int default_bpp; bool hpd_handle; diff --git a/drivers/gpu/drm/exynos/regs-hdmi.h b/drivers/gpu/drm/exynos/regs-hdmi.h index 72e6b52..6b28715 100644 --- a/drivers/gpu/drm/exynos/regs-hdmi.h +++ b/drivers/gpu/drm/exynos/regs-hdmi.h @@ -19,6 +19,7 @@ * Register part */
+/* HDMI Version 1.3 & Common */ #define HDMI_CTRL_BASE(x) ((x) + 0x00000000) #define HDMI_CORE_BASE(x) ((x) + 0x00010000) #define HDMI_TG_BASE(x) ((x) + 0x00050000) @@ -27,56 +28,57 @@ #define HDMI_INTC_CON HDMI_CTRL_BASE(0x0000) #define HDMI_INTC_FLAG HDMI_CTRL_BASE(0x0004) #define HDMI_HPD_STATUS HDMI_CTRL_BASE(0x000C) -#define HDMI_PHY_RSTOUT HDMI_CTRL_BASE(0x0014) -#define HDMI_PHY_VPLL HDMI_CTRL_BASE(0x0018) -#define HDMI_PHY_CMU HDMI_CTRL_BASE(0x001C) -#define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0020) +#define HDMI_V13_PHY_RSTOUT HDMI_CTRL_BASE(0x0014) +#define HDMI_V13_PHY_VPLL HDMI_CTRL_BASE(0x0018) +#define HDMI_V13_PHY_CMU HDMI_CTRL_BASE(0x001C) +#define HDMI_V13_CORE_RSTOUT HDMI_CTRL_BASE(0x0020)
/* Core registers */ #define HDMI_CON_0 HDMI_CORE_BASE(0x0000) #define HDMI_CON_1 HDMI_CORE_BASE(0x0004) #define HDMI_CON_2 HDMI_CORE_BASE(0x0008) #define HDMI_SYS_STATUS HDMI_CORE_BASE(0x0010) -#define HDMI_PHY_STATUS HDMI_CORE_BASE(0x0014) +#define HDMI_V13_PHY_STATUS HDMI_CORE_BASE(0x0014) #define HDMI_STATUS_EN HDMI_CORE_BASE(0x0020) #define HDMI_HPD HDMI_CORE_BASE(0x0030) #define HDMI_MODE_SEL HDMI_CORE_BASE(0x0040) -#define HDMI_BLUE_SCREEN_0 HDMI_CORE_BASE(0x0050) -#define HDMI_BLUE_SCREEN_1 HDMI_CORE_BASE(0x0054) -#define HDMI_BLUE_SCREEN_2 HDMI_CORE_BASE(0x0058) +#define HDMI_ENC_EN HDMI_CORE_BASE(0x0044) +#define HDMI_V13_BLUE_SCREEN_0 HDMI_CORE_BASE(0x0050) +#define HDMI_V13_BLUE_SCREEN_1 HDMI_CORE_BASE(0x0054) +#define HDMI_V13_BLUE_SCREEN_2 HDMI_CORE_BASE(0x0058) #define HDMI_H_BLANK_0 HDMI_CORE_BASE(0x00A0) #define HDMI_H_BLANK_1 HDMI_CORE_BASE(0x00A4) -#define HDMI_V_BLANK_0 HDMI_CORE_BASE(0x00B0) -#define HDMI_V_BLANK_1 HDMI_CORE_BASE(0x00B4) -#define HDMI_V_BLANK_2 HDMI_CORE_BASE(0x00B8) -#define HDMI_H_V_LINE_0 HDMI_CORE_BASE(0x00C0) -#define HDMI_H_V_LINE_1 HDMI_CORE_BASE(0x00C4) -#define HDMI_H_V_LINE_2 HDMI_CORE_BASE(0x00C8) +#define HDMI_V13_V_BLANK_0 HDMI_CORE_BASE(0x00B0) +#define HDMI_V13_V_BLANK_1 HDMI_CORE_BASE(0x00B4) +#define HDMI_V13_V_BLANK_2 HDMI_CORE_BASE(0x00B8) +#define HDMI_V13_H_V_LINE_0 HDMI_CORE_BASE(0x00C0) +#define HDMI_V13_H_V_LINE_1 HDMI_CORE_BASE(0x00C4) +#define HDMI_V13_H_V_LINE_2 HDMI_CORE_BASE(0x00C8) #define HDMI_VSYNC_POL HDMI_CORE_BASE(0x00E4) #define HDMI_INT_PRO_MODE HDMI_CORE_BASE(0x00E8) -#define HDMI_V_BLANK_F_0 HDMI_CORE_BASE(0x0110) -#define HDMI_V_BLANK_F_1 HDMI_CORE_BASE(0x0114) -#define HDMI_V_BLANK_F_2 HDMI_CORE_BASE(0x0118) -#define HDMI_H_SYNC_GEN_0 HDMI_CORE_BASE(0x0120) -#define HDMI_H_SYNC_GEN_1 HDMI_CORE_BASE(0x0124) -#define HDMI_H_SYNC_GEN_2 HDMI_CORE_BASE(0x0128) -#define HDMI_V_SYNC_GEN_1_0 HDMI_CORE_BASE(0x0130) -#define HDMI_V_SYNC_GEN_1_1 HDMI_CORE_BASE(0x0134) -#define HDMI_V_SYNC_GEN_1_2 HDMI_CORE_BASE(0x0138) -#define HDMI_V_SYNC_GEN_2_0 HDMI_CORE_BASE(0x0140) -#define HDMI_V_SYNC_GEN_2_1 HDMI_CORE_BASE(0x0144) -#define HDMI_V_SYNC_GEN_2_2 HDMI_CORE_BASE(0x0148) -#define HDMI_V_SYNC_GEN_3_0 HDMI_CORE_BASE(0x0150) -#define HDMI_V_SYNC_GEN_3_1 HDMI_CORE_BASE(0x0154) -#define HDMI_V_SYNC_GEN_3_2 HDMI_CORE_BASE(0x0158) -#define HDMI_ACR_CON HDMI_CORE_BASE(0x0180) -#define HDMI_AVI_CON HDMI_CORE_BASE(0x0300) -#define HDMI_AVI_BYTE(n) HDMI_CORE_BASE(0x0320 + 4 * (n)) -#define HDMI_DC_CONTROL HDMI_CORE_BASE(0x05C0) -#define HDMI_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x05C4) -#define HDMI_HPD_GEN HDMI_CORE_BASE(0x05C8) -#define HDMI_AUI_CON HDMI_CORE_BASE(0x0360) -#define HDMI_SPD_CON HDMI_CORE_BASE(0x0400) +#define HDMI_V13_V_BLANK_F_0 HDMI_CORE_BASE(0x0110) +#define HDMI_V13_V_BLANK_F_1 HDMI_CORE_BASE(0x0114) +#define HDMI_V13_V_BLANK_F_2 HDMI_CORE_BASE(0x0118) +#define HDMI_V13_H_SYNC_GEN_0 HDMI_CORE_BASE(0x0120) +#define HDMI_V13_H_SYNC_GEN_1 HDMI_CORE_BASE(0x0124) +#define HDMI_V13_H_SYNC_GEN_2 HDMI_CORE_BASE(0x0128) +#define HDMI_V13_V_SYNC_GEN_1_0 HDMI_CORE_BASE(0x0130) +#define HDMI_V13_V_SYNC_GEN_1_1 HDMI_CORE_BASE(0x0134) +#define HDMI_V13_V_SYNC_GEN_1_2 HDMI_CORE_BASE(0x0138) +#define HDMI_V13_V_SYNC_GEN_2_0 HDMI_CORE_BASE(0x0140) +#define HDMI_V13_V_SYNC_GEN_2_1 HDMI_CORE_BASE(0x0144) +#define HDMI_V13_V_SYNC_GEN_2_2 HDMI_CORE_BASE(0x0148) +#define HDMI_V13_V_SYNC_GEN_3_0 HDMI_CORE_BASE(0x0150) +#define HDMI_V13_V_SYNC_GEN_3_1 HDMI_CORE_BASE(0x0154) +#define HDMI_V13_V_SYNC_GEN_3_2 HDMI_CORE_BASE(0x0158) +#define HDMI_V13_ACR_CON HDMI_CORE_BASE(0x0180) +#define HDMI_V13_AVI_CON HDMI_CORE_BASE(0x0300) +#define HDMI_V13_AVI_BYTE(n) HDMI_CORE_BASE(0x0320 + 4 * (n)) +#define HDMI_V13_DC_CONTROL HDMI_CORE_BASE(0x05C0) +#define HDMI_V13_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x05C4) +#define HDMI_V13_HPD_GEN HDMI_CORE_BASE(0x05C8) +#define HDMI_V13_AUI_CON HDMI_CORE_BASE(0x0360) +#define HDMI_V13_SPD_CON HDMI_CORE_BASE(0x0400)
/* Timing generator registers */ #define HDMI_TG_CMD HDMI_TG_BASE(0x0000) @@ -144,4 +146,234 @@ #define HDMI_TG_EN (1 << 0) #define HDMI_FIELD_EN (1 << 1)
+ +/* HDMI Version 1.4 */ +/* Control registers */ +/* #define HDMI_INTC_CON HDMI_CTRL_BASE(0x0000) */ +/* #define HDMI_INTC_FLAG HDMI_CTRL_BASE(0x0004) */ +#define HDMI_HDCP_KEY_LOAD HDMI_CTRL_BASE(0x0008) +/* #define HDMI_HPD_STATUS HDMI_CTRL_BASE(0x000C) */ +#define HDMI_INTC_CON_1 HDMI_CTRL_BASE(0x0010) +#define HDMI_INTC_FLAG_1 HDMI_CTRL_BASE(0x0014) +#define HDMI_PHY_STATUS_0 HDMI_CTRL_BASE(0x0020) +#define HDMI_PHY_STATUS_CMU HDMI_CTRL_BASE(0x0024) +#define HDMI_PHY_STATUS_PLL HDMI_CTRL_BASE(0x0028) +#define HDMI_PHY_CON_0 HDMI_CTRL_BASE(0x0030) +#define HDMI_HPD_CTRL HDMI_CTRL_BASE(0x0040) +#define HDMI_HPD_ST HDMI_CTRL_BASE(0x0044) +#define HDMI_HPD_TH_X HDMI_CTRL_BASE(0x0050) +#define HDMI_AUDIO_CLKSEL HDMI_CTRL_BASE(0x0070) +#define HDMI_PHY_RSTOUT HDMI_CTRL_BASE(0x0074) +#define HDMI_PHY_VPLL HDMI_CTRL_BASE(0x0078) +#define HDMI_PHY_CMU HDMI_CTRL_BASE(0x007C) +#define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0080) + +/* Video related registers */ +#define HDMI_YMAX HDMI_CORE_BASE(0x0060) +#define HDMI_YMIN HDMI_CORE_BASE(0x0064) +#define HDMI_CMAX HDMI_CORE_BASE(0x0068) +#define HDMI_CMIN HDMI_CORE_BASE(0x006C) + +#define HDMI_V2_BLANK_0 HDMI_CORE_BASE(0x00B0) +#define HDMI_V2_BLANK_1 HDMI_CORE_BASE(0x00B4) +#define HDMI_V1_BLANK_0 HDMI_CORE_BASE(0x00B8) +#define HDMI_V1_BLANK_1 HDMI_CORE_BASE(0x00BC) + +#define HDMI_V_LINE_0 HDMI_CORE_BASE(0x00C0) +#define HDMI_V_LINE_1 HDMI_CORE_BASE(0x00C4) +#define HDMI_H_LINE_0 HDMI_CORE_BASE(0x00C8) +#define HDMI_H_LINE_1 HDMI_CORE_BASE(0x00CC) + +#define HDMI_HSYNC_POL HDMI_CORE_BASE(0x00E0) + +#define HDMI_V_BLANK_F0_0 HDMI_CORE_BASE(0x0110) +#define HDMI_V_BLANK_F0_1 HDMI_CORE_BASE(0x0114) +#define HDMI_V_BLANK_F1_0 HDMI_CORE_BASE(0x0118) +#define HDMI_V_BLANK_F1_1 HDMI_CORE_BASE(0x011C) + +#define HDMI_H_SYNC_START_0 HDMI_CORE_BASE(0x0120) +#define HDMI_H_SYNC_START_1 HDMI_CORE_BASE(0x0124) +#define HDMI_H_SYNC_END_0 HDMI_CORE_BASE(0x0128) +#define HDMI_H_SYNC_END_1 HDMI_CORE_BASE(0x012C) + +#define HDMI_V_SYNC_LINE_BEF_2_0 HDMI_CORE_BASE(0x0130) +#define HDMI_V_SYNC_LINE_BEF_2_1 HDMI_CORE_BASE(0x0134) +#define HDMI_V_SYNC_LINE_BEF_1_0 HDMI_CORE_BASE(0x0138) +#define HDMI_V_SYNC_LINE_BEF_1_1 HDMI_CORE_BASE(0x013C) + +#define HDMI_V_SYNC_LINE_AFT_2_0 HDMI_CORE_BASE(0x0140) +#define HDMI_V_SYNC_LINE_AFT_2_1 HDMI_CORE_BASE(0x0144) +#define HDMI_V_SYNC_LINE_AFT_1_0 HDMI_CORE_BASE(0x0148) +#define HDMI_V_SYNC_LINE_AFT_1_1 HDMI_CORE_BASE(0x014C) + +#define HDMI_V_SYNC_LINE_AFT_PXL_2_0 HDMI_CORE_BASE(0x0150) +#define HDMI_V_SYNC_LINE_AFT_PXL_2_1 HDMI_CORE_BASE(0x0154) +#define HDMI_V_SYNC_LINE_AFT_PXL_1_0 HDMI_CORE_BASE(0x0158) +#define HDMI_V_SYNC_LINE_AFT_PXL_1_1 HDMI_CORE_BASE(0x015C) + +#define HDMI_V_BLANK_F2_0 HDMI_CORE_BASE(0x0160) +#define HDMI_V_BLANK_F2_1 HDMI_CORE_BASE(0x0164) +#define HDMI_V_BLANK_F3_0 HDMI_CORE_BASE(0x0168) +#define HDMI_V_BLANK_F3_1 HDMI_CORE_BASE(0x016C) +#define HDMI_V_BLANK_F4_0 HDMI_CORE_BASE(0x0170) +#define HDMI_V_BLANK_F4_1 HDMI_CORE_BASE(0x0174) +#define HDMI_V_BLANK_F5_0 HDMI_CORE_BASE(0x0178) +#define HDMI_V_BLANK_F5_1 HDMI_CORE_BASE(0x017C) + +#define HDMI_V_SYNC_LINE_AFT_3_0 HDMI_CORE_BASE(0x0180) +#define HDMI_V_SYNC_LINE_AFT_3_1 HDMI_CORE_BASE(0x0184) +#define HDMI_V_SYNC_LINE_AFT_4_0 HDMI_CORE_BASE(0x0188) +#define HDMI_V_SYNC_LINE_AFT_4_1 HDMI_CORE_BASE(0x018C) +#define HDMI_V_SYNC_LINE_AFT_5_0 HDMI_CORE_BASE(0x0190) +#define HDMI_V_SYNC_LINE_AFT_5_1 HDMI_CORE_BASE(0x0194) +#define HDMI_V_SYNC_LINE_AFT_6_0 HDMI_CORE_BASE(0x0198) +#define HDMI_V_SYNC_LINE_AFT_6_1 HDMI_CORE_BASE(0x019C) + +#define HDMI_V_SYNC_LINE_AFT_PXL_3_0 HDMI_CORE_BASE(0x01A0) +#define HDMI_V_SYNC_LINE_AFT_PXL_3_1 HDMI_CORE_BASE(0x01A4) +#define HDMI_V_SYNC_LINE_AFT_PXL_4_0 HDMI_CORE_BASE(0x01A8) +#define HDMI_V_SYNC_LINE_AFT_PXL_4_1 HDMI_CORE_BASE(0x01AC) +#define HDMI_V_SYNC_LINE_AFT_PXL_5_0 HDMI_CORE_BASE(0x01B0) +#define HDMI_V_SYNC_LINE_AFT_PXL_5_1 HDMI_CORE_BASE(0x01B4) +#define HDMI_V_SYNC_LINE_AFT_PXL_6_0 HDMI_CORE_BASE(0x01B8) +#define HDMI_V_SYNC_LINE_AFT_PXL_6_1 HDMI_CORE_BASE(0x01BC) + +#define HDMI_VACT_SPACE_1_0 HDMI_CORE_BASE(0x01C0) +#define HDMI_VACT_SPACE_1_1 HDMI_CORE_BASE(0x01C4) +#define HDMI_VACT_SPACE_2_0 HDMI_CORE_BASE(0x01C8) +#define HDMI_VACT_SPACE_2_1 HDMI_CORE_BASE(0x01CC) +#define HDMI_VACT_SPACE_3_0 HDMI_CORE_BASE(0x01D0) +#define HDMI_VACT_SPACE_3_1 HDMI_CORE_BASE(0x01D4) +#define HDMI_VACT_SPACE_4_0 HDMI_CORE_BASE(0x01D8) +#define HDMI_VACT_SPACE_4_1 HDMI_CORE_BASE(0x01DC) +#define HDMI_VACT_SPACE_5_0 HDMI_CORE_BASE(0x01E0) +#define HDMI_VACT_SPACE_5_1 HDMI_CORE_BASE(0x01E4) +#define HDMI_VACT_SPACE_6_0 HDMI_CORE_BASE(0x01E8) +#define HDMI_VACT_SPACE_6_1 HDMI_CORE_BASE(0x01EC) + +#define HDMI_GCP_CON HDMI_CORE_BASE(0x0200) +#define HDMI_GCP_BYTE1 HDMI_CORE_BASE(0x0210) +#define HDMI_GCP_BYTE2 HDMI_CORE_BASE(0x0214) +#define HDMI_GCP_BYTE3 HDMI_CORE_BASE(0x0218) + +/* Audio related registers */ +#define HDMI_ASP_CON HDMI_CORE_BASE(0x0300) +#define HDMI_ASP_SP_FLAT HDMI_CORE_BASE(0x0304) +#define HDMI_ASP_CHCFG0 HDMI_CORE_BASE(0x0310) +#define HDMI_ASP_CHCFG1 HDMI_CORE_BASE(0x0314) +#define HDMI_ASP_CHCFG2 HDMI_CORE_BASE(0x0318) +#define HDMI_ASP_CHCFG3 HDMI_CORE_BASE(0x031C) + +#define HDMI_ACR_CON HDMI_CORE_BASE(0x0400) +#define HDMI_ACR_MCTS0 HDMI_CORE_BASE(0x0410) +#define HDMI_ACR_MCTS1 HDMI_CORE_BASE(0x0414) +#define HDMI_ACR_MCTS2 HDMI_CORE_BASE(0x0418) +#define HDMI_ACR_N0 HDMI_CORE_BASE(0x0430) +#define HDMI_ACR_N1 HDMI_CORE_BASE(0x0434) +#define HDMI_ACR_N2 HDMI_CORE_BASE(0x0438) + +/* Packet related registers */ +#define HDMI_ACP_CON HDMI_CORE_BASE(0x0500) +#define HDMI_ACP_TYPE HDMI_CORE_BASE(0x0514) +#define HDMI_ACP_DATA(n) HDMI_CORE_BASE(0x0520 + 4 * (n)) + +#define HDMI_ISRC_CON HDMI_CORE_BASE(0x0600) +#define HDMI_ISRC1_HEADER1 HDMI_CORE_BASE(0x0614) +#define HDMI_ISRC1_DATA(n) HDMI_CORE_BASE(0x0620 + 4 * (n)) +#define HDMI_ISRC2_DATA(n) HDMI_CORE_BASE(0x06A0 + 4 * (n)) + +#define HDMI_AVI_CON HDMI_CORE_BASE(0x0700) +#define HDMI_AVI_HEADER0 HDMI_CORE_BASE(0x0710) +#define HDMI_AVI_HEADER1 HDMI_CORE_BASE(0x0714) +#define HDMI_AVI_HEADER2 HDMI_CORE_BASE(0x0718) +#define HDMI_AVI_CHECK_SUM HDMI_CORE_BASE(0x071C) +#define HDMI_AVI_BYTE(n) HDMI_CORE_BASE(0x0720 + 4 * (n)) + +#define HDMI_AUI_CON HDMI_CORE_BASE(0x0800) +#define HDMI_AUI_HEADER0 HDMI_CORE_BASE(0x0810) +#define HDMI_AUI_HEADER1 HDMI_CORE_BASE(0x0814) +#define HDMI_AUI_HEADER2 HDMI_CORE_BASE(0x0818) +#define HDMI_AUI_CHECK_SUM HDMI_CORE_BASE(0x081C) +#define HDMI_AUI_BYTE(n) HDMI_CORE_BASE(0x0820 + 4 * (n)) + +#define HDMI_MPG_CON HDMI_CORE_BASE(0x0900) +#define HDMI_MPG_CHECK_SUM HDMI_CORE_BASE(0x091C) +#define HDMI_MPG_DATA(n) HDMI_CORE_BASE(0x0920 + 4 * (n)) + +#define HDMI_SPD_CON HDMI_CORE_BASE(0x0A00) +#define HDMI_SPD_HEADER0 HDMI_CORE_BASE(0x0A10) +#define HDMI_SPD_HEADER1 HDMI_CORE_BASE(0x0A14) +#define HDMI_SPD_HEADER2 HDMI_CORE_BASE(0x0A18) +#define HDMI_SPD_DATA(n) HDMI_CORE_BASE(0x0A20 + 4 * (n)) + +#define HDMI_GAMUT_CON HDMI_CORE_BASE(0x0B00) +#define HDMI_GAMUT_HEADER0 HDMI_CORE_BASE(0x0B10) +#define HDMI_GAMUT_HEADER1 HDMI_CORE_BASE(0x0B14) +#define HDMI_GAMUT_HEADER2 HDMI_CORE_BASE(0x0B18) +#define HDMI_GAMUT_METADATA(n) HDMI_CORE_BASE(0x0B20 + 4 * (n)) + +#define HDMI_VSI_CON HDMI_CORE_BASE(0x0C00) +#define HDMI_VSI_HEADER0 HDMI_CORE_BASE(0x0C10) +#define HDMI_VSI_HEADER1 HDMI_CORE_BASE(0x0C14) +#define HDMI_VSI_HEADER2 HDMI_CORE_BASE(0x0C18) +#define HDMI_VSI_DATA(n) HDMI_CORE_BASE(0x0C20 + 4 * (n)) + +#define HDMI_DC_CONTROL HDMI_CORE_BASE(0x0D00) +#define HDMI_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x0D04) + +#define HDMI_AN_SEED_SEL HDMI_CORE_BASE(0x0E48) +#define HDMI_AN_SEED_0 HDMI_CORE_BASE(0x0E58) +#define HDMI_AN_SEED_1 HDMI_CORE_BASE(0x0E5C) +#define HDMI_AN_SEED_2 HDMI_CORE_BASE(0x0E60) +#define HDMI_AN_SEED_3 HDMI_CORE_BASE(0x0E64) + +/* HDCP related registers */ +#define HDMI_HDCP_SHA1(n) HDMI_CORE_BASE(0x7000 + 4 * (n)) +#define HDMI_HDCP_KSV_LIST(n) HDMI_CORE_BASE(0x7050 + 4 * (n)) + +#define HDMI_HDCP_KSV_LIST_CON HDMI_CORE_BASE(0x7064) +#define HDMI_HDCP_SHA_RESULT HDMI_CORE_BASE(0x7070) +#define HDMI_HDCP_CTRL1 HDMI_CORE_BASE(0x7080) +#define HDMI_HDCP_CTRL2 HDMI_CORE_BASE(0x7084) +#define HDMI_HDCP_CHECK_RESULT HDMI_CORE_BASE(0x7090) +#define HDMI_HDCP_BKSV(n) HDMI_CORE_BASE(0x70A0 + 4 * (n)) +#define HDMI_HDCP_AKSV(n) HDMI_CORE_BASE(0x70C0 + 4 * (n)) +#define HDMI_HDCP_AN(n) HDMI_CORE_BASE(0x70E0 + 4 * (n)) + +#define HDMI_HDCP_BCAPS HDMI_CORE_BASE(0x7100) +#define HDMI_HDCP_BSTATUS_0 HDMI_CORE_BASE(0x7110) +#define HDMI_HDCP_BSTATUS_1 HDMI_CORE_BASE(0x7114) +#define HDMI_HDCP_RI_0 HDMI_CORE_BASE(0x7140) +#define HDMI_HDCP_RI_1 HDMI_CORE_BASE(0x7144) +#define HDMI_HDCP_I2C_INT HDMI_CORE_BASE(0x7180) +#define HDMI_HDCP_AN_INT HDMI_CORE_BASE(0x7190) +#define HDMI_HDCP_WDT_INT HDMI_CORE_BASE(0x71A0) +#define HDMI_HDCP_RI_INT HDMI_CORE_BASE(0x71B0) +#define HDMI_HDCP_RI_COMPARE_0 HDMI_CORE_BASE(0x71D0) +#define HDMI_HDCP_RI_COMPARE_1 HDMI_CORE_BASE(0x71D4) +#define HDMI_HDCP_FRAME_COUNT HDMI_CORE_BASE(0x71E0) + +#define HDMI_RGB_ROUND_EN HDMI_CORE_BASE(0xD500) +#define HDMI_VACT_SPACE_R_0 HDMI_CORE_BASE(0xD504) +#define HDMI_VACT_SPACE_R_1 HDMI_CORE_BASE(0xD508) +#define HDMI_VACT_SPACE_G_0 HDMI_CORE_BASE(0xD50C) +#define HDMI_VACT_SPACE_G_1 HDMI_CORE_BASE(0xD510) +#define HDMI_VACT_SPACE_B_0 HDMI_CORE_BASE(0xD514) +#define HDMI_VACT_SPACE_B_1 HDMI_CORE_BASE(0xD518) + +#define HDMI_BLUE_SCREEN_B_0 HDMI_CORE_BASE(0xD520) +#define HDMI_BLUE_SCREEN_B_1 HDMI_CORE_BASE(0xD524) +#define HDMI_BLUE_SCREEN_G_0 HDMI_CORE_BASE(0xD528) +#define HDMI_BLUE_SCREEN_G_1 HDMI_CORE_BASE(0xD52C) +#define HDMI_BLUE_SCREEN_R_0 HDMI_CORE_BASE(0xD530) +#define HDMI_BLUE_SCREEN_R_1 HDMI_CORE_BASE(0xD534) + +/* Timing generator registers */ +/* TG configure/status registers */ +#define HDMI_TG_VACT_ST3_L HDMI_TG_BASE(0x0068) +#define HDMI_TG_VACT_ST3_H HDMI_TG_BASE(0x006c) +#define HDMI_TG_VACT_ST4_L HDMI_TG_BASE(0x0070) +#define HDMI_TG_VACT_ST4_H HDMI_TG_BASE(0x0074) +#define HDMI_TG_3D HDMI_TG_BASE(0x00F0) + #endif /* SAMSUNG_REGS_HDMI_H */ diff --git a/include/drm/exynos_drm.h b/include/drm/exynos_drm.h index 1ed3aae..aff2313 100644 --- a/include/drm/exynos_drm.h +++ b/include/drm/exynos_drm.h @@ -147,11 +147,13 @@ struct exynos_drm_common_hdmi_pd { * @timing: default video mode for initializing * @default_win: default window layer number to be used for UI. * @bpp: default bit per pixel. + * @is_v13: set if hdmi version 13 is. */ struct exynos_drm_hdmi_pdata { struct fb_videomode timing; unsigned int default_win; unsigned int bpp; + unsigned int is_v13:1; };
#endif /* __KERNEL__ */
From: Joonyoung Shim jy0922.shim@samsung.com
G2D is a 2D graphic accelerator that supports Bit Block Transfer. This G2D driver is exynos drm specific.
This adds below three exynos specific ioctl and one event for G2D. - DRM_EXYNOS_G2D_GET_VER - DRM_EXYNOS_G2D_SET_CMDLIST - DRM_EXYNOS_G2D_EXEC - DRM_EXYNOS_G2D_EVENT
Signed-off-by: Joonyoung Shim jy0922.shim@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/Kconfig | 6 + drivers/gpu/drm/exynos/Makefile | 1 + drivers/gpu/drm/exynos/exynos_drm_drv.c | 31 ++ drivers/gpu/drm/exynos/exynos_drm_drv.h | 13 + drivers/gpu/drm/exynos/exynos_drm_g2d.c | 885 +++++++++++++++++++++++++++++++ drivers/gpu/drm/exynos/exynos_drm_g2d.h | 36 ++ include/drm/exynos_drm.h | 56 ++ 7 files changed, 1028 insertions(+), 0 deletions(-) create mode 100644 drivers/gpu/drm/exynos/exynos_drm_g2d.c create mode 100644 drivers/gpu/drm/exynos/exynos_drm_g2d.h
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 9a9850a..8493fe9 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -21,3 +21,9 @@ config DRM_EXYNOS_HDMI depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_TV help Choose this option if you want to use Exynos HDMI for DRM. + +config DRM_EXYNOS_G2D + bool "Exynos DRM G2D" + depends on DRM_EXYNOS + help + Choose this option if you want to use Exynos G2D for DRM. diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index 5331fa3..d6c1a3c 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile @@ -12,5 +12,6 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o \ exynos_ddc.o exynos_hdmiphy.o \ exynos_drm_hdmi.o +exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o
obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index b4e265f..2b72c5d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -37,6 +37,7 @@ #include "exynos_drm_fbdev.h" #include "exynos_drm_fb.h" #include "exynos_drm_gem.h" +#include "exynos_drm_g2d.h" #include "exynos_drm_plane.h"
#define DRIVER_NAME "exynos" @@ -146,8 +147,16 @@ static int exynos_drm_unload(struct drm_device *dev)
static int exynos_drm_open(struct drm_device *dev, struct drm_file *file) { + struct drm_exynos_file_private *file_priv; + DRM_DEBUG_DRIVER("%s\n", __FILE__);
+ file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL); + if (!file_priv) + return -ENOMEM; + + file->driver_priv = file_priv; + return exynos_drm_subdrv_open(dev, file); }
@@ -208,6 +217,13 @@ static struct drm_ioctl_desc exynos_ioctls[] = { exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH), DRM_IOCTL_DEF_DRV(EXYNOS_PLANE_SET_ZPOS, exynos_plane_set_zpos_ioctl, DRM_UNLOCKED | DRM_AUTH), + + DRM_IOCTL_DEF_DRV(EXYNOS_G2D_GET_VER, + exynos_g2d_get_ver_ioctl, DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(EXYNOS_G2D_SET_CMDLIST, + exynos_g2d_set_cmdlist_ioctl, DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, + exynos_g2d_exec_ioctl, DRM_UNLOCKED | DRM_AUTH), };
static const struct file_operations exynos_drm_driver_fops = { @@ -298,6 +314,12 @@ static int __init exynos_drm_init(void) goto out_common_hdmi; #endif
+#ifdef CONFIG_DRM_EXYNOS_G2D + ret = platform_driver_register(&g2d_driver); + if (ret < 0) + goto out_g2d; +#endif + ret = platform_driver_register(&exynos_drm_platform_driver); if (ret < 0) goto out; @@ -305,6 +327,11 @@ static int __init exynos_drm_init(void) return 0;
out: +#ifdef CONFIG_DRM_EXYNOS_G2D + platform_driver_unregister(&g2d_driver); +out_g2d: +#endif + #ifdef CONFIG_DRM_EXYNOS_HDMI platform_driver_unregister(&exynos_drm_common_hdmi_driver); out_common_hdmi: @@ -327,6 +354,10 @@ static void __exit exynos_drm_exit(void)
platform_driver_unregister(&exynos_drm_platform_driver);
+#ifdef CONFIG_DRM_EXYNOS_G2D + platform_driver_unregister(&g2d_driver); +#endif + #ifdef CONFIG_DRM_EXYNOS_HDMI platform_driver_unregister(&exynos_drm_common_hdmi_driver); platform_driver_unregister(&mixer_driver); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index b26c2f4..750daa9 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -203,6 +203,18 @@ struct exynos_drm_manager { struct exynos_drm_display_ops *display_ops; };
+struct exynos_drm_g2d_private { + struct device *dev; + struct list_head inuse_cmdlist; + struct list_head event_list; + struct list_head gem_list; + unsigned int gem_nr; +}; + +struct drm_exynos_file_private { + struct exynos_drm_g2d_private *g2d_priv; +}; + /* * Exynos drm private structure. */ @@ -284,4 +296,5 @@ extern struct platform_driver fimd_driver; extern struct platform_driver hdmi_driver; extern struct platform_driver mixer_driver; extern struct platform_driver exynos_drm_common_hdmi_driver; +extern struct platform_driver g2d_driver; #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c new file mode 100644 index 0000000..bad7d624 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -0,0 +1,885 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Authors: Joonyoung Shim jy0922.shim@samsung.com + * + * 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 Foundationr + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/workqueue.h> + +#include "drmP.h" +#include "exynos_drm.h" +#include "exynos_drm_drv.h" +#include "exynos_drm_gem.h" + +#define G2D_HW_MAJOR_VER 4 +#define G2D_HW_MINOR_VER 1 + +/* Registers */ +#define G2D_SOFT_RESET 0x0000 +#define G2D_INTEN 0x0004 +#define G2D_INTC_PEND 0x000C +#define G2D_DMA_SFR_BASE_ADDR 0x0080 +#define G2D_DMA_COMMAND 0x0084 +#define G2D_DMA_STATUS 0x008C +#define G2D_DMA_HOLD_CMD 0x0090 +#define G2D_BITBLT_START 0x0100 +#define G2D_SRC_BASE_ADDR 0x0304 + +/* G2D_SOFT_RESET */ +#define G2D_SFRCLEAR (1 << 1) +#define G2D_R (1 << 0) + +/* G2D_INTEN */ +#define G2D_INTEN_ACF (1 << 3) +#define G2D_INTEN_UCF (1 << 2) +#define G2D_INTEN_GCF (1 << 1) +#define G2D_INTEN_SCF (1 << 0) + +/* G2D_INTC_PEND */ +#define G2D_INTP_ACMD_FIN (1 << 3) +#define G2D_INTP_UCMD_FIN (1 << 2) +#define G2D_INTP_GCMD_FIN (1 << 1) +#define G2D_INTP_SCMD_FIN (1 << 0) + +/* G2D_DMA_COMMAND */ +#define G2D_DMA_HALT (1 << 2) +#define G2D_DMA_CONTINUE (1 << 1) +#define G2D_DMA_START (1 << 0) + +/* G2D_DMA_STATUS */ +#define G2D_DMA_LIST_DONE_COUNT (0xFF << 17) +#define G2D_DMA_BITBLT_DONE_COUNT (0xFFFF << 1) +#define G2D_DMA_DONE (1 << 0) +#define G2D_DMA_LIST_DONE_COUNT_OFFSET 17 + +/* G2D_DMA_HOLD_CMD */ +#define G2D_USET_HOLD (1 << 2) +#define G2D_LIST_HOLD (1 << 1) +#define G2D_BITBLT_HOLD (1 << 0) + +/* G2D_BITBLT_START */ +#define G2D_START_CASESEL (1 << 2) +#define G2D_START_NHOLT (1 << 1) +#define G2D_START_BITBLT (1 << 0) + +#define G2D_CMDLIST_SIZE (PAGE_SIZE / 4) +#define G2D_CMDLIST_NUM 64 +#define G2D_CMDLIST_POOL_SIZE (G2D_CMDLIST_SIZE * G2D_CMDLIST_NUM) +#define G2D_CMDLIST_DATA_NUM (G2D_CMDLIST_SIZE / sizeof(u32) - 2) + +/* cmdlist data structure */ +struct g2d_cmdlist { + u32 head; + u32 data[G2D_CMDLIST_DATA_NUM]; + u32 last; /* last data offset */ +}; + +struct drm_exynos_pending_g2d_event { + struct drm_pending_event base; + struct drm_exynos_g2d_event event; +}; + +struct g2d_gem_node { + struct list_head list; + unsigned int handle; +}; + +struct g2d_cmdlist_node { + struct list_head list; + struct g2d_cmdlist *cmdlist; + unsigned int gem_nr; + dma_addr_t dma_addr; + + struct drm_exynos_pending_g2d_event *event; +}; + +struct g2d_runqueue_node { + struct list_head list; + struct list_head run_cmdlist; + struct list_head event_list; + struct completion complete; + int async; +}; + +struct g2d_data { + struct device *dev; + struct clk *gate_clk; + struct resource *regs_res; + void __iomem *regs; + int irq; + struct workqueue_struct *g2d_workq; + struct work_struct runqueue_work; + struct exynos_drm_subdrv subdrv; + bool suspended; + + /* cmdlist */ + struct g2d_cmdlist_node *cmdlist_node; + struct list_head free_cmdlist; + struct mutex cmdlist_mutex; + dma_addr_t cmdlist_pool; + void *cmdlist_pool_virt; + + /* runqueue*/ + struct g2d_runqueue_node *runqueue_node; + struct list_head runqueue; + struct mutex runqueue_mutex; + struct kmem_cache *runqueue_slab; +}; + +/* cmdlist functions */ +static void g2d_print_cmdlist(struct device *dev, struct g2d_cmdlist_node *node) +{ + struct g2d_cmdlist *cmdlist = node->cmdlist; + int nr; + int index; + + dev_dbg(dev, "Head: %d\n", cmdlist->head); + for (nr = 0, index = 0; nr < cmdlist->last; nr += 2) { + dev_dbg(dev, "%02d: [0x%08x] 0x%08x\n", index, + cmdlist->data[nr], cmdlist->data[nr + 1]); + index++; + } + dev_dbg(dev, "Tail: 0x%08x\n", cmdlist->data[cmdlist->last]); +} + +static int g2d_init_cmdlist(struct g2d_data *g2d) +{ + struct device *dev = g2d->dev; + struct g2d_cmdlist_node *node = g2d->cmdlist_node; + int nr; + int ret; + + g2d->cmdlist_pool_virt = dma_alloc_coherent(dev, G2D_CMDLIST_POOL_SIZE, + &g2d->cmdlist_pool, GFP_KERNEL); + if (!g2d->cmdlist_pool_virt) { + dev_err(dev, "failed to allocate dma memory\n"); + return -ENOMEM; + } + + node = kcalloc(G2D_CMDLIST_NUM, G2D_CMDLIST_NUM * sizeof(*node), + GFP_KERNEL); + if (!node) { + dev_err(dev, "failed to allocate memory\n"); + ret = -ENOMEM; + goto err; + } + + for (nr = 0; nr < G2D_CMDLIST_NUM; nr++) { + node[nr].cmdlist = + g2d->cmdlist_pool_virt + nr * G2D_CMDLIST_SIZE; + node[nr].dma_addr = + g2d->cmdlist_pool + nr * G2D_CMDLIST_SIZE; + + list_add_tail(&node[nr].list, &g2d->free_cmdlist); + } + + return 0; + +err: + dma_free_coherent(dev, G2D_CMDLIST_POOL_SIZE, g2d->cmdlist_pool_virt, + g2d->cmdlist_pool); + return ret; +} + +static void g2d_fini_cmdlist(struct g2d_data *g2d) +{ + struct device *dev = g2d->dev; + + kfree(g2d->cmdlist_node); + dma_free_coherent(dev, G2D_CMDLIST_POOL_SIZE, g2d->cmdlist_pool_virt, + g2d->cmdlist_pool); +} + +static struct g2d_cmdlist_node *g2d_get_cmdlist(struct g2d_data *g2d) +{ + struct device *dev = g2d->dev; + struct g2d_cmdlist_node *node; + + mutex_lock(&g2d->cmdlist_mutex); + if (list_empty(&g2d->free_cmdlist)) { + dev_err(dev, "there is no free cmdlist\n"); + mutex_unlock(&g2d->cmdlist_mutex); + return NULL; + } + + node = list_first_entry(&g2d->free_cmdlist, struct g2d_cmdlist_node, + list); + list_del_init(&node->list); + mutex_unlock(&g2d->cmdlist_mutex); + + return node; +} + +static void g2d_put_cmdlist(struct g2d_data *g2d, struct g2d_cmdlist_node *node) +{ + mutex_lock(&g2d->cmdlist_mutex); + list_move_tail(&node->list, &g2d->free_cmdlist); + mutex_unlock(&g2d->cmdlist_mutex); +} + +static void g2d_add_cmdlist_to_inuse(struct exynos_drm_g2d_private *g2d_priv, + struct g2d_cmdlist_node *node) +{ + struct g2d_cmdlist_node *lnode; + + if (list_empty(&g2d_priv->inuse_cmdlist)) + goto add_to_list; + + /* this links to base address of new cmdlist */ + lnode = list_entry(g2d_priv->inuse_cmdlist.prev, + struct g2d_cmdlist_node, list); + lnode->cmdlist->data[lnode->cmdlist->last] = node->dma_addr; + +add_to_list: + list_add_tail(&node->list, &g2d_priv->inuse_cmdlist); + + if (node->event) + list_add_tail(&node->event->base.link, &g2d_priv->event_list); +} + +static int g2d_get_cmdlist_gem(struct drm_device *drm_dev, + struct drm_file *file, + struct g2d_cmdlist_node *node) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; + struct g2d_cmdlist *cmdlist = node->cmdlist; + dma_addr_t *addr; + int offset; + int i; + + for (i = 0; i < node->gem_nr; i++) { + struct g2d_gem_node *gem_node; + + gem_node = kzalloc(sizeof(*gem_node), GFP_KERNEL); + if (!gem_node) { + dev_err(g2d_priv->dev, "failed to allocate gem node\n"); + return -ENOMEM; + } + + offset = cmdlist->last - (i * 2 + 1); + gem_node->handle = cmdlist->data[offset]; + + addr = exynos_drm_gem_get_dma_addr(drm_dev, gem_node->handle, + file); + if (IS_ERR(addr)) { + node->gem_nr = i; + kfree(gem_node); + return PTR_ERR(addr); + } + + cmdlist->data[offset] = *addr; + list_add_tail(&gem_node->list, &g2d_priv->gem_list); + g2d_priv->gem_nr++; + } + + return 0; +} + +static void g2d_put_cmdlist_gem(struct drm_device *drm_dev, + struct drm_file *file, + unsigned int nr) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; + struct g2d_gem_node *node, *n; + + list_for_each_entry_safe_reverse(node, n, &g2d_priv->gem_list, list) { + if (!nr) + break; + + exynos_drm_gem_put_dma_addr(drm_dev, node->handle, file); + list_del_init(&node->list); + kfree(node); + nr--; + } +} + +static void g2d_dma_start(struct g2d_data *g2d, + struct g2d_runqueue_node *runqueue_node) +{ + struct g2d_cmdlist_node *node = + list_first_entry(&runqueue_node->run_cmdlist, + struct g2d_cmdlist_node, list); + + pm_runtime_get_sync(g2d->dev); + clk_enable(g2d->gate_clk); + + /* interrupt enable */ + writel_relaxed(G2D_INTEN_ACF | G2D_INTEN_UCF | G2D_INTEN_GCF, + g2d->regs + G2D_INTEN); + + writel_relaxed(node->dma_addr, g2d->regs + G2D_DMA_SFR_BASE_ADDR); + writel_relaxed(G2D_DMA_START, g2d->regs + G2D_DMA_COMMAND); +} + +static struct g2d_runqueue_node *g2d_get_runqueue_node(struct g2d_data *g2d) +{ + struct g2d_runqueue_node *runqueue_node; + + if (list_empty(&g2d->runqueue)) + return NULL; + + runqueue_node = list_first_entry(&g2d->runqueue, + struct g2d_runqueue_node, list); + list_del_init(&runqueue_node->list); + return runqueue_node; +} + +static void g2d_free_runqueue_node(struct g2d_data *g2d, + struct g2d_runqueue_node *runqueue_node) +{ + if (!runqueue_node) + return; + + mutex_lock(&g2d->cmdlist_mutex); + list_splice_tail_init(&runqueue_node->run_cmdlist, &g2d->free_cmdlist); + mutex_unlock(&g2d->cmdlist_mutex); + + kmem_cache_free(g2d->runqueue_slab, runqueue_node); +} + +static void g2d_exec_runqueue(struct g2d_data *g2d) +{ + g2d->runqueue_node = g2d_get_runqueue_node(g2d); + if (g2d->runqueue_node) + g2d_dma_start(g2d, g2d->runqueue_node); +} + +static void g2d_runqueue_worker(struct work_struct *work) +{ + struct g2d_data *g2d = container_of(work, struct g2d_data, + runqueue_work); + + + mutex_lock(&g2d->runqueue_mutex); + clk_disable(g2d->gate_clk); + pm_runtime_put_sync(g2d->dev); + + complete(&g2d->runqueue_node->complete); + if (g2d->runqueue_node->async) + g2d_free_runqueue_node(g2d, g2d->runqueue_node); + + if (g2d->suspended) + g2d->runqueue_node = NULL; + else + g2d_exec_runqueue(g2d); + mutex_unlock(&g2d->runqueue_mutex); +} + +static void g2d_finish_event(struct g2d_data *g2d, u32 cmdlist_no) +{ + struct drm_device *drm_dev = g2d->subdrv.drm_dev; + struct g2d_runqueue_node *runqueue_node = g2d->runqueue_node; + struct drm_exynos_pending_g2d_event *e; + struct timeval now; + unsigned long flags; + + if (list_empty(&runqueue_node->event_list)) + return; + + e = list_first_entry(&runqueue_node->event_list, + struct drm_exynos_pending_g2d_event, base.link); + + do_gettimeofday(&now); + e->event.tv_sec = now.tv_sec; + e->event.tv_usec = now.tv_usec; + e->event.cmdlist_no = cmdlist_no; + + spin_lock_irqsave(&drm_dev->event_lock, flags); + list_move_tail(&e->base.link, &e->base.file_priv->event_list); + wake_up_interruptible(&e->base.file_priv->event_wait); + spin_unlock_irqrestore(&drm_dev->event_lock, flags); +} + +static irqreturn_t g2d_irq_handler(int irq, void *dev_id) +{ + struct g2d_data *g2d = dev_id; + u32 pending; + + pending = readl_relaxed(g2d->regs + G2D_INTC_PEND); + if (pending) + writel_relaxed(pending, g2d->regs + G2D_INTC_PEND); + + if (pending & G2D_INTP_GCMD_FIN) { + u32 cmdlist_no = readl_relaxed(g2d->regs + G2D_DMA_STATUS); + + cmdlist_no = (cmdlist_no & G2D_DMA_LIST_DONE_COUNT) >> 17; + + g2d_finish_event(g2d, cmdlist_no); + + writel_relaxed(0, g2d->regs + G2D_DMA_HOLD_CMD); + if (!(pending & G2D_INTP_ACMD_FIN)) { + writel_relaxed(G2D_DMA_CONTINUE, + g2d->regs + G2D_DMA_COMMAND); + } + } + + if (pending & G2D_INTP_ACMD_FIN) + queue_work(g2d->g2d_workq, &g2d->runqueue_work); + + return IRQ_HANDLED; +} + +/* ioctl functions */ +int exynos_g2d_get_ver_ioctl(struct drm_device *drm_dev, void *data, + struct drm_file *file) +{ + struct drm_exynos_g2d_get_ver *ver = data; + + ver->major = G2D_HW_MAJOR_VER; + ver->minor = G2D_HW_MINOR_VER; + + return 0; +} +EXPORT_SYMBOL_GPL(exynos_g2d_get_ver_ioctl); + +int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; + struct device *dev = g2d_priv->dev; + struct g2d_data *g2d; + struct drm_exynos_g2d_set_cmdlist *req = data; + struct drm_exynos_pending_g2d_event *e; + struct g2d_cmdlist_node *node; + struct g2d_cmdlist *cmdlist; + unsigned long flags; + int ret; + + if (!dev) + return -ENODEV; + + g2d = dev_get_drvdata(dev); + if (!g2d) + return -EFAULT; + + node = g2d_get_cmdlist(g2d); + if (!node) + return -ENOMEM; + + node->event = NULL; + + if (req->event_type != G2D_EVENT_NOT) { + spin_lock_irqsave(&drm_dev->event_lock, flags); + if (file->event_space < sizeof(e->event)) { + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + ret = -ENOMEM; + goto err; + } + file->event_space -= sizeof(e->event); + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + + e = kzalloc(sizeof(*node->event), GFP_KERNEL); + if (!e) { + dev_err(dev, "failed to allocate event\n"); + + spin_lock_irqsave(&drm_dev->event_lock, flags); + file->event_space += sizeof(e->event); + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + + ret = -ENOMEM; + goto err; + } + + e->event.base.type = DRM_EXYNOS_G2D_EVENT; + e->event.base.length = sizeof(e->event); + e->event.user_data = req->user_data; + e->base.event = &e->event.base; + e->base.file_priv = file; + e->base.destroy = (void (*) (struct drm_pending_event *)) kfree; + + node->event = e; + } + + cmdlist = node->cmdlist; + + cmdlist->last = 0; + + /* TODO: check cmdlist size */ + /* + * If don't clear SFR registers, the cmdlist is affected by register + * values of previous cmdlist. G2D hw executes SFR clear command and + * a next command at the same time then the next command is ignored and + * is executed rightly from next next command, so needs a dummy command + * to next command of SFR clear command. + */ + cmdlist->data[cmdlist->last++] = G2D_SOFT_RESET; + cmdlist->data[cmdlist->last++] = G2D_SFRCLEAR; + cmdlist->data[cmdlist->last++] = G2D_SRC_BASE_ADDR; + cmdlist->data[cmdlist->last++] = 0; + + if (node->event) { + cmdlist->data[cmdlist->last++] = G2D_DMA_HOLD_CMD; + cmdlist->data[cmdlist->last++] = G2D_LIST_HOLD; + } + + if (copy_from_user(cmdlist->data + cmdlist->last, + (void __user *)req->cmd, + sizeof(*req->cmd) * req->cmd_nr)) { + ret = -EFAULT; + goto err_free_event; + } + cmdlist->last += req->cmd_nr * 2; + + node->gem_nr = req->cmd_gem_nr; + if (req->cmd_gem_nr) { + struct drm_exynos_g2d_cmd *cmd_gem = req->cmd_gem; + + if (copy_from_user(cmdlist->data + cmdlist->last, + (void __user *)cmd_gem, + sizeof(*cmd_gem) * req->cmd_gem_nr)) { + ret = -EFAULT; + goto err_free_event; + } + cmdlist->last += req->cmd_gem_nr * 2; + + ret = g2d_get_cmdlist_gem(drm_dev, file, node); + if (ret < 0) + goto err_unmap; + } + + cmdlist->data[cmdlist->last++] = G2D_BITBLT_START; + cmdlist->data[cmdlist->last++] = G2D_START_BITBLT; + + /* head */ + cmdlist->head = cmdlist->last / 2; + + /* tail */ + cmdlist->data[cmdlist->last] = 0; + + g2d_add_cmdlist_to_inuse(g2d_priv, node); + + return 0; + +err_unmap: + g2d_put_cmdlist_gem(drm_dev, file, node->gem_nr); +err_free_event: + if (node->event) { + spin_lock_irqsave(&drm_dev->event_lock, flags); + file->event_space += sizeof(e->event); + spin_unlock_irqrestore(&drm_dev->event_lock, flags); + kfree(node->event); + } +err: + g2d_put_cmdlist(g2d, node); + return ret; +} +EXPORT_SYMBOL_GPL(exynos_g2d_set_cmdlist_ioctl); + +int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; + struct device *dev = g2d_priv->dev; + struct g2d_data *g2d; + struct drm_exynos_g2d_exec *req = data; + struct g2d_runqueue_node *runqueue_node; + struct list_head *run_cmdlist; + struct list_head *event_list; + + if (!dev) + return -ENODEV; + + g2d = dev_get_drvdata(dev); + if (!g2d) + return -EFAULT; + + runqueue_node = kmem_cache_alloc(g2d->runqueue_slab, GFP_KERNEL); + if (!runqueue_node) { + dev_err(dev, "failed to allocate memory\n"); + return -ENOMEM; + } + run_cmdlist = &runqueue_node->run_cmdlist; + event_list = &runqueue_node->event_list; + INIT_LIST_HEAD(run_cmdlist); + INIT_LIST_HEAD(event_list); + init_completion(&runqueue_node->complete); + runqueue_node->async = req->async; + + list_splice_init(&g2d_priv->inuse_cmdlist, run_cmdlist); + list_splice_init(&g2d_priv->event_list, event_list); + + if (list_empty(run_cmdlist)) { + dev_err(dev, "there is no inuse cmdlist\n"); + kmem_cache_free(g2d->runqueue_slab, runqueue_node); + return -EPERM; + } + + mutex_lock(&g2d->runqueue_mutex); + list_add_tail(&runqueue_node->list, &g2d->runqueue); + if (!g2d->runqueue_node) + g2d_exec_runqueue(g2d); + mutex_unlock(&g2d->runqueue_mutex); + + if (runqueue_node->async) + goto out; + + wait_for_completion(&runqueue_node->complete); + g2d_free_runqueue_node(g2d, runqueue_node); + +out: + return 0; +} +EXPORT_SYMBOL_GPL(exynos_g2d_exec_ioctl); + +static int g2d_open(struct drm_device *drm_dev, struct device *dev, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_g2d_private *g2d_priv; + + g2d_priv = kzalloc(sizeof(*g2d_priv), GFP_KERNEL); + if (!g2d_priv) { + dev_err(dev, "failed to allocate g2d private data\n"); + return -ENOMEM; + } + + g2d_priv->dev = dev; + file_priv->g2d_priv = g2d_priv; + + INIT_LIST_HEAD(&g2d_priv->inuse_cmdlist); + INIT_LIST_HEAD(&g2d_priv->event_list); + INIT_LIST_HEAD(&g2d_priv->gem_list); + + return 0; +} + +static void g2d_close(struct drm_device *drm_dev, struct device *dev, + struct drm_file *file) +{ + struct drm_exynos_file_private *file_priv = file->driver_priv; + struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; + struct g2d_data *g2d; + struct g2d_cmdlist_node *node, *n; + + if (!dev) + return; + + g2d = dev_get_drvdata(dev); + if (!g2d) + return; + + mutex_lock(&g2d->cmdlist_mutex); + list_for_each_entry_safe(node, n, &g2d_priv->inuse_cmdlist, list) + list_move_tail(&node->list, &g2d->free_cmdlist); + mutex_unlock(&g2d->cmdlist_mutex); + + g2d_put_cmdlist_gem(drm_dev, file, g2d_priv->gem_nr); + + kfree(file_priv->g2d_priv); +} + +static int g2d_subdrv_probe(struct drm_device *drm_dev, struct device *dev) +{ + return 0; +} + +static int __devinit g2d_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct g2d_data *g2d; + struct exynos_drm_subdrv *subdrv; + int ret; + + g2d = kzalloc(sizeof(*g2d), GFP_KERNEL); + if (!g2d) { + dev_err(dev, "failed to allocate driver data\n"); + return -ENOMEM; + } + + g2d->runqueue_slab = kmem_cache_create("g2d_runqueue_slab", + sizeof(struct g2d_runqueue_node), 0, 0, NULL); + if (!g2d->runqueue_slab) { + ret = -ENOMEM; + goto err_free_mem; + } + + g2d->dev = dev; + + g2d->g2d_workq = create_singlethread_workqueue("g2d"); + if (!g2d->g2d_workq) { + dev_err(dev, "failed to create workqueue\n"); + ret = -EINVAL; + goto err_destroy_slab; + } + + INIT_WORK(&g2d->runqueue_work, g2d_runqueue_worker); + INIT_LIST_HEAD(&g2d->free_cmdlist); + INIT_LIST_HEAD(&g2d->runqueue); + + mutex_init(&g2d->cmdlist_mutex); + mutex_init(&g2d->runqueue_mutex); + + ret = g2d_init_cmdlist(g2d); + if (ret < 0) + goto err_destroy_workqueue; + + g2d->gate_clk = clk_get(dev, "fimg2d"); + if (IS_ERR(g2d->gate_clk)) { + dev_err(dev, "failed to get gate clock\n"); + ret = PTR_ERR(g2d->gate_clk); + goto err_fini_cmdlist; + } + + pm_runtime_enable(dev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "failed to get I/O memory\n"); + ret = -ENOENT; + goto err_put_clk; + } + + g2d->regs_res = request_mem_region(res->start, resource_size(res), + dev_name(dev)); + if (!g2d->regs_res) { + dev_err(dev, "failed to request I/O memory\n"); + ret = -ENOENT; + goto err_put_clk; + } + + g2d->regs = ioremap(res->start, resource_size(res)); + if (!g2d->regs) { + dev_err(dev, "failed to remap I/O memory\n"); + ret = -ENXIO; + goto err_release_res; + } + + g2d->irq = platform_get_irq(pdev, 0); + if (g2d->irq < 0) { + dev_err(dev, "failed to get irq\n"); + ret = g2d->irq; + goto err_unmap_base; + } + + ret = request_irq(g2d->irq, g2d_irq_handler, 0, "drm_g2d", g2d); + if (ret < 0) { + dev_err(dev, "irq request failed\n"); + goto err_unmap_base; + } + + platform_set_drvdata(pdev, g2d); + + subdrv = &g2d->subdrv; + subdrv->is_local = true; + subdrv->probe = g2d_subdrv_probe; + subdrv->open = g2d_open; + subdrv->close = g2d_close; + subdrv->manager.dev = dev; + + ret = exynos_drm_subdrv_register(subdrv); + if (ret < 0) { + dev_err(dev, "failed to register drm g2d device\n"); + goto err_free_irq; + } + + dev_info(dev, "The exynos g2d(ver %d.%d) successfully probed\n", + G2D_HW_MAJOR_VER, G2D_HW_MINOR_VER); + + return 0; + +err_free_irq: + free_irq(g2d->irq, g2d); +err_unmap_base: + iounmap(g2d->regs); +err_release_res: + release_resource(g2d->regs_res); + kfree(g2d->regs_res); +err_put_clk: + pm_runtime_disable(dev); + clk_put(g2d->gate_clk); +err_fini_cmdlist: + g2d_fini_cmdlist(g2d); +err_destroy_workqueue: + destroy_workqueue(g2d->g2d_workq); +err_destroy_slab: + kmem_cache_destroy(g2d->runqueue_slab); +err_free_mem: + kfree(g2d); + return ret; +} + +static int __devexit g2d_remove(struct platform_device *pdev) +{ + struct g2d_data *g2d = platform_get_drvdata(pdev); + + cancel_work_sync(&g2d->runqueue_work); + exynos_drm_subdrv_unregister(&g2d->subdrv); + free_irq(g2d->irq, g2d); + + while (g2d->runqueue_node) { + g2d_free_runqueue_node(g2d, g2d->runqueue_node); + g2d->runqueue_node = g2d_get_runqueue_node(g2d); + } + + iounmap(g2d->regs); + release_resource(g2d->regs_res); + kfree(g2d->regs_res); + + pm_runtime_disable(&pdev->dev); + clk_put(g2d->gate_clk); + + g2d_fini_cmdlist(g2d); + destroy_workqueue(g2d->g2d_workq); + kmem_cache_destroy(g2d->runqueue_slab); + kfree(g2d); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int g2d_suspend(struct device *dev) +{ + struct g2d_data *g2d = dev_get_drvdata(dev); + + mutex_lock(&g2d->runqueue_mutex); + g2d->suspended = true; + mutex_unlock(&g2d->runqueue_mutex); + + while (g2d->runqueue_node) + /* FIXME: good range? */ + usleep_range(500, 1000); + + flush_work_sync(&g2d->runqueue_work); + + return 0; +} + +static int g2d_resume(struct device *dev) +{ + struct g2d_data *g2d = dev_get_drvdata(dev); + + g2d->suspended = false; + g2d_exec_runqueue(g2d); + + return 0; +} +#endif + +SIMPLE_DEV_PM_OPS(g2d_pm_ops, g2d_suspend, g2d_resume); + +struct platform_driver g2d_driver = { + .probe = g2d_probe, + .remove = __devexit_p(g2d_remove), + .driver = { + .name = "s5p-g2d", + .owner = THIS_MODULE, + .pm = &g2d_pm_ops, + }, +}; diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.h b/drivers/gpu/drm/exynos/exynos_drm_g2d.h new file mode 100644 index 0000000..1a9c7ca --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Authors: Joonyoung Shim jy0922.shim@samsung.com + * + * 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 Foundationr + */ + +#ifdef CONFIG_DRM_EXYNOS_G2D +extern int exynos_g2d_get_ver_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int exynos_g2d_set_cmdlist_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int exynos_g2d_exec_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +#else +static inline int exynos_g2d_get_ver_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return -ENODEV; +} + +static inline int exynos_g2d_set_cmdlist_ioctl(struct drm_device *dev, + void *data, + struct drm_file *file_priv) +{ + return -ENODEV; +} + +static inline int exynos_g2d_exec_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return -ENODEV; +} +#endif diff --git a/include/drm/exynos_drm.h b/include/drm/exynos_drm.h index 81c9cb7..907daaf 100644 --- a/include/drm/exynos_drm.h +++ b/include/drm/exynos_drm.h @@ -29,6 +29,8 @@ #ifndef _EXYNOS_DRM_H_ #define _EXYNOS_DRM_H_
+#include "drm.h" + /** * User-desired buffer creation information structure. * @@ -85,12 +87,48 @@ enum e_drm_exynos_gem_mem_type { EXYNOS_BO_NONCONTIG = 1 << 0 };
+struct drm_exynos_g2d_get_ver { + __u32 major; + __u32 minor; +}; + +struct drm_exynos_g2d_cmd { + __u32 offset; + __u32 data; +}; + +enum drm_exynos_g2d_event_type { + G2D_EVENT_NOT, + G2D_EVENT_NONSTOP, + G2D_EVENT_STOP, /* not yet */ +}; + +struct drm_exynos_g2d_set_cmdlist { + struct drm_exynos_g2d_cmd *cmd; + struct drm_exynos_g2d_cmd *cmd_gem; + __u32 cmd_nr; + __u32 cmd_gem_nr; + + /* for g2d event */ + enum drm_exynos_g2d_event_type event_type; + __u32 user_data; +}; + +struct drm_exynos_g2d_exec { + __u32 async; +}; + #define DRM_EXYNOS_GEM_CREATE 0x00 #define DRM_EXYNOS_GEM_MAP_OFFSET 0x01 #define DRM_EXYNOS_GEM_MMAP 0x02 /* Reserved 0x03 ~ 0x05 for exynos specific gem ioctl */ #define DRM_EXYNOS_PLANE_SET_ZPOS 0x06
+/* G2D */ +#define DRM_EXYNOS_G2D_GET_VER 0x20 +#define DRM_EXYNOS_G2D_SET_CMDLIST 0x21 +#define DRM_EXYNOS_G2D_EXEC 0x22 + #define DRM_IOCTL_EXYNOS_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \ DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create)
@@ -103,6 +141,24 @@ enum e_drm_exynos_gem_mem_type { #define DRM_IOCTL_EXYNOS_PLANE_SET_ZPOS DRM_IOWR(DRM_COMMAND_BASE + \ DRM_EXYNOS_PLANE_SET_ZPOS, struct drm_exynos_plane_set_zpos)
+#define DRM_IOCTL_EXYNOS_G2D_GET_VER DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_EXYNOS_G2D_GET_VER, struct drm_exynos_g2d_get_ver) +#define DRM_IOCTL_EXYNOS_G2D_SET_CMDLIST DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_EXYNOS_G2D_SET_CMDLIST, struct drm_exynos_g2d_set_cmdlist) +#define DRM_IOCTL_EXYNOS_G2D_EXEC DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_EXYNOS_G2D_EXEC, struct drm_exynos_g2d_exec) + +/* EXYNOS specific events */ +#define DRM_EXYNOS_G2D_EVENT 0x80000000 + +struct drm_exynos_g2d_event { + struct drm_event base; + __u32 tv_sec; + __u32 tv_usec; + __u32 cmdlist_no; + __u32 user_data; +}; + #ifdef __KERNEL__
/**
index 1ed3aae..aff2313 100644 --- a/include/drm/exynos_drm.h +++ b/include/drm/exynos_drm.h @@ -147,11 +147,13 @@ struct exynos_drm_common_hdmi_pd { * @timing: default video mode for initializing * @default_win: default window layer number to be used for UI. * @bpp: default bit per pixel.
- @is_v13: set if hdmi version 13 is.
*/ struct exynos_drm_hdmi_pdata { struct fb_videomode timing; unsigned int default_win; unsigned int bpp;
- unsigned int is_v13:1;
};
Is this an ABI change, where do you document to userspace that it can use this field, have you bumped some userspace accessible version?
Dave.
On 03/15/2012 07:52 PM, Dave Airlie wrote:
index 1ed3aae..aff2313 100644 --- a/include/drm/exynos_drm.h +++ b/include/drm/exynos_drm.h @@ -147,11 +147,13 @@ struct exynos_drm_common_hdmi_pd {
- @timing: default video mode for initializing
- @default_win: default window layer number to be used for UI.
- @bpp: default bit per pixel.
*/ struct exynos_drm_hdmi_pdata { struct fb_videomode timing; unsigned int default_win; unsigned int bpp;
- @is_v13: set if hdmi version 13 is.
};unsigned int is_v13:1;
Is this an ABI change, where do you document to userspace that it can use this field, have you bumped some userspace accessible version?
No, this doesn't related with user space currently.
dri-devel@lists.freedesktop.org