drm: brainlcd: implement brain LCD driver
This commit is contained in:
parent
459d2f25e9
commit
4be8ba38b8
|
@ -82,19 +82,6 @@
|
|||
regulator-max-microvolt = <5000000>;
|
||||
};
|
||||
|
||||
panel {
|
||||
compatible = "sii,43wvf1g";
|
||||
backlight = <&backlight_display>;
|
||||
dvdd-supply = <®_lcd_3v3>;
|
||||
avdd-supply = <®_lcd_5v>;
|
||||
|
||||
port {
|
||||
panel_in: endpoint {
|
||||
remote-endpoint = <&display_out>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
apb@80000000 {
|
||||
apbh@80000000 {
|
||||
ssp0: spi@80010000 {
|
||||
|
@ -119,17 +106,26 @@
|
|||
|
||||
pinctrl@80018000 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&hog_pins_a>;
|
||||
pinctrl-0 = <&hog_pins_3v3_pullup &hog_pins_3v3_nopull &hog_pins_1v8_nopull>;
|
||||
|
||||
hog_pins_a: hog@0 {
|
||||
hog_pins_3v3_pullup: hog@0 {
|
||||
reg = <0>;
|
||||
fsl,pinmux-ids = <
|
||||
MX28_PAD_GPMI_CLE__GPIO_0_27
|
||||
>;
|
||||
fsl,drive-strength = <MXS_DRIVE_4mA>;
|
||||
fsl,voltage = <MXS_VOLTAGE_HIGH>;
|
||||
fsl,pull-up = <MXS_PULL_ENABLE>;
|
||||
};
|
||||
|
||||
hog_pins_3v3_nopull: hog@1 {
|
||||
reg = <1>;
|
||||
fsl,pinmux-ids = <
|
||||
MX28_PAD_SSP1_CMD__GPIO_2_13
|
||||
MX28_PAD_SSP1_DATA3__GPIO_2_15
|
||||
MX28_PAD_ENET0_RX_CLK__GPIO_4_13
|
||||
MX28_PAD_SSP1_SCK__GPIO_2_12
|
||||
MX28_PAD_PWM3__GPIO_3_28
|
||||
MX28_PAD_LCD_RESET__GPIO_3_30
|
||||
MX28_PAD_AUART2_RX__GPIO_3_8
|
||||
MX28_PAD_AUART2_TX__GPIO_3_9
|
||||
>;
|
||||
|
@ -138,10 +134,21 @@
|
|||
fsl,pull-up = <MXS_PULL_DISABLE>;
|
||||
};
|
||||
|
||||
led_pin_gpio3_5: led_gpio3_5@0 {
|
||||
hog_pins_1v8_nopull: hog@2 {
|
||||
reg = <2>;
|
||||
fsl,pinmux-ids = <
|
||||
MX28_PAD_GPMI_ALE__GPIO_0_26
|
||||
>;
|
||||
fsl,drive-strength = <MXS_DRIVE_4mA>;
|
||||
fsl,voltage = <MXS_VOLTAGE_LOW>;
|
||||
fsl,pull-up = <MXS_PULL_DISABLE>;
|
||||
};
|
||||
|
||||
lcd_backlight: pwm@0 {
|
||||
reg = <0>;
|
||||
fsl,pinmux-ids = <
|
||||
MX28_PAD_AUART1_TX__GPIO_3_5
|
||||
MX28_PAD_AUART1_RX__PWM_0
|
||||
MX28_PAD_AUART1_TX__PWM_1
|
||||
>;
|
||||
fsl,drive-strength = <MXS_DRIVE_4mA>;
|
||||
fsl,voltage = <MXS_VOLTAGE_HIGH>;
|
||||
|
@ -151,28 +158,44 @@
|
|||
lcdif_pins_evk: lcdif-evk@0 {
|
||||
reg = <0>;
|
||||
fsl,pinmux-ids = <
|
||||
MX28_PAD_LCD_RD_E__LCD_VSYNC
|
||||
MX28_PAD_LCD_WR_RWN__LCD_HSYNC
|
||||
MX28_PAD_LCD_RS__LCD_DOTCLK
|
||||
MX28_PAD_LCD_CS__LCD_ENABLE
|
||||
MX28_PAD_LCD_D00__LCD_D0
|
||||
MX28_PAD_LCD_D01__LCD_D1
|
||||
MX28_PAD_LCD_D02__LCD_D2
|
||||
MX28_PAD_LCD_D03__LCD_D3
|
||||
MX28_PAD_LCD_D04__LCD_D4
|
||||
MX28_PAD_LCD_D05__LCD_D5
|
||||
MX28_PAD_LCD_D06__LCD_D6
|
||||
MX28_PAD_LCD_D07__LCD_D7
|
||||
MX28_PAD_LCD_D08__LCD_D8
|
||||
MX28_PAD_LCD_D09__LCD_D9
|
||||
MX28_PAD_LCD_D10__LCD_D10
|
||||
MX28_PAD_LCD_D11__LCD_D11
|
||||
MX28_PAD_LCD_D12__LCD_D12
|
||||
MX28_PAD_LCD_D13__LCD_D13
|
||||
MX28_PAD_LCD_D14__LCD_D14
|
||||
MX28_PAD_LCD_D15__LCD_D15
|
||||
MX28_PAD_LCD_RD_E__LCD_RD_E
|
||||
MX28_PAD_LCD_WR_RWN__LCD_WR_RWN
|
||||
MX28_PAD_LCD_RS__LCD_RS
|
||||
MX28_PAD_LCD_CS__LCD_CS
|
||||
MX28_PAD_LCD_RESET__LCD_VSYNC
|
||||
>;
|
||||
fsl,drive-strength = <MXS_DRIVE_4mA>;
|
||||
fsl,voltage = <MXS_VOLTAGE_HIGH>;
|
||||
fsl,voltage = <MXS_VOLTAGE_LOW>;
|
||||
fsl,pull-up = <MXS_PULL_DISABLE>;
|
||||
};
|
||||
};
|
||||
|
||||
lcdif@80030000 {
|
||||
compatible = "sharp,brainlcd";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&lcdif_24bit_pins_a
|
||||
&lcdif_pins_evk>;
|
||||
pinctrl-0 = <&lcdif_pins_evk>;
|
||||
status = "okay";
|
||||
|
||||
port {
|
||||
display_out: endpoint {
|
||||
remote-endpoint = <&panel_in>;
|
||||
};
|
||||
};
|
||||
sharp,en-gpios =
|
||||
<&gpio0 26 GPIO_ACTIVE_HIGH>,
|
||||
<&gpio0 27 GPIO_ACTIVE_HIGH>,
|
||||
<&gpio4 16 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -221,8 +244,8 @@
|
|||
|
||||
pwm: pwm@80064000 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pwm2_pins_a>;
|
||||
status = "disabled";
|
||||
pinctrl-0 = <&lcd_backlight>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
duart: serial@80074000 {
|
||||
|
@ -246,6 +269,7 @@
|
|||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&usb0_id_pins_a>;
|
||||
vbus-supply = <®_usb0_vbus>;
|
||||
dr_mode = "host";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
|
@ -263,20 +287,9 @@
|
|||
audio-codec = <&sgtl5000>;
|
||||
};
|
||||
|
||||
leds {
|
||||
compatible = "gpio-leds";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&led_pin_gpio3_5>;
|
||||
|
||||
backlight_static {
|
||||
gpios = <&gpio3 5 0>;
|
||||
default-state = "on";
|
||||
};
|
||||
};
|
||||
|
||||
backlight_display: backlight {
|
||||
compatible = "pwm-backlight";
|
||||
pwms = <&pwm 0 5000000>;
|
||||
pwms = <&pwm 0 50000>, <&pwm 1 50000>;
|
||||
brightness-levels = <0 4 8 16 32 64 128 255>;
|
||||
default-brightness-level = <4>; //Set 4 for identification on probing
|
||||
};
|
||||
|
|
|
@ -47,6 +47,18 @@ config TINYDRM_ILI9341
|
|||
|
||||
If M is selected the module will be called ili9341.
|
||||
|
||||
config TINYDRM_BRAIN
|
||||
tristate "DRM support for ILI9805 display panels on Sharp Brain"
|
||||
depends on DRM
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_KMS_CMA_HELPER
|
||||
select BACKLIGHT_CLASS_DEVICE
|
||||
help
|
||||
DRM driver for the following Ilitek ILI9805 panels:
|
||||
* Sharp Brain 800x480 TFT
|
||||
|
||||
If M is selected the module will be called brain.
|
||||
|
||||
config TINYDRM_MI0283QT
|
||||
tristate "DRM support for MI0283QT"
|
||||
depends on DRM && SPI
|
||||
|
|
|
@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_GM12U320) += gm12u320.o
|
|||
obj-$(CONFIG_TINYDRM_HX8357D) += hx8357d.o
|
||||
obj-$(CONFIG_TINYDRM_ILI9225) += ili9225.o
|
||||
obj-$(CONFIG_TINYDRM_ILI9341) += ili9341.o
|
||||
obj-$(CONFIG_TINYDRM_BRAIN) += brain.o
|
||||
obj-$(CONFIG_TINYDRM_MI0283QT) += mi0283qt.o
|
||||
obj-$(CONFIG_TINYDRM_REPAPER) += repaper.o
|
||||
obj-$(CONFIG_TINYDRM_ST7586) += st7586.o
|
||||
|
|
|
@ -0,0 +1,586 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* DRM driver for Ilitek ILI9805 panels on Sharp Brain
|
||||
*
|
||||
* Copyright 2020 Takumi Sueda <puhitaku@gmail.com>
|
||||
*
|
||||
* Based on ili9341.c
|
||||
* Copyright 2018 David Lechner <david@lechnology.com>
|
||||
*
|
||||
* Based on mi0283qt.c:
|
||||
* Copyright 2016 Noralf Trønnes
|
||||
*/
|
||||
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_damage_helper.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_mipi_dbi.h>
|
||||
#include <drm/drm_modeset_helper.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
#include <video/mipi_display.h>
|
||||
|
||||
#include "mxsfb_regs.h"
|
||||
|
||||
#define RESET_TIMEOUT 1000000
|
||||
#define TX_TIMEOUT 1000000
|
||||
|
||||
#define ILI9805_FRMCTR1 0xb1
|
||||
#define ILI9805_DISCTRL 0xb6
|
||||
#define ILI9805_ETMOD 0xb7
|
||||
|
||||
#define ILI9805_PWCTRL1 0xc0
|
||||
#define ILI9805_PWCTRL2 0xc1
|
||||
#define ILI9805_VMCTRL1 0xc5
|
||||
#define ILI9805_VMCTRL2 0xc7
|
||||
#define ILI9805_PWCTRLA 0xcb
|
||||
#define ILI9805_PWCTRLB 0xcf
|
||||
|
||||
#define ILI9805_PGAMCTRL 0xe0
|
||||
#define ILI9805_NGAMCTRL 0xe1
|
||||
#define ILI9805_DTCTRLA 0xe8
|
||||
#define ILI9805_DTCTRLB 0xea
|
||||
#define ILI9805_PWRSEQ 0xed
|
||||
|
||||
#define ILI9805_EN3GAM 0xf2
|
||||
#define ILI9805_PUMPCTRL 0xf7
|
||||
|
||||
#define ILI9805_MADCTL_BGR BIT(3)
|
||||
#define ILI9805_MADCTL_MV BIT(5)
|
||||
#define ILI9805_MADCTL_MX BIT(6)
|
||||
#define ILI9805_MADCTL_MY BIT(7)
|
||||
|
||||
struct brain_drm_private {
|
||||
void __iomem *base;
|
||||
struct platform_device *pdev;
|
||||
struct clk *clk_lcdif;
|
||||
bool enabled;
|
||||
|
||||
struct drm_simple_display_pipe pipe;
|
||||
const struct drm_display_mode *mode;
|
||||
struct drm_connector connector;
|
||||
struct drm_panel *panel;
|
||||
};
|
||||
|
||||
static const struct {
|
||||
u8 payload;
|
||||
u8 data;
|
||||
u32 delay;
|
||||
} lcd_regs[] = {
|
||||
{ 0xff, 0, 0 }, /* EXTC Command Set Enable */
|
||||
{ 0xff, 1, 0 }, { 0x98, 1, 0 }, { 0x05, 1, 0 },
|
||||
{ 0xfd, 0, 0 }, /* PFM Type C */
|
||||
{ 0x03, 1, 0 }, { 0x13, 1, 0 }, { 0x44, 1, 0 }, { 0x00, 1, 0 },
|
||||
{ 0xf8, 0, 0 }, /* PFM Type C */
|
||||
{ 0x18, 1, 0 }, { 0x02, 1, 0 }, { 0x02, 1, 0 }, { 0x18, 1, 0 },
|
||||
{ 0x02, 1, 0 }, { 0x02, 1, 0 }, { 0x30, 1, 0 }, { 0x01, 1, 0 },
|
||||
{ 0x01, 1, 0 }, { 0x30, 1, 0 }, { 0x01, 1, 0 }, { 0x01, 1, 0 },
|
||||
{ 0x30, 1, 0 }, { 0x01, 1, 0 }, { 0x01, 1, 0 },
|
||||
{ 0xb8, 0, 0 }, /* DBI Type B Interface Setting */
|
||||
{ 0x72, 1, 0 },
|
||||
{ 0xf1, 0, 0 }, /* Gate Modulation */
|
||||
{ 0x00, 1, 0 },
|
||||
{ 0xf2, 0, 0 }, /* CR/EQ/PC */
|
||||
{ 0x00, 1, 0 }, { 0x58, 1, 0 }, { 0x40, 1, 0 },
|
||||
{ 0xfc, 0, 0 }, /* LVGL Voltage Setting? */
|
||||
{ 0x04, 1, 0 }, { 0x0f, 1, 0 }, { 0x01, 1, 0 },
|
||||
{ 0xeb, 0, 0 }, /* ? */
|
||||
{ 0x08, 1, 0 }, { 0x0f, 1, 0 },
|
||||
{ 0xe0, 0, 0 }, /* Positive Gamma Control */
|
||||
{ 0x0a, 1, 0 }, { 0x23, 1, 0 }, { 0x35, 1, 0 }, { 0x15, 1, 0 },
|
||||
{ 0x13, 1, 0 }, { 0x16, 1, 0 }, { 0x0a, 1, 0 }, { 0x06, 1, 0 },
|
||||
{ 0x03, 1, 0 }, { 0x06, 1, 0 }, { 0x05, 1, 0 }, { 0x0a, 1, 0 },
|
||||
{ 0x08, 1, 0 }, { 0x23, 1, 0 }, { 0x1a, 1, 0 }, { 0x00, 1, 0 },
|
||||
{ 0xe1, 0, 0 }, /* Negative Gamma Control */
|
||||
{ 0x0a, 1, 0 }, { 0x23, 1, 0 }, { 0x28, 1, 0 }, { 0x10, 1, 0 },
|
||||
{ 0x11, 1, 0 }, { 0x16, 1, 0 }, { 0x0b, 1, 0 }, { 0x0a, 1, 0 },
|
||||
{ 0x02, 1, 0 }, { 0x05, 1, 0 }, { 0x04, 1, 0 }, { 0x0a, 1, 0 },
|
||||
{ 0x08, 1, 0 }, { 0x1d, 1, 0 }, { 0x1a, 1, 0 }, { 0x00, 1, 0 },
|
||||
{ 0xc1, 0, 0 }, /* Power Control 1 */
|
||||
{ 0x13, 1, 0 }, { 0x28, 1, 0 }, { 0x08, 1, 0 }, { 0x26, 1, 0 },
|
||||
{ 0xc7, 0, 0 }, /* VCOM Control */
|
||||
{ 0x90, 1, 0 },
|
||||
{ 0xb1, 0, 0 }, /* Frame Rate Control */
|
||||
{ 0x00, 1, 0 }, { 0x12, 1, 0 }, { 0x14, 1, 0 },
|
||||
{ 0xb4, 0, 0 }, /* Display Inversion Control */
|
||||
{ 0x02, 1, 0 },
|
||||
{ 0xbb, 0, 0 }, /* ? */
|
||||
{ 0x14, 1, 0 }, { 0x55, 1, 0 },
|
||||
{ 0x36, 0, 0 }, /* Memory Access Control */
|
||||
{ 0x28, 1, 0 },
|
||||
{ 0x3a, 0, 0 }, /* Interface Pixel Format */
|
||||
{ 0x55, 1, 0 },
|
||||
{ 0x21, 0, 0 }, /* Display Inversion On */
|
||||
{ 0xb6, 0, 0 }, /* MCU/RGB Interface Select */
|
||||
{ 0x01, 1, 0 }, { 0x80, 1, 0 }, { 0x8f, 1, 0 },
|
||||
{ 0x44, 0, 0 }, /* Write Tear Scan Line? */
|
||||
{ 0x00, 1, 0 }, { 0x00, 1, 0 },
|
||||
{ 0x35, 0, 0 }, /* Tearing Effect Line On */
|
||||
{ 0x00, 1, 0 },
|
||||
{ 0x11, 0, 120 }, /* Sleep Out */
|
||||
{ 0x29, 0, 20 }, /* Display On */
|
||||
{ 0x2a, 0, 0 }, /* Column Address Set */
|
||||
{ 0x00, 1, 0 }, { 0x00, 1, 0 }, { 0x03, 1, 0 }, { 0x1f, 1, 0 },
|
||||
{ 0x2b, 0, 0 }, /* Page Address Set */
|
||||
{ 0x00, 1, 0 }, { 0x00, 1, 0 }, { 0x01, 1, 0 }, { 0xdf, 1, 0 },
|
||||
{ 0x2c, 0, 0 }, /* Memory Write*/
|
||||
};
|
||||
|
||||
|
||||
static struct brain_drm_private *pipe_to_brain_drm_private(struct drm_simple_display_pipe *pipe)
|
||||
{
|
||||
return container_of(pipe, struct brain_drm_private, pipe);
|
||||
}
|
||||
|
||||
static int brain_clear_poll(const void __iomem *addr, u32 mask, u32 timeout)
|
||||
{
|
||||
u32 reg;
|
||||
return readl_poll_timeout(addr, reg, !(reg & mask), 0, timeout);
|
||||
}
|
||||
|
||||
static int brain_write_clear_poll(void __iomem *addr, u32 mask, u32 timeout)
|
||||
{
|
||||
u32 reg;
|
||||
writel(mask, addr + REG_CLR);
|
||||
return readl_poll_timeout(addr, reg, !(reg & mask), 0, timeout);
|
||||
}
|
||||
|
||||
static int brain_write_byte(struct brain_drm_private *priv, u8 payload, const u8 is_data)
|
||||
{
|
||||
|
||||
if (brain_clear_poll(priv->base + LCDC_CTRL, CTRL_RUN, TX_TIMEOUT)) {
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
writel(TRANSFER_COUNT_SET_VCOUNT(1) | TRANSFER_COUNT_SET_HCOUNT(1),
|
||||
priv->base + LCDC_V4_TRANSFER_COUNT);
|
||||
|
||||
writel(CTRL_DATA_SELECT | CTRL_RUN, priv->base + LCDC_CTRL + REG_CLR);
|
||||
|
||||
if (is_data) {
|
||||
writel(CTRL_DATA_SELECT, priv->base + LCDC_CTRL + REG_SET);
|
||||
}
|
||||
|
||||
writel(CTRL_RUN, priv->base + LCDC_CTRL + REG_SET);
|
||||
|
||||
if (brain_clear_poll(priv->base + LCDC_STAT, STAT_LFIFO_FULL, TX_TIMEOUT)) {
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
writel(payload, priv->base + LCDC_DATA);
|
||||
|
||||
if (brain_clear_poll(priv->base + LCDC_CTRL, CTRL_RUN, TX_TIMEOUT)) {
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int brain_reset_block(void __iomem *reset_addr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = brain_write_clear_poll(reset_addr, CTRL_SFTRST, RESET_TIMEOUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(CTRL_CLKGATE, reset_addr + REG_CLR);
|
||||
|
||||
ret = brain_write_clear_poll(reset_addr, CTRL_SFTRST, RESET_TIMEOUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return brain_write_clear_poll(reset_addr, CTRL_CLKGATE, RESET_TIMEOUT);
|
||||
}
|
||||
|
||||
static int brain_set_pixel_fmt(struct brain_drm_private *ili, struct drm_plane_state *plane_state)
|
||||
{
|
||||
struct drm_crtc *crtc = &ili->pipe.crtc;
|
||||
struct drm_device *drm = crtc->dev;
|
||||
const u32 format = plane_state->fb->format->format;
|
||||
u32 ctrl, ctrl1;
|
||||
|
||||
ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER;
|
||||
|
||||
/* CTRL1 contains IRQ config and status bits, preserve those. */
|
||||
ctrl1 = readl(ili->base + LCDC_CTRL1);
|
||||
ctrl1 &= CTRL1_CUR_FRAME_DONE_IRQ_EN | CTRL1_CUR_FRAME_DONE_IRQ;
|
||||
|
||||
switch (format) {
|
||||
case DRM_FORMAT_RGB565:
|
||||
dev_dbg(drm->dev, "Setting up RGB565 mode\n");
|
||||
ctrl |= CTRL_SET_WORD_LENGTH(0);
|
||||
ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0xf);
|
||||
break;
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
dev_dbg(drm->dev, "Setting up XRGB8888 mode\n");
|
||||
ctrl |= CTRL_SET_WORD_LENGTH(3);
|
||||
/* Do not use packed pixels = one pixel per word instead. */
|
||||
ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0x7);
|
||||
break;
|
||||
default:
|
||||
dev_err(drm->dev, "Unhandled pixel format %08x\n", format);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(ctrl1, ili->base + LCDC_CTRL1);
|
||||
writel(ctrl, ili->base + LCDC_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void brain_set_bus_fmt(struct brain_drm_private *ili)
|
||||
{
|
||||
struct drm_crtc *crtc = &ili->pipe.crtc;
|
||||
struct drm_device *drm = crtc->dev;
|
||||
u32 bus_format = MEDIA_BUS_FMT_RGB565_1X16;
|
||||
u32 reg;
|
||||
|
||||
reg = readl(ili->base + LCDC_CTRL);
|
||||
|
||||
if (ili->connector.display_info.num_bus_formats)
|
||||
bus_format = ili->connector.display_info.bus_formats[0];
|
||||
|
||||
reg &= ~CTRL_BUS_WIDTH_MASK;
|
||||
switch (bus_format) {
|
||||
case MEDIA_BUS_FMT_RGB565_1X16:
|
||||
reg |= CTRL_SET_BUS_WIDTH(STMLCDIF_16BIT);
|
||||
break;
|
||||
case MEDIA_BUS_FMT_RGB666_1X18:
|
||||
reg |= CTRL_SET_BUS_WIDTH(STMLCDIF_18BIT);
|
||||
break;
|
||||
case MEDIA_BUS_FMT_RGB888_1X24:
|
||||
reg |= CTRL_SET_BUS_WIDTH(STMLCDIF_24BIT);
|
||||
break;
|
||||
default:
|
||||
dev_err(drm->dev, "Unknown media bus format %d\n", bus_format);
|
||||
break;
|
||||
}
|
||||
writel(reg, ili->base + LCDC_CTRL);
|
||||
}
|
||||
|
||||
|
||||
static void brain_enable(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_plane_state *plane_state)
|
||||
{
|
||||
struct brain_drm_private *ili;
|
||||
struct drm_display_mode *m = &crtc_state->adjusted_mode;
|
||||
int i, ret, idx;
|
||||
u32 valid;
|
||||
|
||||
ili = pipe_to_brain_drm_private(pipe);
|
||||
m = &ili->pipe.crtc.state->adjusted_mode;
|
||||
|
||||
if (!drm_dev_enter(pipe->crtc.dev, &idx))
|
||||
return;
|
||||
|
||||
clk_prepare_enable(ili->clk_lcdif);
|
||||
|
||||
ret = brain_reset_block(ili->base);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
writel(CTRL1_FIFO_CLEAR, ili->base + LCDC_CTRL1 + REG_SET);
|
||||
|
||||
ret = brain_set_pixel_fmt(ili, plane_state);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
/* Unset DOTCLK */
|
||||
writel(CTRL_MASTER | CTRL_DOTCLK_MODE | CTRL_BYPASS_COUNT, ili->base + LCDC_CTRL + REG_CLR);
|
||||
|
||||
brain_set_bus_fmt(ili);
|
||||
|
||||
/* Timing */
|
||||
writel(TIMING_CMD_HOLD(1) | TIMING_CMD_SETUP(1) | TIMING_DATA_HOLD(1) | TIMING_DATA_SETUP(1),
|
||||
ili->base + LCDC_TIMING);
|
||||
|
||||
/* Initialize LCD */
|
||||
/* Decrease the bus width to 8-bit temporarily */
|
||||
valid = CTRL1_GET_BYTE_PACKAGING(readl(ili->base + LCDC_CTRL1));
|
||||
writel(CTRL1_SET_BYTE_PACKAGING(0xf), ili->base + LCDC_CTRL1 + REG_CLR);
|
||||
writel(CTRL1_SET_BYTE_PACKAGING(0x3), ili->base + LCDC_CTRL1 + REG_SET);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(lcd_regs); i++) {
|
||||
brain_write_byte(ili, lcd_regs[i].payload, lcd_regs[i].data);
|
||||
mdelay(lcd_regs[i].delay);
|
||||
}
|
||||
|
||||
writel(CTRL1_SET_BYTE_PACKAGING(0xf), ili->base + LCDC_CTRL1 + REG_CLR);
|
||||
writel(CTRL1_SET_BYTE_PACKAGING(valid), ili->base + LCDC_CTRL1 + REG_SET);
|
||||
|
||||
/* Ready to transmit */
|
||||
writel(CTRL_MASTER, ili->base + LCDC_CTRL + REG_SET);
|
||||
ili->enabled = true;
|
||||
}
|
||||
|
||||
static void brain_disable(struct drm_simple_display_pipe *pipe)
|
||||
{
|
||||
struct brain_drm_private *ili = pipe_to_brain_drm_private(pipe);
|
||||
|
||||
clk_disable_unprepare(ili->clk_lcdif);
|
||||
return;
|
||||
}
|
||||
|
||||
static void brain_fb_dirty_full(struct brain_drm_private *ili, struct drm_framebuffer *fb, struct drm_rect *rect) {
|
||||
struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
int idx;
|
||||
|
||||
u16 width = fb->width;
|
||||
u16 height = fb->height;
|
||||
|
||||
if (!ili->enabled)
|
||||
return;
|
||||
|
||||
if (!drm_dev_enter(fb->dev, &idx))
|
||||
return;
|
||||
|
||||
if (brain_clear_poll(ili->base + LCDC_CTRL, CTRL_RUN, TX_TIMEOUT)) {
|
||||
dev_warn(&ili->pdev->dev, "Exceeded timeout\n");
|
||||
goto timeout_exit;
|
||||
}
|
||||
|
||||
writel(cma_obj->paddr, ili->base + LCDC_V4_CUR_BUF);
|
||||
writel(cma_obj->paddr, ili->base + LCDC_V4_NEXT_BUF);
|
||||
writel(TRANSFER_COUNT_SET_VCOUNT(height) | TRANSFER_COUNT_SET_HCOUNT(width),
|
||||
ili->base + LCDC_V4_TRANSFER_COUNT);
|
||||
writel(CTRL_DATA_SELECT | CTRL_RUN, ili->base + LCDC_CTRL + REG_SET);
|
||||
|
||||
timeout_exit:
|
||||
drm_dev_exit(idx);
|
||||
}
|
||||
|
||||
static void brain_update(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_plane_state *old_state)
|
||||
{
|
||||
struct brain_drm_private *ili = pipe_to_brain_drm_private(pipe);
|
||||
struct drm_plane_state *state = pipe->plane.state;
|
||||
struct drm_crtc *crtc = &pipe->crtc;
|
||||
struct drm_rect rect;
|
||||
|
||||
if (drm_atomic_helper_damage_merged(old_state, state, &rect))
|
||||
brain_fb_dirty_full(ili, state->fb, &rect);
|
||||
|
||||
if (crtc->state->event) {
|
||||
spin_lock_irq(&crtc->dev->event_lock);
|
||||
drm_crtc_send_vblank_event(crtc, crtc->state->event);
|
||||
spin_unlock_irq(&crtc->dev->event_lock);
|
||||
crtc->state->event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct drm_simple_display_pipe_funcs brain_pipe_funcs = {
|
||||
.enable = brain_enable,
|
||||
.disable = brain_disable,
|
||||
.update = brain_update,
|
||||
.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode brainlcd_mode = {
|
||||
DRM_SIMPLE_MODE(800, 480, 112, 67),
|
||||
};
|
||||
|
||||
DEFINE_DRM_GEM_CMA_FOPS(brain_fops);
|
||||
|
||||
static int brain_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
mode = drm_mode_duplicate(connector->dev, &brainlcd_mode);
|
||||
if (!mode) {
|
||||
DRM_ERROR("Failed to duplicate mode\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mode->name[0] == '\0')
|
||||
drm_mode_set_name(mode);
|
||||
|
||||
mode->type |= DRM_MODE_TYPE_PREFERRED;
|
||||
drm_mode_probed_add(connector, mode);
|
||||
|
||||
if (mode->width_mm) {
|
||||
connector->display_info.width_mm = mode->width_mm;
|
||||
connector->display_info.height_mm = mode->height_mm;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs brain_connector_helper_funcs = {
|
||||
.get_modes = brain_connector_get_modes,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs brain_connector_funcs = {
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static const struct drm_mode_config_funcs brain_mode_config_funcs = {
|
||||
.fb_create = drm_gem_fb_create_with_dirty,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
};
|
||||
|
||||
static struct drm_driver brain_driver = {
|
||||
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
|
||||
.fops = &brain_fops,
|
||||
DRM_GEM_CMA_VMAP_DRIVER_OPS,
|
||||
.name = "brain",
|
||||
.desc = "Ilitek ILI9805",
|
||||
.date = "20200829",
|
||||
.major = 1,
|
||||
.minor = 0,
|
||||
};
|
||||
|
||||
static const struct of_device_id brain_of_match[] = {
|
||||
{ .compatible = "sharp,brainlcd" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, brain_of_match);
|
||||
|
||||
static int brain_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct drm_device *drm;
|
||||
struct brain_drm_private *ili;
|
||||
size_t bufsize;
|
||||
struct resource *res;
|
||||
struct gpio_desc *en;
|
||||
int i, ret;
|
||||
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(brain_of_match, &pdev->dev);
|
||||
|
||||
static const uint32_t formats[] = {
|
||||
DRM_FORMAT_RGB565,
|
||||
};
|
||||
|
||||
if (!pdev->dev.of_node)
|
||||
return -ENODEV;
|
||||
|
||||
if (of_id)
|
||||
pdev->id_entry = of_id->data;
|
||||
|
||||
drm = drm_dev_alloc(&brain_driver, &pdev->dev);
|
||||
if (IS_ERR(drm))
|
||||
return PTR_ERR(drm);
|
||||
|
||||
ili = devm_kzalloc(&pdev->dev, sizeof(*ili), GFP_KERNEL);
|
||||
if (!ili) {
|
||||
drm_dev_put(drm);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ili->pdev = pdev;
|
||||
drm->dev_private = ili;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
ili->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(ili->base)) {
|
||||
drm_dev_put(drm);
|
||||
return PTR_ERR(ili->base);
|
||||
}
|
||||
|
||||
ili->enabled = false;
|
||||
|
||||
ili->clk_lcdif = devm_clk_get(drm->dev, NULL);
|
||||
if (IS_ERR(ili->clk_lcdif)) {
|
||||
drm_dev_put(drm);
|
||||
return PTR_ERR(ili->clk_lcdif);
|
||||
}
|
||||
|
||||
drm_mode_config_init(drm);
|
||||
drm->mode_config.funcs = &brain_mode_config_funcs;
|
||||
|
||||
for (i = 0; i < gpiod_count(&pdev->dev, "sharp,en"); i++) {
|
||||
en = devm_gpiod_get_index(&pdev->dev, "sharp,en", i, GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(en))
|
||||
dev_err(&pdev->dev, "failed to get gpio %d\n", i);
|
||||
continue;
|
||||
ret = gpiod_direction_output(en, 1);
|
||||
if (IS_ERR(en))
|
||||
dev_err(&pdev->dev, "failed to output gpio %d\n", i);
|
||||
}
|
||||
|
||||
bufsize = brainlcd_mode.vdisplay * brainlcd_mode.hdisplay * sizeof(u16);
|
||||
|
||||
ili->mode = &brainlcd_mode;
|
||||
drm->mode_config.min_width = brainlcd_mode.hdisplay;
|
||||
drm->mode_config.max_width = brainlcd_mode.hdisplay;
|
||||
drm->mode_config.min_height = brainlcd_mode.vdisplay;
|
||||
drm->mode_config.max_height = brainlcd_mode.vdisplay;
|
||||
|
||||
drm_connector_helper_add(&ili->connector, &brain_connector_helper_funcs);
|
||||
ret = drm_connector_init(drm, &ili->connector, &brain_connector_funcs, DRM_MODE_CONNECTOR_DPI);
|
||||
if (ret) {
|
||||
drm_dev_put(drm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = drm_simple_display_pipe_init(drm, &ili->pipe, &brain_pipe_funcs,
|
||||
formats, ARRAY_SIZE(formats),
|
||||
NULL, &ili->connector);
|
||||
if (ret) {
|
||||
drm_dev_put(drm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_mode_config_reset(drm);
|
||||
|
||||
ret = drm_dev_register(drm, 0);
|
||||
if (ret) {
|
||||
drm_dev_put(drm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, drm);
|
||||
drm_fbdev_generic_setup(drm, 16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int brain_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct drm_device *drm = platform_get_drvdata(pdev);
|
||||
|
||||
drm_dev_unplug(drm);
|
||||
drm_atomic_helper_shutdown(drm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void brain_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
drm_atomic_helper_shutdown(platform_get_drvdata(pdev));
|
||||
}
|
||||
|
||||
static struct platform_driver brain_platform_driver = {
|
||||
.probe = brain_probe,
|
||||
.remove = brain_remove,
|
||||
.shutdown = brain_shutdown,
|
||||
.driver = {
|
||||
.name = "brain",
|
||||
.of_match_table = brain_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(brain_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("DRM driver for Ilitek ILI9805 panels on Sharp Brain");
|
||||
MODULE_AUTHOR("Takumi Sueda <puhitaku@gmail.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,117 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2010 Juergen Beisert, Pengutronix
|
||||
* Copyright (C) 2016 Marek Vasut <marex@denx.de>
|
||||
*
|
||||
* i.MX23/i.MX28/i.MX6SX MXSFB LCD controller driver.
|
||||
*/
|
||||
|
||||
#ifndef __MXSFB_REGS_H__
|
||||
#define __MXSFB_REGS_H__
|
||||
|
||||
#define REG_SET 4
|
||||
#define REG_CLR 8
|
||||
|
||||
#define LCDC_CTRL 0x00
|
||||
#define LCDC_CTRL1 0x10
|
||||
#define LCDC_V3_TRANSFER_COUNT 0x20
|
||||
#define LCDC_V4_TRANSFER_COUNT 0x30
|
||||
#define LCDC_V4_CUR_BUF 0x40
|
||||
#define LCDC_V4_NEXT_BUF 0x50
|
||||
#define LCDC_V3_CUR_BUF 0x30
|
||||
#define LCDC_V3_NEXT_BUF 0x40
|
||||
#define LCDC_TIMING 0x60
|
||||
#define LCDC_VDCTRL0 0x70
|
||||
#define LCDC_VDCTRL1 0x80
|
||||
#define LCDC_VDCTRL2 0x90
|
||||
#define LCDC_VDCTRL3 0xa0
|
||||
#define LCDC_VDCTRL4 0xb0
|
||||
#define LCDC_DATA 0x180
|
||||
#define LCDC_STAT 0x1b0
|
||||
#define LCDC_V4_DEBUG0 0x1d0
|
||||
#define LCDC_V3_DEBUG0 0x1f0
|
||||
|
||||
#define CTRL_SFTRST (1 << 31)
|
||||
#define CTRL_CLKGATE (1 << 30)
|
||||
#define CTRL_BYPASS_COUNT (1 << 19)
|
||||
#define CTRL_VSYNC_MODE (1 << 18)
|
||||
#define CTRL_DOTCLK_MODE (1 << 17)
|
||||
#define CTRL_DATA_SELECT (1 << 16)
|
||||
#define CTRL_SET_BUS_WIDTH(x) (((x) & 0x3) << 10)
|
||||
#define CTRL_GET_BUS_WIDTH(x) (((x) >> 10) & 0x3)
|
||||
#define CTRL_BUS_WIDTH_MASK (0x3 << 10)
|
||||
#define CTRL_SET_WORD_LENGTH(x) (((x) & 0x3) << 8)
|
||||
#define CTRL_GET_WORD_LENGTH(x) (((x) >> 8) & 0x3)
|
||||
#define CTRL_MASTER (1 << 5)
|
||||
#define CTRL_DF16 (1 << 3)
|
||||
#define CTRL_DF18 (1 << 2)
|
||||
#define CTRL_DF24 (1 << 1)
|
||||
#define CTRL_RUN (1 << 0)
|
||||
|
||||
#define CTRL1_FIFO_CLEAR (1 << 21)
|
||||
#define CTRL1_SET_BYTE_PACKAGING(x) (((x) & 0xf) << 16)
|
||||
#define CTRL1_GET_BYTE_PACKAGING(x) (((x) >> 16) & 0xf)
|
||||
#define CTRL1_CUR_FRAME_DONE_IRQ_EN (1 << 13)
|
||||
#define CTRL1_CUR_FRAME_DONE_IRQ (1 << 9)
|
||||
|
||||
#define TRANSFER_COUNT_SET_VCOUNT(x) (((x) & 0xffff) << 16)
|
||||
#define TRANSFER_COUNT_GET_VCOUNT(x) (((x) >> 16) & 0xffff)
|
||||
#define TRANSFER_COUNT_SET_HCOUNT(x) ((x) & 0xffff)
|
||||
#define TRANSFER_COUNT_GET_HCOUNT(x) ((x) & 0xffff)
|
||||
|
||||
#define VDCTRL0_ENABLE_PRESENT (1 << 28)
|
||||
#define VDCTRL0_VSYNC_ACT_HIGH (1 << 27)
|
||||
#define VDCTRL0_HSYNC_ACT_HIGH (1 << 26)
|
||||
#define VDCTRL0_DOTCLK_ACT_FALLING (1 << 25)
|
||||
#define VDCTRL0_ENABLE_ACT_HIGH (1 << 24)
|
||||
#define VDCTRL0_VSYNC_PERIOD_UNIT (1 << 21)
|
||||
#define VDCTRL0_VSYNC_PULSE_WIDTH_UNIT (1 << 20)
|
||||
#define VDCTRL0_HALF_LINE (1 << 19)
|
||||
#define VDCTRL0_HALF_LINE_MODE (1 << 18)
|
||||
#define VDCTRL0_SET_VSYNC_PULSE_WIDTH(x) ((x) & 0x3ffff)
|
||||
#define VDCTRL0_GET_VSYNC_PULSE_WIDTH(x) ((x) & 0x3ffff)
|
||||
|
||||
#define VDCTRL2_SET_HSYNC_PERIOD(x) ((x) & 0x3ffff)
|
||||
#define VDCTRL2_GET_HSYNC_PERIOD(x) ((x) & 0x3ffff)
|
||||
|
||||
#define VDCTRL3_MUX_SYNC_SIGNALS (1 << 29)
|
||||
#define VDCTRL3_VSYNC_ONLY (1 << 28)
|
||||
#define SET_HOR_WAIT_CNT(x) (((x) & 0xfff) << 16)
|
||||
#define GET_HOR_WAIT_CNT(x) (((x) >> 16) & 0xfff)
|
||||
#define SET_VERT_WAIT_CNT(x) ((x) & 0xffff)
|
||||
#define GET_VERT_WAIT_CNT(x) ((x) & 0xffff)
|
||||
|
||||
#define VDCTRL4_SET_DOTCLK_DLY(x) (((x) & 0x7) << 29) /* v4 only */
|
||||
#define VDCTRL4_GET_DOTCLK_DLY(x) (((x) >> 29) & 0x7) /* v4 only */
|
||||
#define VDCTRL4_SYNC_SIGNALS_ON (1 << 18)
|
||||
#define SET_DOTCLK_H_VALID_DATA_CNT(x) ((x) & 0x3ffff)
|
||||
|
||||
#define STAT_LFIFO_FULL (1 << 29)
|
||||
|
||||
#define DEBUG0_HSYNC (1 < 26)
|
||||
#define DEBUG0_VSYNC (1 < 25)
|
||||
|
||||
#define MXSFB_MIN_XRES 120
|
||||
#define MXSFB_MIN_YRES 120
|
||||
#define MXSFB_MAX_XRES 0xffff
|
||||
#define MXSFB_MAX_YRES 0xffff
|
||||
|
||||
#define RED 0
|
||||
#define GREEN 1
|
||||
#define BLUE 2
|
||||
#define TRANSP 3
|
||||
|
||||
#define STMLCDIF_8BIT 1 /* pixel data bus to the display is of 8 bit width */
|
||||
#define STMLCDIF_16BIT 0 /* pixel data bus to the display is of 16 bit width */
|
||||
#define STMLCDIF_18BIT 2 /* pixel data bus to the display is of 18 bit width */
|
||||
#define STMLCDIF_24BIT 3 /* pixel data bus to the display is of 24 bit width */
|
||||
|
||||
#define MXSFB_SYNC_DATA_ENABLE_HIGH_ACT (1 << 6)
|
||||
#define MXSFB_SYNC_DOTCLK_FALLING_ACT (1 << 7) /* negative edge sampling */
|
||||
|
||||
#define TIMING_CMD_HOLD(x) ((x) << 24)
|
||||
#define TIMING_CMD_SETUP(x) ((x) << 16)
|
||||
#define TIMING_DATA_HOLD(x) ((x) << 8)
|
||||
#define TIMING_DATA_SETUP(x) ((x) << 0)
|
||||
|
||||
#endif /* __MXSFB_REGS_H__ */
|
Loading…
Reference in New Issue