forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 407
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
drm: Add new driver for MXSFB controller
Add new driver for the MXSFB controller found in i.MX23/28/6SX . The MXSFB controller is a simple framebuffer controller with one parallel LCD output. Unlike the MXSFB fbdev driver that is used on these systems now, this driver uses the DRM/KMS framework. Signed-off-by: Marek Vasut <marex@denx.de> Cc: Lucas Stach <l.stach@pengutronix.de> Cc: Fabio Estevam <fabio.estevam@nxp.com> Cc: Shawn Guo <shawnguo@kernel.org>
- Loading branch information
Marek Vasut
committed
Dec 6, 2016
1 parent
7b920aa
commit 45d59d7
Showing
10 changed files
with
1,013 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
config DRM_MXS | ||
bool | ||
help | ||
Choose this option to select drivers for MXS FB devices | ||
|
||
config DRM_MXSFB | ||
tristate "i.MX23/i.MX28/i.MX6SX MXSFB LCD controller" | ||
depends on DRM && OF | ||
depends on COMMON_CLK | ||
select DRM_MXS | ||
select DRM_KMS_HELPER | ||
select DRM_KMS_FB_HELPER | ||
select DRM_KMS_CMA_HELPER | ||
help | ||
Choose this option if you have an i.MX23/i.MX28/i.MX6SX MXSFB | ||
LCD controller. | ||
|
||
If M is selected the module will be called mxsfb. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
mxsfb-y := mxsfb_drv.o mxsfb_crtc.o mxsfb_out.o | ||
obj-$(CONFIG_DRM_MXSFB) += mxsfb.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
/* | ||
* Copyright (C) 2016 Marek Vasut <marex@denx.de> | ||
* | ||
* This code is based on drivers/video/fbdev/mxsfb.c : | ||
* Copyright (C) 2010 Juergen Beisert, Pengutronix | ||
* Copyright (C) 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. | ||
* Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved. | ||
* | ||
* 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. | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
*/ | ||
|
||
#include <drm/drmP.h> | ||
#include <drm/drm_atomic_helper.h> | ||
#include <drm/drm_crtc.h> | ||
#include <drm/drm_crtc_helper.h> | ||
#include <drm/drm_fb_helper.h> | ||
#include <drm/drm_fb_cma_helper.h> | ||
#include <drm/drm_gem_cma_helper.h> | ||
#include <drm/drm_of.h> | ||
#include <drm/drm_plane_helper.h> | ||
#include <drm/drm_simple_kms_helper.h> | ||
#include <linux/clk.h> | ||
#include <linux/iopoll.h> | ||
#include <linux/of_graph.h> | ||
#include <linux/platform_data/simplefb.h> | ||
#include <video/videomode.h> | ||
|
||
#include "mxsfb_drv.h" | ||
#include "mxsfb_regs.h" | ||
|
||
static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val) | ||
{ | ||
return (val & mxsfb->devdata->hs_wdth_mask) << | ||
mxsfb->devdata->hs_wdth_shift; | ||
} | ||
|
||
/* Setup the MXSFB registers for decoding the pixels out of the framebuffer */ | ||
static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb) | ||
{ | ||
struct drm_crtc *crtc = &mxsfb->pipe.crtc; | ||
struct drm_device *drm = crtc->dev; | ||
const u32 format = crtc->primary->state->fb->pixel_format; | ||
u32 ctrl, ctrl1; | ||
|
||
ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER; | ||
|
||
/* | ||
* WARNING: The bus width, CTRL_SET_BUS_WIDTH(), is configured to | ||
* match the selected mode here. This differs from the original | ||
* MXSFB driver, which had the option to configure the bus width | ||
* to arbitrary value. This limitation should not pose an issue. | ||
*/ | ||
|
||
/* CTRL1 contains IRQ config and status bits, preserve those. */ | ||
ctrl1 = readl(mxsfb->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_BUS_WIDTH(STMLCDIF_16BIT); | ||
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_BUS_WIDTH(STMLCDIF_24BIT); | ||
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, mxsfb->base + LCDC_CTRL1); | ||
writel(ctrl, mxsfb->base + LCDC_CTRL); | ||
|
||
return 0; | ||
} | ||
|
||
static void mxsfb_enable_controller(struct mxsfb_drm_private *mxsfb) | ||
{ | ||
u32 reg; | ||
|
||
if (mxsfb->clk_disp_axi) | ||
clk_prepare_enable(mxsfb->clk_disp_axi); | ||
clk_prepare_enable(mxsfb->clk); | ||
mxsfb_enable_axi_clk(mxsfb); | ||
|
||
/* If it was disabled, re-enable the mode again */ | ||
writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_SET); | ||
|
||
/* Enable the SYNC signals first, then the DMA engine */ | ||
reg = readl(mxsfb->base + LCDC_VDCTRL4); | ||
reg |= VDCTRL4_SYNC_SIGNALS_ON; | ||
writel(reg, mxsfb->base + LCDC_VDCTRL4); | ||
|
||
writel(CTRL_RUN, mxsfb->base + LCDC_CTRL + REG_SET); | ||
} | ||
|
||
static void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb) | ||
{ | ||
u32 reg; | ||
|
||
/* | ||
* Even if we disable the controller here, it will still continue | ||
* until its FIFOs are running out of data | ||
*/ | ||
writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_CLR); | ||
|
||
readl_poll_timeout(mxsfb->base + LCDC_CTRL, reg, !(reg & CTRL_RUN), | ||
0, 1000); | ||
|
||
reg = readl(mxsfb->base + LCDC_VDCTRL4); | ||
reg &= ~VDCTRL4_SYNC_SIGNALS_ON; | ||
writel(reg, mxsfb->base + LCDC_VDCTRL4); | ||
|
||
mxsfb_disable_axi_clk(mxsfb); | ||
|
||
clk_disable_unprepare(mxsfb->clk); | ||
if (mxsfb->clk_disp_axi) | ||
clk_disable_unprepare(mxsfb->clk_disp_axi); | ||
} | ||
|
||
static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb) | ||
{ | ||
struct drm_display_mode *m = &mxsfb->pipe.crtc.state->adjusted_mode; | ||
const u32 bus_flags = mxsfb->connector.display_info.bus_flags; | ||
u32 vdctrl0, vsync_pulse_len, hsync_pulse_len; | ||
int err; | ||
|
||
/* | ||
* It seems, you can't re-program the controller if it is still | ||
* running. This may lead to shifted pictures (FIFO issue?), so | ||
* first stop the controller and drain its FIFOs. | ||
*/ | ||
mxsfb_enable_axi_clk(mxsfb); | ||
|
||
/* Clear the FIFOs */ | ||
writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET); | ||
|
||
err = mxsfb_set_pixel_fmt(mxsfb); | ||
if (err) | ||
return; | ||
|
||
clk_set_rate(mxsfb->clk, m->crtc_clock * 1000); | ||
|
||
writel(TRANSFER_COUNT_SET_VCOUNT(m->crtc_vdisplay) | | ||
TRANSFER_COUNT_SET_HCOUNT(m->crtc_hdisplay), | ||
mxsfb->base + mxsfb->devdata->transfer_count); | ||
|
||
vsync_pulse_len = m->crtc_vsync_end - m->crtc_vsync_start; | ||
|
||
vdctrl0 = VDCTRL0_ENABLE_PRESENT | /* Always in DOTCLOCK mode */ | ||
VDCTRL0_VSYNC_PERIOD_UNIT | | ||
VDCTRL0_VSYNC_PULSE_WIDTH_UNIT | | ||
VDCTRL0_SET_VSYNC_PULSE_WIDTH(vsync_pulse_len); | ||
if (m->flags & DRM_MODE_FLAG_PHSYNC) | ||
vdctrl0 |= VDCTRL0_HSYNC_ACT_HIGH; | ||
if (m->flags & DRM_MODE_FLAG_PVSYNC) | ||
vdctrl0 |= VDCTRL0_VSYNC_ACT_HIGH; | ||
if (bus_flags & DRM_BUS_FLAG_DE_HIGH) | ||
vdctrl0 |= VDCTRL0_ENABLE_ACT_HIGH; | ||
if (bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE) | ||
vdctrl0 |= VDCTRL0_DOTCLK_ACT_FALLING; | ||
|
||
writel(vdctrl0, mxsfb->base + LCDC_VDCTRL0); | ||
|
||
/* Frame length in lines. */ | ||
writel(m->crtc_vtotal, mxsfb->base + LCDC_VDCTRL1); | ||
|
||
/* Line length in units of clocks or pixels. */ | ||
hsync_pulse_len = m->crtc_hsync_end - m->crtc_hsync_start; | ||
writel(set_hsync_pulse_width(mxsfb, hsync_pulse_len) | | ||
VDCTRL2_SET_HSYNC_PERIOD(m->crtc_htotal), | ||
mxsfb->base + LCDC_VDCTRL2); | ||
|
||
writel(SET_HOR_WAIT_CNT(m->crtc_hblank_end - m->crtc_hsync_end) | | ||
SET_VERT_WAIT_CNT(m->crtc_vblank_end - m->crtc_vsync_end), | ||
mxsfb->base + LCDC_VDCTRL3); | ||
|
||
writel(SET_DOTCLK_H_VALID_DATA_CNT(m->hdisplay), | ||
mxsfb->base + LCDC_VDCTRL4); | ||
|
||
mxsfb_disable_axi_clk(mxsfb); | ||
} | ||
|
||
void mxsfb_crtc_enable(struct mxsfb_drm_private *mxsfb) | ||
{ | ||
mxsfb_crtc_mode_set_nofb(mxsfb); | ||
mxsfb_enable_controller(mxsfb); | ||
} | ||
|
||
void mxsfb_crtc_disable(struct mxsfb_drm_private *mxsfb) | ||
{ | ||
mxsfb_disable_controller(mxsfb); | ||
} | ||
|
||
void mxsfb_plane_atomic_update(struct mxsfb_drm_private *mxsfb, | ||
struct drm_plane_state *state) | ||
{ | ||
struct drm_simple_display_pipe *pipe = &mxsfb->pipe; | ||
struct drm_crtc *crtc = &pipe->crtc; | ||
struct drm_framebuffer *fb = pipe->plane.state->fb; | ||
struct drm_pending_vblank_event *event; | ||
struct drm_gem_cma_object *gem; | ||
|
||
if (!crtc) | ||
return; | ||
|
||
spin_lock_irq(&crtc->dev->event_lock); | ||
event = crtc->state->event; | ||
if (event) { | ||
crtc->state->event = NULL; | ||
|
||
if (drm_crtc_vblank_get(crtc) == 0) { | ||
drm_crtc_arm_vblank_event(crtc, event); | ||
} else { | ||
drm_crtc_send_vblank_event(crtc, event); | ||
} | ||
} | ||
spin_unlock_irq(&crtc->dev->event_lock); | ||
|
||
if (!fb) | ||
return; | ||
|
||
gem = drm_fb_cma_get_gem_obj(fb, 0); | ||
|
||
mxsfb_enable_axi_clk(mxsfb); | ||
writel(gem->paddr, mxsfb->base + mxsfb->devdata->next_buf); | ||
mxsfb_disable_axi_clk(mxsfb); | ||
} |
Oops, something went wrong.