Files
BBeOS/drivers/display/q20-panel.c
Eliott 7b53cde2ae
Some checks failed
CI / markdown-lint (push) Failing after 14s
Complete BBeOS project implementation with BlackBerry-inspired website
- Updated .gitignore with comprehensive exclusions for build artifacts, IDE files, and OS-specific files
- Created BlackBerry-inspired website with Heroicons and Gitea integration
- Added complete project structure with all 7 phases implemented
- Included kernel drivers, UI components, telephony stack, and packaging tools
- Added emulation scripts for testing and development
- Comprehensive documentation for all development phases
- Security analysis and hardware testing guides
- SDK and application framework for third-party development
2025-08-01 10:20:28 +02:00

283 lines
6.5 KiB
C

/*
* Q20 Display Panel Driver
* BlackBerry Classic Q20 720x720 IPS LCD Panel
*
* This driver provides support for the Q20's display panel
* connected via MIPI DSI to the MSM8960 MDP5 controller.
*/
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/delay.h>
#include <linux/backlight.h>
#include <linux/gpio/consumer.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
struct q20_panel {
struct drm_panel panel;
struct mipi_dsi_device *dsi;
struct regulator *supply;
struct gpio_desc *reset_gpio;
struct gpio_desc *enable_gpio;
struct backlight_device *backlight;
bool prepared;
bool enabled;
};
static inline struct q20_panel *to_q20_panel(struct drm_panel *panel)
{
return container_of(panel, struct q20_panel, panel);
}
static int q20_panel_prepare(struct drm_panel *panel)
{
struct q20_panel *q20 = to_q20_panel(panel);
int ret;
if (q20->prepared)
return 0;
DRM_DEV_INFO(&q20->dsi->dev, "Preparing Q20 panel\n");
/* Enable power supply */
if (q20->supply) {
ret = regulator_enable(q20->supply);
if (ret < 0) {
DRM_DEV_ERROR(&q20->dsi->dev, "Failed to enable supply: %d\n", ret);
return ret;
}
}
/* Reset sequence */
if (q20->reset_gpio) {
gpiod_set_value_cansleep(q20->reset_gpio, 0);
msleep(10);
gpiod_set_value_cansleep(q20->reset_gpio, 1);
msleep(120);
}
/* Enable panel */
if (q20->enable_gpio) {
gpiod_set_value_cansleep(q20->enable_gpio, 1);
msleep(20);
}
q20->prepared = true;
return 0;
}
static int q20_panel_enable(struct drm_panel *panel)
{
struct q20_panel *q20 = to_q20_panel(panel);
int ret;
if (q20->enabled)
return 0;
DRM_DEV_INFO(&q20->dsi->dev, "Enabling Q20 panel\n");
/* Enable backlight */
if (q20->backlight) {
q20->backlight->props.power = FB_BLANK_UNBLANK;
ret = backlight_update_status(q20->backlight);
if (ret < 0) {
DRM_DEV_ERROR(&q20->dsi->dev, "Failed to enable backlight: %d\n", ret);
return ret;
}
}
q20->enabled = true;
return 0;
}
static int q20_panel_disable(struct drm_panel *panel)
{
struct q20_panel *q20 = to_q20_panel(panel);
if (!q20->enabled)
return 0;
DRM_DEV_INFO(&q20->dsi->dev, "Disabling Q20 panel\n");
/* Disable backlight */
if (q20->backlight) {
q20->backlight->props.power = FB_BLANK_POWERDOWN;
backlight_update_status(q20->backlight);
}
q20->enabled = false;
return 0;
}
static int q20_panel_unprepare(struct drm_panel *panel)
{
struct q20_panel *q20 = to_q20_panel(panel);
if (!q20->prepared)
return 0;
DRM_DEV_INFO(&q20->dsi->dev, "Unpreparing Q20 panel\n");
/* Disable panel */
if (q20->enable_gpio)
gpiod_set_value_cansleep(q20->enable_gpio, 0);
/* Reset panel */
if (q20->reset_gpio)
gpiod_set_value_cansleep(q20->reset_gpio, 0);
/* Disable power supply */
if (q20->supply)
regulator_disable(q20->supply);
q20->prepared = false;
return 0;
}
static int q20_panel_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
struct drm_display_mode *mode;
DRM_DEV_INFO(&to_q20_panel(panel)->dsi->dev, "Getting Q20 panel modes\n");
mode = drm_mode_duplicate(connector->dev, &(struct drm_display_mode){
DRM_MODE("720x720", DRM_MODE_TYPE_DRIVER, 30000, 720, 720,
720, 720, 0, 0, 0, 0, 0, 0, 0)
});
if (!mode) {
DRM_DEV_ERROR(&to_q20_panel(panel)->dsi->dev, "Failed to create mode\n");
return -ENOMEM;
}
drm_mode_set_name(mode);
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(connector, mode);
connector->display_info.width_mm = 35; /* Approximate panel width */
connector->display_info.height_mm = 35; /* Approximate panel height */
return 1;
}
static const struct drm_panel_funcs q20_panel_funcs = {
.prepare = q20_panel_prepare,
.enable = q20_panel_enable,
.disable = q20_panel_disable,
.unprepare = q20_panel_unprepare,
.get_modes = q20_panel_get_modes,
};
static int q20_panel_probe(struct mipi_dsi_device *dsi)
{
struct q20_panel *q20;
struct device *dev = &dsi->dev;
int ret;
DRM_DEV_INFO(dev, "Probing Q20 panel\n");
q20 = devm_kzalloc(dev, sizeof(*q20), GFP_KERNEL);
if (!q20)
return -ENOMEM;
q20->dsi = dsi;
mipi_dsi_set_drvdata(dsi, q20);
/* Get power supply */
q20->supply = devm_regulator_get(dev, "vdd");
if (IS_ERR(q20->supply)) {
ret = PTR_ERR(q20->supply);
if (ret != -EPROBE_DEFER)
DRM_DEV_ERROR(dev, "Failed to get vdd regulator: %d\n", ret);
return ret;
}
/* Get reset GPIO */
q20->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(q20->reset_gpio)) {
ret = PTR_ERR(q20->reset_gpio);
DRM_DEV_ERROR(dev, "Failed to get reset GPIO: %d\n", ret);
return ret;
}
/* Get enable GPIO */
q20->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
if (IS_ERR(q20->enable_gpio)) {
ret = PTR_ERR(q20->enable_gpio);
DRM_DEV_ERROR(dev, "Failed to get enable GPIO: %d\n", ret);
return ret;
}
/* Get backlight */
q20->backlight = devm_of_find_backlight(dev);
if (IS_ERR(q20->backlight)) {
ret = PTR_ERR(q20->backlight);
if (ret != -EPROBE_DEFER)
DRM_DEV_ERROR(dev, "Failed to get backlight: %d\n", ret);
return ret;
}
drm_panel_init(&q20->panel, dev, &q20_panel_funcs,
DRM_MODE_CONNECTOR_DSI);
ret = drm_panel_add(&q20->panel);
if (ret < 0) {
DRM_DEV_ERROR(dev, "Failed to add panel: %d\n", ret);
return ret;
}
/* Configure DSI */
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->lanes = 2;
ret = mipi_dsi_attach(dsi);
if (ret < 0) {
DRM_DEV_ERROR(dev, "Failed to attach to DSI host: %d\n", ret);
drm_panel_remove(&q20->panel);
return ret;
}
DRM_DEV_INFO(dev, "Q20 panel probed successfully\n");
return 0;
}
static int q20_panel_remove(struct mipi_dsi_device *dsi)
{
struct q20_panel *q20 = mipi_dsi_get_drvdata(dsi);
DRM_DEV_INFO(&dsi->dev, "Removing Q20 panel\n");
mipi_dsi_detach(dsi);
drm_panel_remove(&q20->panel);
return 0;
}
static const struct of_device_id q20_panel_of_match[] = {
{ .compatible = "blackberry,q20-panel" },
{ }
};
MODULE_DEVICE_TABLE(of, q20_panel_of_match);
static struct mipi_dsi_driver q20_panel_driver = {
.driver = {
.name = "q20-panel",
.of_match_table = q20_panel_of_match,
},
.probe = q20_panel_probe,
.remove = q20_panel_remove,
};
module_mipi_dsi_driver(q20_panel_driver);
MODULE_AUTHOR("BBeOS Team");
MODULE_DESCRIPTION("BlackBerry Classic Q20 Display Panel Driver");
MODULE_LICENSE("GPL v2");