/* * (C) Copyright 2008-2017 Fuzhou Rockchip Electronics Co., Ltd * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rockchip_display.h" #include "rockchip_crtc.h" #include "rockchip_connector.h" #include "rockchip_mipi_dsi.h" #include "rockchip_panel.h" struct rockchip_cmd_header { u8 data_type; u8 delay_ms; u8 payload_length; } __packed; struct rockchip_cmd_desc { struct rockchip_cmd_header header; const u8 *payload; }; struct rockchip_panel_cmds { struct rockchip_cmd_desc *cmds; int cmd_cnt; }; struct rockchip_panel_plat { bool power_invert; u32 bus_format; struct { unsigned int prepare; unsigned int unprepare; unsigned int enable; unsigned int disable; unsigned int reset; unsigned int init; } delay; struct rockchip_panel_cmds *on_cmds; struct rockchip_panel_cmds *off_cmds; }; struct rockchip_panel_priv { bool prepared; bool enabled; struct udevice *power_supply; struct udevice *backlight; struct gpio_desc enable_gpio; struct gpio_desc reset_gpio; }; static int rockchip_panel_parse_cmds(const u8 *data, int length, struct rockchip_panel_cmds *pcmds) { int len; const u8 *buf; const struct rockchip_cmd_header *header; int i, cnt = 0; /* scan commands */ cnt = 0; buf = data; len = length; while (len > sizeof(*header)) { header = (const struct rockchip_cmd_header *)buf; buf += sizeof(*header) + header->payload_length; len -= sizeof(*header) + header->payload_length; cnt++; } pcmds->cmds = calloc(cnt, sizeof(struct rockchip_cmd_desc)); if (!pcmds->cmds) return -ENOMEM; pcmds->cmd_cnt = cnt; buf = data; len = length; for (i = 0; i < cnt; i++) { struct rockchip_cmd_desc *desc = &pcmds->cmds[i]; header = (const struct rockchip_cmd_header *)buf; length -= sizeof(*header); buf += sizeof(*header); desc->header.data_type = header->data_type; desc->header.delay_ms = header->delay_ms; desc->header.payload_length = header->payload_length; desc->payload = buf; buf += header->payload_length; length -= header->payload_length; } return 0; } static int rockchip_panel_send_cmds(struct display_state *state, struct rockchip_panel_cmds *cmds) { int i, ret; if (!cmds) return -EINVAL; for (i = 0; i < cmds->cmd_cnt; i++) { struct rockchip_cmd_desc *desc = &cmds->cmds[i]; switch (desc->header.data_type) { case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM: case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM: case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM: case MIPI_DSI_GENERIC_LONG_WRITE: ret = mipi_dsi_generic_write(state, desc->payload, desc->header.payload_length); break; case MIPI_DSI_DCS_SHORT_WRITE: case MIPI_DSI_DCS_SHORT_WRITE_PARAM: case MIPI_DSI_DCS_LONG_WRITE: ret = mipi_dsi_dcs_write(state, desc->payload, desc->header.payload_length); break; default: printf("unsupport command data type: %d\n", desc->header.data_type); return -EINVAL; } if (ret) { printf("failed to write cmd%d: %d\n", i, ret); return ret; } if (desc->header.delay_ms) mdelay(desc->header.delay_ms); } return 0; } static int rockchip_panel_prepare(struct display_state *state) { struct panel_state *panel_state = &state->panel_state; struct rockchip_panel_plat *plat = dev_get_platdata(panel_state->dev); struct rockchip_panel_priv *priv = dev_get_priv(panel_state->dev); int ret; if (priv->prepared) return 0; if (priv->power_supply) { ret = regulator_set_enable(priv->power_supply, !plat->power_invert); if (ret) { printf("%s: failed to enable power supply", __func__); return ret; } } dm_gpio_set_value(&priv->enable_gpio, 1); mdelay(plat->delay.prepare); dm_gpio_set_value(&priv->reset_gpio, 1); mdelay(plat->delay.reset); dm_gpio_set_value(&priv->reset_gpio, 0); mdelay(plat->delay.init); if (plat->on_cmds) { ret = rockchip_panel_send_cmds(state, plat->on_cmds); if (ret) printf("failed to send on cmds: %d\n", ret); } priv->prepared = true; return 0; } static void rockchip_panel_unprepare(struct display_state *state) { struct panel_state *panel_state = &state->panel_state; struct rockchip_panel_plat *plat = dev_get_platdata(panel_state->dev); struct rockchip_panel_priv *priv = dev_get_priv(panel_state->dev); int ret; if (!priv->prepared) return; if (plat->off_cmds) { ret = rockchip_panel_send_cmds(state, plat->off_cmds); if (ret) printf("failed to send off cmds: %d\n", ret); } dm_gpio_set_value(&priv->reset_gpio, 1); dm_gpio_set_value(&priv->enable_gpio, 0); if (priv->power_supply) { ret = regulator_set_enable(priv->power_supply, plat->power_invert); if (ret) printf("%s: failed to disable power supply", __func__); } mdelay(plat->delay.unprepare); priv->prepared = false; } static int rockchip_panel_enable(struct display_state *state) { struct panel_state *panel_state = &state->panel_state; struct rockchip_panel_plat *plat = dev_get_platdata(panel_state->dev); struct rockchip_panel_priv *priv = dev_get_priv(panel_state->dev); if (priv->enabled) return 0; mdelay(plat->delay.enable); if (priv->backlight) backlight_enable(priv->backlight); priv->enabled = true; return 0; } static void rockchip_panel_disable(struct display_state *state) { struct panel_state *panel_state = &state->panel_state; struct rockchip_panel_plat *plat = dev_get_platdata(panel_state->dev); struct rockchip_panel_priv *priv = dev_get_priv(panel_state->dev); if (!priv->enabled) return; if (priv->backlight) backlight_disable(priv->backlight); mdelay(plat->delay.disable); priv->enabled = false; } static int rockchip_panel_init(struct display_state *state) { struct connector_state *conn_state = &state->conn_state; struct panel_state *panel_state = &state->panel_state; struct rockchip_panel_plat *plat = dev_get_platdata(panel_state->dev); conn_state->bus_format = plat->bus_format; return 0; } static const struct rockchip_panel_funcs rockchip_panel_funcs = { .init = rockchip_panel_init, .prepare = rockchip_panel_prepare, .unprepare = rockchip_panel_unprepare, .enable = rockchip_panel_enable, .disable = rockchip_panel_disable, }; static int rockchip_panel_ofdata_to_platdata(struct udevice *dev) { struct rockchip_panel_plat *plat = dev_get_platdata(dev); const void *data; int len = 0; int ret; plat->power_invert = dev_read_bool(dev, "power-invert"); plat->delay.prepare = dev_read_u32_default(dev, "prepare-delay-ms", 0); plat->delay.unprepare = dev_read_u32_default(dev, "unprepare-delay-ms", 0); plat->delay.enable = dev_read_u32_default(dev, "enable-delay-ms", 0); plat->delay.disable = dev_read_u32_default(dev, "disable-delay-ms", 0); plat->delay.init = dev_read_u32_default(dev, "init-delay-ms", 0); plat->delay.reset = dev_read_u32_default(dev, "reset-delay-ms", 0); plat->bus_format = dev_read_u32_default(dev, "bus-format", MEDIA_BUS_FMT_RBG888_1X24); data = dev_read_prop(dev, "panel-init-sequence", &len); if (data) { plat->on_cmds = calloc(1, sizeof(*plat->on_cmds)); if (!plat->on_cmds) return -ENOMEM; ret = rockchip_panel_parse_cmds(data, len, plat->on_cmds); if (ret) { printf("failed to parse panel init sequence\n"); goto free_on_cmds; } } data = dev_read_prop(dev, "panel-exit-sequence", &len); if (data) { plat->off_cmds = calloc(1, sizeof(*plat->off_cmds)); if (!plat->off_cmds) { ret = -ENOMEM; goto free_on_cmds; } ret = rockchip_panel_parse_cmds(data, len, plat->off_cmds); if (ret) { printf("failed to parse panel exit sequence\n"); goto free_cmds; } } return 0; free_cmds: free(plat->off_cmds); free_on_cmds: free(plat->on_cmds); return ret; } static int rockchip_panel_probe(struct udevice *dev) { struct rockchip_panel_priv *priv = dev_get_priv(dev); int ret; ret = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable_gpio, GPIOD_IS_OUT); if (ret && ret != -ENOENT) { printf("%s: Cannot get enable GPIO: %d\n", __func__, ret); return ret; } ret = gpio_request_by_name(dev, "reset-gpios", 0, &priv->reset_gpio, GPIOD_IS_OUT); if (ret && ret != -ENOENT) { printf("%s: Cannot get reset GPIO: %d\n", __func__, ret); return ret; } ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, "backlight", &priv->backlight); if (ret && ret != -ENOENT) { printf("%s: Cannot get backlight: %d\n", __func__, ret); return ret; } ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, "power-supply", &priv->power_supply); if (ret && ret != -ENOENT) { printf("%s: Cannot get power supply: %d\n", __func__, ret); return ret; } return 0; } static const struct drm_display_mode auo_b125han03_mode = { .clock = 146900, .hdisplay = 1920, .hsync_start = 1920 + 48, .hsync_end = 1920 + 48 + 32, .htotal = 1920 + 48 + 32 + 140, .vdisplay = 1080, .vsync_start = 1080 + 2, .vsync_end = 1080 + 2 + 5, .vtotal = 1080 + 2 + 5 + 57, .vrefresh = 60, .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, }; static const struct rockchip_panel auo_b125han03_data = { .funcs = &rockchip_panel_funcs, .data = &auo_b125han03_mode, }; static const struct drm_display_mode lg_lp079qx1_sp0v_mode = { .clock = 200000, .hdisplay = 1536, .hsync_start = 1536 + 12, .hsync_end = 1536 + 12 + 16, .htotal = 1536 + 12 + 16 + 48, .vdisplay = 2048, .vsync_start = 2048 + 8, .vsync_end = 2048 + 8 + 4, .vtotal = 2048 + 8 + 4 + 8, .vrefresh = 60, .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, }; static const struct rockchip_panel lg_lp079qx1_sp0v_data = { .funcs = &rockchip_panel_funcs, .data = &lg_lp079qx1_sp0v_mode, }; static const struct rockchip_panel rockchip_panel_data = { .funcs = &rockchip_panel_funcs, }; static const struct udevice_id rockchip_panel_ids[] = { { .compatible = "auo,b125han03", .data = (ulong)&auo_b125han03_data, }, { .compatible = "lg,lp079qx1-sp0v", .data = (ulong)&lg_lp079qx1_sp0v_data, }, { .compatible = "simple-panel", .data = (ulong)&rockchip_panel_data, }, { .compatible = "simple-panel-dsi", .data = (ulong)&rockchip_panel_data, }, {} }; U_BOOT_DRIVER(rockchip_panel) = { .name = "rockchip_panel", .id = UCLASS_PANEL, .of_match = rockchip_panel_ids, .ofdata_to_platdata = rockchip_panel_ofdata_to_platdata, .probe = rockchip_panel_probe, .priv_auto_alloc_size = sizeof(struct rockchip_panel_priv), .platdata_auto_alloc_size = sizeof(struct rockchip_panel_plat), };