Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Harigovindan P | 1312 | 98.28% | 1 | 20.00% |
Sam Ravnborg | 13 | 0.97% | 1 | 20.00% |
Randy Dunlap | 5 | 0.37% | 1 | 20.00% |
Ville Syrjälä | 4 | 0.30% | 1 | 20.00% |
Uwe Kleine-König | 1 | 0.07% | 1 | 20.00% |
Total | 1335 | 5 |
// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019, The Linux Foundation. All rights reserved. */ #include <linux/delay.h> #include <linux/module.h> #include <linux/of_device.h> #include <linux/gpio/consumer.h> #include <linux/regulator/consumer.h> #include <video/mipi_display.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_modes.h> #include <drm/drm_panel.h> struct visionox_rm69299 { struct drm_panel panel; struct regulator_bulk_data supplies[2]; struct gpio_desc *reset_gpio; struct mipi_dsi_device *dsi; bool prepared; bool enabled; }; static inline struct visionox_rm69299 *panel_to_ctx(struct drm_panel *panel) { return container_of(panel, struct visionox_rm69299, panel); } static int visionox_rm69299_power_on(struct visionox_rm69299 *ctx) { int ret; ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); if (ret < 0) return ret; /* * Reset sequence of visionox panel requires the panel to be * out of reset for 10ms, followed by being held in reset * for 10ms and then out again */ gpiod_set_value(ctx->reset_gpio, 1); usleep_range(10000, 20000); gpiod_set_value(ctx->reset_gpio, 0); usleep_range(10000, 20000); gpiod_set_value(ctx->reset_gpio, 1); usleep_range(10000, 20000); return 0; } static int visionox_rm69299_power_off(struct visionox_rm69299 *ctx) { gpiod_set_value(ctx->reset_gpio, 0); return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); } static int visionox_rm69299_unprepare(struct drm_panel *panel) { struct visionox_rm69299 *ctx = panel_to_ctx(panel); int ret; ctx->dsi->mode_flags = 0; ret = mipi_dsi_dcs_write(ctx->dsi, MIPI_DCS_SET_DISPLAY_OFF, NULL, 0); if (ret < 0) dev_err(ctx->panel.dev, "set_display_off cmd failed ret = %d\n", ret); /* 120ms delay required here as per DCS spec */ msleep(120); ret = mipi_dsi_dcs_write(ctx->dsi, MIPI_DCS_ENTER_SLEEP_MODE, NULL, 0); if (ret < 0) { dev_err(ctx->panel.dev, "enter_sleep cmd failed ret = %d\n", ret); } ret = visionox_rm69299_power_off(ctx); ctx->prepared = false; return ret; } static int visionox_rm69299_prepare(struct drm_panel *panel) { struct visionox_rm69299 *ctx = panel_to_ctx(panel); int ret; if (ctx->prepared) return 0; ret = visionox_rm69299_power_on(ctx); if (ret < 0) return ret; ctx->dsi->mode_flags |= MIPI_DSI_MODE_LPM; ret = mipi_dsi_dcs_write_buffer(ctx->dsi, (u8[]) { 0xfe, 0x00 }, 2); if (ret < 0) { dev_err(ctx->panel.dev, "cmd set tx 0 failed, ret = %d\n", ret); goto power_off; } ret = mipi_dsi_dcs_write_buffer(ctx->dsi, (u8[]) { 0xc2, 0x08 }, 2); if (ret < 0) { dev_err(ctx->panel.dev, "cmd set tx 1 failed, ret = %d\n", ret); goto power_off; } ret = mipi_dsi_dcs_write_buffer(ctx->dsi, (u8[]) { 0x35, 0x00 }, 2); if (ret < 0) { dev_err(ctx->panel.dev, "cmd set tx 2 failed, ret = %d\n", ret); goto power_off; } ret = mipi_dsi_dcs_write_buffer(ctx->dsi, (u8[]) { 0x51, 0xff }, 2); if (ret < 0) { dev_err(ctx->panel.dev, "cmd set tx 3 failed, ret = %d\n", ret); goto power_off; } ret = mipi_dsi_dcs_write(ctx->dsi, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0); if (ret < 0) { dev_err(ctx->panel.dev, "exit_sleep_mode cmd failed ret = %d\n", ret); goto power_off; } /* Per DSI spec wait 120ms after sending exit sleep DCS command */ msleep(120); ret = mipi_dsi_dcs_write(ctx->dsi, MIPI_DCS_SET_DISPLAY_ON, NULL, 0); if (ret < 0) { dev_err(ctx->panel.dev, "set_display_on cmd failed ret = %d\n", ret); goto power_off; } /* Per DSI spec wait 120ms after sending set_display_on DCS command */ msleep(120); ctx->prepared = true; return 0; power_off: return ret; } static const struct drm_display_mode visionox_rm69299_1080x2248_60hz = { .name = "1080x2248", .clock = 158695, .hdisplay = 1080, .hsync_start = 1080 + 26, .hsync_end = 1080 + 26 + 2, .htotal = 1080 + 26 + 2 + 36, .vdisplay = 2248, .vsync_start = 2248 + 56, .vsync_end = 2248 + 56 + 4, .vtotal = 2248 + 56 + 4 + 4, .flags = 0, }; static int visionox_rm69299_get_modes(struct drm_panel *panel, struct drm_connector *connector) { struct visionox_rm69299 *ctx = panel_to_ctx(panel); struct drm_display_mode *mode; mode = drm_mode_duplicate(connector->dev, &visionox_rm69299_1080x2248_60hz); if (!mode) { dev_err(ctx->panel.dev, "failed to create a new display mode\n"); return 0; } connector->display_info.width_mm = 74; connector->display_info.height_mm = 131; mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; drm_mode_probed_add(connector, mode); return 1; } static const struct drm_panel_funcs visionox_rm69299_drm_funcs = { .unprepare = visionox_rm69299_unprepare, .prepare = visionox_rm69299_prepare, .get_modes = visionox_rm69299_get_modes, }; static int visionox_rm69299_probe(struct mipi_dsi_device *dsi) { struct device *dev = &dsi->dev; struct visionox_rm69299 *ctx; int ret; ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; mipi_dsi_set_drvdata(dsi, ctx); ctx->panel.dev = dev; ctx->dsi = dsi; ctx->supplies[0].supply = "vdda"; ctx->supplies[1].supply = "vdd3p3"; ret = devm_regulator_bulk_get(ctx->panel.dev, ARRAY_SIZE(ctx->supplies), ctx->supplies); if (ret < 0) return ret; ctx->reset_gpio = devm_gpiod_get(ctx->panel.dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ctx->reset_gpio)) { dev_err(dev, "cannot get reset gpio %ld\n", PTR_ERR(ctx->reset_gpio)); return PTR_ERR(ctx->reset_gpio); } drm_panel_init(&ctx->panel, dev, &visionox_rm69299_drm_funcs, DRM_MODE_CONNECTOR_DSI); ctx->panel.dev = dev; ctx->panel.funcs = &visionox_rm69299_drm_funcs; drm_panel_add(&ctx->panel); dsi->lanes = 4; dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS; ret = mipi_dsi_attach(dsi); if (ret < 0) { dev_err(dev, "dsi attach failed ret = %d\n", ret); goto err_dsi_attach; } ret = regulator_set_load(ctx->supplies[0].consumer, 32000); if (ret) { dev_err(dev, "regulator set load failed for vdda supply ret = %d\n", ret); goto err_set_load; } ret = regulator_set_load(ctx->supplies[1].consumer, 13200); if (ret) { dev_err(dev, "regulator set load failed for vdd3p3 supply ret = %d\n", ret); goto err_set_load; } return 0; err_set_load: mipi_dsi_detach(dsi); err_dsi_attach: drm_panel_remove(&ctx->panel); return ret; } static void visionox_rm69299_remove(struct mipi_dsi_device *dsi) { struct visionox_rm69299 *ctx = mipi_dsi_get_drvdata(dsi); mipi_dsi_detach(ctx->dsi); mipi_dsi_device_unregister(ctx->dsi); drm_panel_remove(&ctx->panel); } static const struct of_device_id visionox_rm69299_of_match[] = { { .compatible = "visionox,rm69299-1080p-display", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, visionox_rm69299_of_match); static struct mipi_dsi_driver visionox_rm69299_driver = { .driver = { .name = "panel-visionox-rm69299", .of_match_table = visionox_rm69299_of_match, }, .probe = visionox_rm69299_probe, .remove = visionox_rm69299_remove, }; module_mipi_dsi_driver(visionox_rm69299_driver); MODULE_DESCRIPTION("Visionox RM69299 DSI Panel Driver"); MODULE_LICENSE("GPL v2");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1