summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorStephen Rothwell <sfr@canb.auug.org.au>2014-05-16 12:58:17 +1000
committerStephen Rothwell <sfr@canb.auug.org.au>2014-05-16 12:58:17 +1000
commit6d1404aafcd709e648054ccc4b689a9e86e05640 (patch)
tree767247b17f189d11ed20d851f88e1811b0b03aaf /drivers
parent633388c7b579684d0d567e6d095ef4bd26e2210e (diff)
parentf00e104dc1650e4493780b0e6f55fcbd85e2e406 (diff)
Merge remote-tracking branch 'drm-tegra/drm/tegra/for-next'
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/tegra/dc.c536
-rw-r--r--drivers/gpu/drm/tegra/dc.h3
-rw-r--r--drivers/gpu/drm/tegra/drm.c1
-rw-r--r--drivers/gpu/drm/tegra/drm.h54
-rw-r--r--drivers/gpu/drm/tegra/dsi.c244
-rw-r--r--drivers/gpu/drm/tegra/dsi.h10
-rw-r--r--drivers/gpu/drm/tegra/gem.c3
-rw-r--r--drivers/gpu/drm/tegra/hdmi.c196
-rw-r--r--drivers/gpu/drm/tegra/hdmi.h5
-rw-r--r--drivers/gpu/drm/tegra/rgb.c31
-rw-r--r--drivers/gpu/drm/tegra/sor.c192
-rw-r--r--drivers/gpu/drm/tegra/sor.h4
12 files changed, 807 insertions, 472 deletions
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index edb871d7d395..859e424e15e5 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -29,6 +29,254 @@ static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane)
return container_of(plane, struct tegra_plane, base);
}
+static unsigned int tegra_dc_format(uint32_t format, uint32_t *swap)
+{
+ /* assume no swapping of fetched data */
+ if (swap)
+ *swap = BYTE_SWAP_NOSWAP;
+
+ switch (format) {
+ case DRM_FORMAT_XBGR8888:
+ return WIN_COLOR_DEPTH_R8G8B8A8;
+
+ case DRM_FORMAT_XRGB8888:
+ return WIN_COLOR_DEPTH_B8G8R8A8;
+
+ case DRM_FORMAT_RGB565:
+ return WIN_COLOR_DEPTH_B5G6R5;
+
+ case DRM_FORMAT_UYVY:
+ return WIN_COLOR_DEPTH_YCbCr422;
+
+ case DRM_FORMAT_YUYV:
+ if (swap)
+ *swap = BYTE_SWAP_SWAP2;
+
+ return WIN_COLOR_DEPTH_YCbCr422;
+
+ case DRM_FORMAT_YUV420:
+ return WIN_COLOR_DEPTH_YCbCr420P;
+
+ case DRM_FORMAT_YUV422:
+ return WIN_COLOR_DEPTH_YCbCr422P;
+
+ default:
+ break;
+ }
+
+ WARN(1, "unsupported pixel format %u, using default\n", format);
+ return WIN_COLOR_DEPTH_B8G8R8A8;
+}
+
+static bool tegra_dc_format_is_yuv(unsigned int format, bool *planar)
+{
+ switch (format) {
+ case WIN_COLOR_DEPTH_YCbCr422:
+ case WIN_COLOR_DEPTH_YUV422:
+ if (planar)
+ *planar = false;
+
+ return true;
+
+ case WIN_COLOR_DEPTH_YCbCr420P:
+ case WIN_COLOR_DEPTH_YUV420P:
+ case WIN_COLOR_DEPTH_YCbCr422P:
+ case WIN_COLOR_DEPTH_YUV422P:
+ case WIN_COLOR_DEPTH_YCbCr422R:
+ case WIN_COLOR_DEPTH_YUV422R:
+ case WIN_COLOR_DEPTH_YCbCr422RA:
+ case WIN_COLOR_DEPTH_YUV422RA:
+ if (planar)
+ *planar = true;
+
+ return true;
+ }
+
+ return false;
+}
+
+static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v,
+ unsigned int bpp)
+{
+ fixed20_12 outf = dfixed_init(out);
+ fixed20_12 inf = dfixed_init(in);
+ u32 dda_inc;
+ int max;
+
+ if (v)
+ max = 15;
+ else {
+ switch (bpp) {
+ case 2:
+ max = 8;
+ break;
+
+ default:
+ WARN_ON_ONCE(1);
+ /* fallthrough */
+ case 4:
+ max = 4;
+ break;
+ }
+ }
+
+ outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1));
+ inf.full -= dfixed_const(1);
+
+ dda_inc = dfixed_div(inf, outf);
+ dda_inc = min_t(u32, dda_inc, dfixed_const(max));
+
+ return dda_inc;
+}
+
+static inline u32 compute_initial_dda(unsigned int in)
+{
+ fixed20_12 inf = dfixed_init(in);
+ return dfixed_frac(inf);
+}
+
+static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
+ const struct tegra_dc_window *window)
+{
+ unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp;
+ unsigned long value;
+ bool yuv, planar;
+
+ /*
+ * For YUV planar modes, the number of bytes per pixel takes into
+ * account only the luma component and therefore is 1.
+ */
+ yuv = tegra_dc_format_is_yuv(window->format, &planar);
+ if (!yuv)
+ bpp = window->bits_per_pixel / 8;
+ else
+ bpp = planar ? 1 : 2;
+
+ value = WINDOW_A_SELECT << index;
+ tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
+
+ tegra_dc_writel(dc, window->format, DC_WIN_COLOR_DEPTH);
+ tegra_dc_writel(dc, window->swap, DC_WIN_BYTE_SWAP);
+
+ value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x);
+ tegra_dc_writel(dc, value, DC_WIN_POSITION);
+
+ value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w);
+ tegra_dc_writel(dc, value, DC_WIN_SIZE);
+
+ h_offset = window->src.x * bpp;
+ v_offset = window->src.y;
+ h_size = window->src.w * bpp;
+ v_size = window->src.h;
+
+ value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size);
+ tegra_dc_writel(dc, value, DC_WIN_PRESCALED_SIZE);
+
+ /*
+ * For DDA computations the number of bytes per pixel for YUV planar
+ * modes needs to take into account all Y, U and V components.
+ */
+ if (yuv && planar)
+ bpp = 2;
+
+ h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp);
+ v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp);
+
+ value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda);
+ tegra_dc_writel(dc, value, DC_WIN_DDA_INC);
+
+ h_dda = compute_initial_dda(window->src.x);
+ v_dda = compute_initial_dda(window->src.y);
+
+ tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA);
+ tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA);
+
+ tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE);
+ tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE);
+
+ tegra_dc_writel(dc, window->base[0], DC_WINBUF_START_ADDR);
+
+ if (yuv && planar) {
+ tegra_dc_writel(dc, window->base[1], DC_WINBUF_START_ADDR_U);
+ tegra_dc_writel(dc, window->base[2], DC_WINBUF_START_ADDR_V);
+ value = window->stride[1] << 16 | window->stride[0];
+ tegra_dc_writel(dc, value, DC_WIN_LINE_STRIDE);
+ } else {
+ tegra_dc_writel(dc, window->stride[0], DC_WIN_LINE_STRIDE);
+ }
+
+ if (window->bottom_up)
+ v_offset += window->src.h - 1;
+
+ tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET);
+ tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET);
+
+ if (window->tiled) {
+ value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
+ DC_WIN_BUFFER_ADDR_MODE_TILE;
+ } else {
+ value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
+ DC_WIN_BUFFER_ADDR_MODE_LINEAR;
+ }
+
+ tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
+
+ value = WIN_ENABLE;
+
+ if (yuv) {
+ /* setup default colorspace conversion coefficients */
+ tegra_dc_writel(dc, 0x00f0, DC_WIN_CSC_YOF);
+ tegra_dc_writel(dc, 0x012a, DC_WIN_CSC_KYRGB);
+ tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KUR);
+ tegra_dc_writel(dc, 0x0198, DC_WIN_CSC_KVR);
+ tegra_dc_writel(dc, 0x039b, DC_WIN_CSC_KUG);
+ tegra_dc_writel(dc, 0x032f, DC_WIN_CSC_KVG);
+ tegra_dc_writel(dc, 0x0204, DC_WIN_CSC_KUB);
+ tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KVB);
+
+ value |= CSC_ENABLE;
+ } else if (window->bits_per_pixel < 24) {
+ value |= COLOR_EXPAND;
+ }
+
+ if (window->bottom_up)
+ value |= V_DIRECTION;
+
+ tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
+
+ /*
+ * Disable blending and assume Window A is the bottom-most window,
+ * Window C is the top-most window and Window B is in the middle.
+ */
+ tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_NOKEY);
+ tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_1WIN);
+
+ switch (index) {
+ case 0:
+ tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_X);
+ tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y);
+ tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY);
+ break;
+
+ case 1:
+ tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X);
+ tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y);
+ tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY);
+ break;
+
+ case 2:
+ tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X);
+ tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_Y);
+ tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_3WIN_XY);
+ break;
+ }
+
+ tegra_dc_writel(dc, WIN_A_UPDATE << index, DC_CMD_STATE_CONTROL);
+ tegra_dc_writel(dc, WIN_A_ACT_REQ << index, DC_CMD_STATE_CONTROL);
+
+ return 0;
+}
+
static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_framebuffer *fb, int crtc_x,
int crtc_y, unsigned int crtc_w,
@@ -49,7 +297,7 @@ static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
window.dst.y = crtc_y;
window.dst.w = crtc_w;
window.dst.h = crtc_h;
- window.format = tegra_dc_format(fb->pixel_format);
+ window.format = tegra_dc_format(fb->pixel_format, &window.swap);
window.bits_per_pixel = fb->bits_per_pixel;
window.bottom_up = tegra_fb_is_bottom_up(fb);
window.tiled = tegra_fb_is_tiled(fb);
@@ -117,6 +365,7 @@ static const uint32_t plane_formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_RGB565,
DRM_FORMAT_UYVY,
+ DRM_FORMAT_YUYV,
DRM_FORMAT_YUV420,
DRM_FORMAT_YUV422,
};
@@ -150,9 +399,9 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
struct drm_framebuffer *fb)
{
- unsigned int format = tegra_dc_format(fb->pixel_format);
struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
unsigned int h_offset = 0, v_offset = 0;
+ unsigned int format, swap;
unsigned long value;
tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER);
@@ -162,7 +411,10 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
tegra_dc_writel(dc, bo->paddr + value, DC_WINBUF_START_ADDR);
tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE);
+
+ format = tegra_dc_format(fb->pixel_format, &swap);
tegra_dc_writel(dc, format, DC_WIN_COLOR_DEPTH);
+ tegra_dc_writel(dc, swap, DC_WIN_BYTE_SWAP);
if (tegra_fb_is_tiled(fb)) {
value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
@@ -177,13 +429,13 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
/* make sure bottom-up buffers are properly displayed */
if (tegra_fb_is_bottom_up(fb)) {
value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
- value |= INVERT_V;
+ value |= V_DIRECTION;
tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
v_offset += fb->height - 1;
} else {
value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
- value &= ~INVERT_V;
+ value &= ~V_DIRECTION;
tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
}
@@ -334,52 +586,11 @@ static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
return true;
}
-static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v,
- unsigned int bpp)
-{
- fixed20_12 outf = dfixed_init(out);
- fixed20_12 inf = dfixed_init(in);
- u32 dda_inc;
- int max;
-
- if (v)
- max = 15;
- else {
- switch (bpp) {
- case 2:
- max = 8;
- break;
-
- default:
- WARN_ON_ONCE(1);
- /* fallthrough */
- case 4:
- max = 4;
- break;
- }
- }
-
- outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1));
- inf.full -= dfixed_const(1);
-
- dda_inc = dfixed_div(inf, outf);
- dda_inc = min_t(u32, dda_inc, dfixed_const(max));
-
- return dda_inc;
-}
-
-static inline u32 compute_initial_dda(unsigned int in)
-{
- fixed20_12 inf = dfixed_init(in);
- return dfixed_frac(inf);
-}
-
static int tegra_dc_set_timings(struct tegra_dc *dc,
struct drm_display_mode *mode)
{
- /* TODO: For HDMI compliance, h & v ref_to_sync should be set to 1 */
- unsigned int h_ref_to_sync = 0;
- unsigned int v_ref_to_sync = 0;
+ unsigned int h_ref_to_sync = 1;
+ unsigned int v_ref_to_sync = 1;
unsigned long value;
tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS);
@@ -406,13 +617,14 @@ static int tegra_dc_set_timings(struct tegra_dc *dc,
}
static int tegra_crtc_setup_clk(struct drm_crtc *crtc,
- struct drm_display_mode *mode,
- unsigned long *div)
+ struct drm_display_mode *mode)
{
- unsigned long pclk = mode->clock * 1000, rate;
+ unsigned long pclk = mode->clock * 1000;
struct tegra_dc *dc = to_tegra_dc(crtc);
struct tegra_output *output = NULL;
struct drm_encoder *encoder;
+ unsigned int div;
+ u32 value;
long err;
list_for_each_entry(encoder, &crtc->dev->mode_config.encoder_list, head)
@@ -425,221 +637,23 @@ static int tegra_crtc_setup_clk(struct drm_crtc *crtc,
return -ENODEV;
/*
- * This assumes that the display controller will divide its parent
- * clock by 2 to generate the pixel clock.
+ * This assumes that the parent clock is pll_d_out0 or pll_d2_out
+ * respectively, each of which divides the base pll_d by 2.
*/
- err = tegra_output_setup_clock(output, dc->clk, pclk * 2);
+ err = tegra_output_setup_clock(output, dc->clk, pclk, &div);
if (err < 0) {
dev_err(dc->dev, "failed to setup clock: %ld\n", err);
return err;
}
- rate = clk_get_rate(dc->clk);
- *div = (rate * 2 / pclk) - 2;
-
- DRM_DEBUG_KMS("rate: %lu, div: %lu\n", rate, *div);
-
- return 0;
-}
-
-static bool tegra_dc_format_is_yuv(unsigned int format, bool *planar)
-{
- switch (format) {
- case WIN_COLOR_DEPTH_YCbCr422:
- case WIN_COLOR_DEPTH_YUV422:
- if (planar)
- *planar = false;
-
- return true;
-
- case WIN_COLOR_DEPTH_YCbCr420P:
- case WIN_COLOR_DEPTH_YUV420P:
- case WIN_COLOR_DEPTH_YCbCr422P:
- case WIN_COLOR_DEPTH_YUV422P:
- case WIN_COLOR_DEPTH_YCbCr422R:
- case WIN_COLOR_DEPTH_YUV422R:
- case WIN_COLOR_DEPTH_YCbCr422RA:
- case WIN_COLOR_DEPTH_YUV422RA:
- if (planar)
- *planar = true;
-
- return true;
- }
-
- return false;
-}
-
-int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
- const struct tegra_dc_window *window)
-{
- unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp;
- unsigned long value;
- bool yuv, planar;
-
- /*
- * For YUV planar modes, the number of bytes per pixel takes into
- * account only the luma component and therefore is 1.
- */
- yuv = tegra_dc_format_is_yuv(window->format, &planar);
- if (!yuv)
- bpp = window->bits_per_pixel / 8;
- else
- bpp = planar ? 1 : 2;
-
- value = WINDOW_A_SELECT << index;
- tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER);
-
- tegra_dc_writel(dc, window->format, DC_WIN_COLOR_DEPTH);
- tegra_dc_writel(dc, 0, DC_WIN_BYTE_SWAP);
-
- value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x);
- tegra_dc_writel(dc, value, DC_WIN_POSITION);
-
- value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w);
- tegra_dc_writel(dc, value, DC_WIN_SIZE);
-
- h_offset = window->src.x * bpp;
- v_offset = window->src.y;
- h_size = window->src.w * bpp;
- v_size = window->src.h;
-
- value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size);
- tegra_dc_writel(dc, value, DC_WIN_PRESCALED_SIZE);
+ DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk), div);
- /*
- * For DDA computations the number of bytes per pixel for YUV planar
- * modes needs to take into account all Y, U and V components.
- */
- if (yuv && planar)
- bpp = 2;
-
- h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp);
- v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp);
-
- value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda);
- tegra_dc_writel(dc, value, DC_WIN_DDA_INC);
-
- h_dda = compute_initial_dda(window->src.x);
- v_dda = compute_initial_dda(window->src.y);
-
- tegra_dc_writel(dc, h_dda, DC_WIN_H_INITIAL_DDA);
- tegra_dc_writel(dc, v_dda, DC_WIN_V_INITIAL_DDA);
-
- tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE);
- tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE);
-
- tegra_dc_writel(dc, window->base[0], DC_WINBUF_START_ADDR);
-
- if (yuv && planar) {
- tegra_dc_writel(dc, window->base[1], DC_WINBUF_START_ADDR_U);
- tegra_dc_writel(dc, window->base[2], DC_WINBUF_START_ADDR_V);
- value = window->stride[1] << 16 | window->stride[0];
- tegra_dc_writel(dc, value, DC_WIN_LINE_STRIDE);
- } else {
- tegra_dc_writel(dc, window->stride[0], DC_WIN_LINE_STRIDE);
- }
-
- if (window->bottom_up)
- v_offset += window->src.h - 1;
-
- tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET);
- tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET);
-
- if (window->tiled) {
- value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
- DC_WIN_BUFFER_ADDR_MODE_TILE;
- } else {
- value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
- DC_WIN_BUFFER_ADDR_MODE_LINEAR;
- }
-
- tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
-
- value = WIN_ENABLE;
-
- if (yuv) {
- /* setup default colorspace conversion coefficients */
- tegra_dc_writel(dc, 0x00f0, DC_WIN_CSC_YOF);
- tegra_dc_writel(dc, 0x012a, DC_WIN_CSC_KYRGB);
- tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KUR);
- tegra_dc_writel(dc, 0x0198, DC_WIN_CSC_KVR);
- tegra_dc_writel(dc, 0x039b, DC_WIN_CSC_KUG);
- tegra_dc_writel(dc, 0x032f, DC_WIN_CSC_KVG);
- tegra_dc_writel(dc, 0x0204, DC_WIN_CSC_KUB);
- tegra_dc_writel(dc, 0x0000, DC_WIN_CSC_KVB);
-
- value |= CSC_ENABLE;
- } else if (window->bits_per_pixel < 24) {
- value |= COLOR_EXPAND;
- }
-
- if (window->bottom_up)
- value |= INVERT_V;
-
- tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
-
- /*
- * Disable blending and assume Window A is the bottom-most window,
- * Window C is the top-most window and Window B is in the middle.
- */
- tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_NOKEY);
- tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_1WIN);
-
- switch (index) {
- case 0:
- tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_X);
- tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y);
- tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY);
- break;
-
- case 1:
- tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X);
- tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_2WIN_Y);
- tegra_dc_writel(dc, 0x000000, DC_WIN_BLEND_3WIN_XY);
- break;
-
- case 2:
- tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_X);
- tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_2WIN_Y);
- tegra_dc_writel(dc, 0xffff00, DC_WIN_BLEND_3WIN_XY);
- break;
- }
-
- tegra_dc_writel(dc, WIN_A_UPDATE << index, DC_CMD_STATE_CONTROL);
- tegra_dc_writel(dc, WIN_A_ACT_REQ << index, DC_CMD_STATE_CONTROL);
+ value = SHIFT_CLK_DIVIDER(div) | PIXEL_CLK_DIVIDER_PCD1;
+ tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
return 0;
}
-unsigned int tegra_dc_format(uint32_t format)
-{
- switch (format) {
- case DRM_FORMAT_XBGR8888:
- return WIN_COLOR_DEPTH_R8G8B8A8;
-
- case DRM_FORMAT_XRGB8888:
- return WIN_COLOR_DEPTH_B8G8R8A8;
-
- case DRM_FORMAT_RGB565:
- return WIN_COLOR_DEPTH_B5G6R5;
-
- case DRM_FORMAT_UYVY:
- return WIN_COLOR_DEPTH_YCbCr422;
-
- case DRM_FORMAT_YUV420:
- return WIN_COLOR_DEPTH_YCbCr420P;
-
- case DRM_FORMAT_YUV422:
- return WIN_COLOR_DEPTH_YCbCr422P;
-
- default:
- break;
- }
-
- WARN(1, "unsupported pixel format %u, using default\n", format);
- return WIN_COLOR_DEPTH_B8G8R8A8;
-}
-
static int tegra_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted,
@@ -648,12 +662,12 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
struct tegra_bo *bo = tegra_fb_get_plane(crtc->primary->fb, 0);
struct tegra_dc *dc = to_tegra_dc(crtc);
struct tegra_dc_window window;
- unsigned long div, value;
+ u32 value;
int err;
drm_vblank_pre_modeset(crtc->dev, dc->pipe);
- err = tegra_crtc_setup_clk(crtc, mode, &div);
+ err = tegra_crtc_setup_clk(crtc, mode);
if (err) {
dev_err(dc->dev, "failed to setup clock for CRTC: %d\n", err);
return err;
@@ -669,9 +683,6 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL);
}
- value = SHIFT_CLK_DIVIDER(div) | PIXEL_CLK_DIVIDER_PCD1;
- tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
-
/* setup window parameters */
memset(&window, 0, sizeof(window));
window.src.x = 0;
@@ -682,7 +693,8 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
window.dst.y = 0;
window.dst.w = mode->hdisplay;
window.dst.h = mode->vdisplay;
- window.format = tegra_dc_format(crtc->primary->fb->pixel_format);
+ window.format = tegra_dc_format(crtc->primary->fb->pixel_format,
+ &window.swap);
window.bits_per_pixel = crtc->primary->fb->bits_per_pixel;
window.stride[0] = crtc->primary->fb->pitches[0];
window.base[0] = bo->paddr;
@@ -728,10 +740,6 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc)
WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
- value = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
- PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
- tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
-
/* initialize timer */
value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) |
WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20);
diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h
index c94101494826..44e31aefe899 100644
--- a/drivers/gpu/drm/tegra/dc.h
+++ b/drivers/gpu/drm/tegra/dc.h
@@ -312,7 +312,8 @@
#define DC_WIN_CSC_KVB 0x618
#define DC_WIN_WIN_OPTIONS 0x700
-#define INVERT_V (1 << 2)
+#define H_DIRECTION (1 << 0)
+#define V_DIRECTION (1 << 2)
#define COLOR_EXPAND (1 << 6)
#define CSC_ENABLE (1 << 18)
#define WIN_ENABLE (1 << 30)
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 6f5b6e2f552e..09ee77923d67 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -666,6 +666,7 @@ static const struct of_device_id host1x_drm_subdevs[] = {
{ .compatible = "nvidia,tegra114-gr3d", },
{ .compatible = "nvidia,tegra124-dc", },
{ .compatible = "nvidia,tegra124-sor", },
+ { .compatible = "nvidia,tegra124-hdmi", },
{ /* sentinel */ }
};
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 126332c3ecbb..784fd5c77441 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -80,13 +80,13 @@ host1x_to_drm_client(struct host1x_client *client)
return container_of(client, struct tegra_drm_client, base);
}
-extern int tegra_drm_register_client(struct tegra_drm *tegra,
- struct tegra_drm_client *client);
-extern int tegra_drm_unregister_client(struct tegra_drm *tegra,
- struct tegra_drm_client *client);
+int tegra_drm_register_client(struct tegra_drm *tegra,
+ struct tegra_drm_client *client);
+int tegra_drm_unregister_client(struct tegra_drm *tegra,
+ struct tegra_drm_client *client);
-extern int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm);
-extern int tegra_drm_exit(struct tegra_drm *tegra);
+int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm);
+int tegra_drm_exit(struct tegra_drm *tegra);
struct tegra_dc_soc_info;
struct tegra_output;
@@ -156,6 +156,7 @@ struct tegra_dc_window {
} dst;
unsigned int bits_per_pixel;
unsigned int format;
+ unsigned int swap;
unsigned int stride[2];
unsigned long base[3];
bool bottom_up;
@@ -163,19 +164,15 @@ struct tegra_dc_window {
};
/* from dc.c */
-extern unsigned int tegra_dc_format(uint32_t format);
-extern int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
- const struct tegra_dc_window *window);
-extern void tegra_dc_enable_vblank(struct tegra_dc *dc);
-extern void tegra_dc_disable_vblank(struct tegra_dc *dc);
-extern void tegra_dc_cancel_page_flip(struct drm_crtc *crtc,
- struct drm_file *file);
+void tegra_dc_enable_vblank(struct tegra_dc *dc);
+void tegra_dc_disable_vblank(struct tegra_dc *dc);
+void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file);
struct tegra_output_ops {
int (*enable)(struct tegra_output *output);
int (*disable)(struct tegra_output *output);
int (*setup_clock)(struct tegra_output *output, struct clk *clk,
- unsigned long pclk);
+ unsigned long pclk, unsigned int *div);
int (*check_mode)(struct tegra_output *output,
struct drm_display_mode *mode,
enum drm_mode_status *status);
@@ -233,10 +230,11 @@ static inline int tegra_output_disable(struct tegra_output *output)
}
static inline int tegra_output_setup_clock(struct tegra_output *output,
- struct clk *clk, unsigned long pclk)
+ struct clk *clk, unsigned long pclk,
+ unsigned int *div)
{
if (output && output->ops && output->ops->setup_clock)
- return output->ops->setup_clock(output, clk, pclk);
+ return output->ops->setup_clock(output, clk, pclk, div);
return output ? -ENOSYS : -EINVAL;
}
@@ -256,22 +254,20 @@ int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device);
void drm_host1x_exit(struct drm_driver *driver, struct host1x_device *device);
/* from rgb.c */
-extern int tegra_dc_rgb_probe(struct tegra_dc *dc);
-extern int tegra_dc_rgb_remove(struct tegra_dc *dc);
-extern int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc);
-extern int tegra_dc_rgb_exit(struct tegra_dc *dc);
+int tegra_dc_rgb_probe(struct tegra_dc *dc);
+int tegra_dc_rgb_remove(struct tegra_dc *dc);
+int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc);
+int tegra_dc_rgb_exit(struct tegra_dc *dc);
/* from output.c */
-extern int tegra_output_probe(struct tegra_output *output);
-extern int tegra_output_remove(struct tegra_output *output);
-extern int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
-extern int tegra_output_exit(struct tegra_output *output);
+int tegra_output_probe(struct tegra_output *output);
+int tegra_output_remove(struct tegra_output *output);
+int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
+int tegra_output_exit(struct tegra_output *output);
/* from dpaux.c */
-
struct tegra_dpaux;
struct drm_dp_link;
-struct drm_dp_aux;
struct tegra_dpaux *tegra_dpaux_find_by_of_node(struct device_node *np);
enum drm_connector_status tegra_dpaux_detect(struct tegra_dpaux *dpaux);
@@ -288,10 +284,10 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
unsigned int index);
bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer);
bool tegra_fb_is_tiled(struct drm_framebuffer *framebuffer);
-extern int tegra_drm_fb_init(struct drm_device *drm);
-extern void tegra_drm_fb_exit(struct drm_device *drm);
+int tegra_drm_fb_init(struct drm_device *drm);
+void tegra_drm_fb_exit(struct drm_device *drm);
#ifdef CONFIG_DRM_TEGRA_FBDEV
-extern void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev);
+void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev);
#endif
extern struct platform_driver tegra_dc_driver;
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 0e599f0417c0..3838575f71c6 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -14,6 +14,8 @@
#include <linux/platform_device.h>
#include <linux/reset.h>
+#include <linux/regulator/consumer.h>
+
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_panel.h>
@@ -43,11 +45,15 @@ struct tegra_dsi {
struct drm_minor *minor;
struct dentry *debugfs;
+ unsigned long flags;
enum mipi_dsi_pixel_format format;
unsigned int lanes;
struct tegra_mipi_device *mipi;
struct mipi_dsi_host host;
+
+ struct regulator *vdd;
+ bool enabled;
};
static inline struct tegra_dsi *
@@ -244,8 +250,10 @@ static int tegra_dsi_debugfs_exit(struct tegra_dsi *dsi)
#define PKT_LP (1 << 30)
#define NUM_PKT_SEQ 12
-/* non-burst mode with sync-end */
-static const u32 pkt_seq_vnb_syne[NUM_PKT_SEQ] = {
+/*
+ * non-burst mode with sync pulses
+ */
+static const u32 pkt_seq_video_non_burst_sync_pulses[NUM_PKT_SEQ] = {
[ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) |
PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) |
@@ -280,6 +288,36 @@ static const u32 pkt_seq_vnb_syne[NUM_PKT_SEQ] = {
PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4),
};
+/*
+ * non-burst mode with sync events
+ */
+static const u32 pkt_seq_video_non_burst_sync_events[NUM_PKT_SEQ] = {
+ [ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
+ PKT_LP,
+ [ 1] = 0,
+ [ 2] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
+ PKT_LP,
+ [ 3] = 0,
+ [ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
+ PKT_LP,
+ [ 5] = 0,
+ [ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) |
+ PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3),
+ [ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4),
+ [ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
+ PKT_LP,
+ [ 9] = 0,
+ [10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+ PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) |
+ PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3),
+ [11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4),
+};
+
static int tegra_dsi_set_phy_timing(struct tegra_dsi *dsi)
{
struct mipi_dphy_timing timing;
@@ -361,28 +399,70 @@ static int tegra_dsi_get_muldiv(enum mipi_dsi_pixel_format format,
return 0;
}
+static int tegra_dsi_get_format(enum mipi_dsi_pixel_format format,
+ enum tegra_dsi_format *fmt)
+{
+ switch (format) {
+ case MIPI_DSI_FMT_RGB888:
+ *fmt = TEGRA_DSI_FORMAT_24P;
+ break;
+
+ case MIPI_DSI_FMT_RGB666:
+ *fmt = TEGRA_DSI_FORMAT_18NP;
+ break;
+
+ case MIPI_DSI_FMT_RGB666_PACKED:
+ *fmt = TEGRA_DSI_FORMAT_18P;
+ break;
+
+ case MIPI_DSI_FMT_RGB565:
+ *fmt = TEGRA_DSI_FORMAT_16P;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int tegra_output_dsi_enable(struct tegra_output *output)
{
struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
struct drm_display_mode *mode = &dc->base.mode;
unsigned int hact, hsw, hbp, hfp, i, mul, div;
struct tegra_dsi *dsi = to_dsi(output);
- /* FIXME: don't hardcode this */
- const u32 *pkt_seq = pkt_seq_vnb_syne;
+ enum tegra_dsi_format format;
unsigned long value;
+ const u32 *pkt_seq;
int err;
+ if (dsi->enabled)
+ return 0;
+
+ if (dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
+ DRM_DEBUG_KMS("Non-burst video mode with sync pulses\n");
+ pkt_seq = pkt_seq_video_non_burst_sync_pulses;
+ } else {
+ DRM_DEBUG_KMS("Non-burst video mode with sync events\n");
+ pkt_seq = pkt_seq_video_non_burst_sync_events;
+ }
+
err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
if (err < 0)
return err;
+ err = tegra_dsi_get_format(dsi->format, &format);
+ if (err < 0)
+ return err;
+
err = clk_enable(dsi->clk);
if (err < 0)
return err;
reset_control_deassert(dsi->rst);
- value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(dsi->format) |
+ value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(format) |
DSI_CONTROL_LANES(dsi->lanes - 1) |
DSI_CONTROL_SOURCE(dc->pipe);
tegra_dsi_writel(dsi, value, DSI_CONTROL);
@@ -454,6 +534,8 @@ static int tegra_output_dsi_enable(struct tegra_output *output)
value |= DSI_POWER_CONTROL_ENABLE;
tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL);
+ dsi->enabled = true;
+
return 0;
}
@@ -463,9 +545,12 @@ static int tegra_output_dsi_disable(struct tegra_output *output)
struct tegra_dsi *dsi = to_dsi(output);
unsigned long value;
+ if (!dsi->enabled)
+ return 0;
+
/* disable DSI controller */
value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL);
- value &= DSI_POWER_CONTROL_ENABLE;
+ value &= ~DSI_POWER_CONTROL_ENABLE;
tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL);
/*
@@ -492,30 +577,44 @@ static int tegra_output_dsi_disable(struct tegra_output *output)
clk_disable(dsi->clk);
+ dsi->enabled = false;
+
return 0;
}
static int tegra_output_dsi_setup_clock(struct tegra_output *output,
- struct clk *clk, unsigned long pclk)
+ struct clk *clk, unsigned long pclk,
+ unsigned int *divp)
{
struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
struct drm_display_mode *mode = &dc->base.mode;
unsigned int timeout, mul, div, vrefresh;
struct tegra_dsi *dsi = to_dsi(output);
unsigned long bclk, plld, value;
- struct clk *base;
int err;
err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
if (err < 0)
return err;
+ DRM_DEBUG_KMS("mul: %u, div: %u, lanes: %u\n", mul, div, dsi->lanes);
vrefresh = drm_mode_vrefresh(mode);
+ DRM_DEBUG_KMS("vrefresh: %u\n", vrefresh);
- pclk = mode->htotal * mode->vtotal * vrefresh;
+ /* compute byte clock */
bclk = (pclk * mul) / (div * dsi->lanes);
- plld = DIV_ROUND_UP(bclk * 8, 1000000);
- pclk = (plld * 1000000) / 2;
+
+ /*
+ * Compute bit clock and round up to the next MHz.
+ */
+ plld = DIV_ROUND_UP(bclk * 8, 1000000) * 1000000;
+
+ /*
+ * We divide the frequency by two here, but we make up for that by
+ * setting the shift clock divider (further below) to half of the
+ * correct value.
+ */
+ plld /= 2;
err = clk_set_parent(clk, dsi->clk_parent);
if (err < 0) {
@@ -523,20 +622,26 @@ static int tegra_output_dsi_setup_clock(struct tegra_output *output,
return err;
}
- base = clk_get_parent(dsi->clk_parent);
-
- /*
- * This assumes that the parent clock is pll_d_out0 or pll_d2_out
- * respectively, each of which divides the base pll_d by 2.
- */
- err = clk_set_rate(base, pclk * 2);
+ err = clk_set_rate(dsi->clk_parent, plld);
if (err < 0) {
dev_err(dsi->dev, "failed to set base clock rate to %lu Hz\n",
- pclk * 2);
+ plld);
return err;
}
/*
+ * Derive pixel clock from bit clock using the shift clock divider.
+ * Note that this is only half of what we would expect, but we need
+ * that to make up for the fact that we divided the bit clock by a
+ * factor of two above.
+ *
+ * It's not clear exactly why this is necessary, but the display is
+ * not working properly otherwise. Perhaps the PLLs cannot generate
+ * frequencies sufficiently high.
+ */
+ *divp = ((8 * mul) / (div * dsi->lanes)) - 2;
+
+ /*
* XXX: Move the below somewhere else so that we don't need to have
* access to the vrefresh in this function?
*/
@@ -612,7 +717,6 @@ static int tegra_dsi_init(struct host1x_client *client)
{
struct tegra_drm *tegra = dev_get_drvdata(client->parent);
struct tegra_dsi *dsi = host1x_client_to_dsi(client);
- unsigned long value, i;
int err;
dsi->output.type = TEGRA_OUTPUT_DSI;
@@ -631,40 +735,12 @@ static int tegra_dsi_init(struct host1x_client *client)
dev_err(dsi->dev, "debugfs setup failed: %d\n", err);
}
- /*
- * enable high-speed mode, checksum generation, ECC generation and
- * disable raw mode
- */
- value = tegra_dsi_readl(dsi, DSI_HOST_CONTROL);
- value |= DSI_HOST_CONTROL_ECC | DSI_HOST_CONTROL_CS |
- DSI_HOST_CONTROL_HS;
- value &= ~DSI_HOST_CONTROL_RAW;
- tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);
-
- tegra_dsi_writel(dsi, 0, DSI_SOL_DELAY);
- tegra_dsi_writel(dsi, 0, DSI_MAX_THRESHOLD);
-
- tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_CONTROL);
-
- for (i = 0; i < 8; i++) {
- tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_0 + i);
- tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_8 + i);
- }
-
- for (i = 0; i < 12; i++)
- tegra_dsi_writel(dsi, 0, DSI_PKT_SEQ_0_LO + i);
-
- tegra_dsi_writel(dsi, 0, DSI_DCS_CMDS);
-
err = tegra_dsi_pad_calibrate(dsi);
if (err < 0) {
dev_err(dsi->dev, "MIPI calibration failed: %d\n", err);
return err;
}
- tegra_dsi_writel(dsi, DSI_POWER_CONTROL_ENABLE, DSI_POWER_CONTROL);
- usleep_range(300, 1000);
-
return 0;
}
@@ -715,66 +791,13 @@ static int tegra_dsi_setup_clocks(struct tegra_dsi *dsi)
return 0;
}
-static void tegra_dsi_initialize(struct tegra_dsi *dsi)
-{
- unsigned int i;
-
- tegra_dsi_writel(dsi, 0, DSI_POWER_CONTROL);
-
- tegra_dsi_writel(dsi, 0, DSI_INT_ENABLE);
- tegra_dsi_writel(dsi, 0, DSI_INT_STATUS);
- tegra_dsi_writel(dsi, 0, DSI_INT_MASK);
-
- tegra_dsi_writel(dsi, 0, DSI_HOST_CONTROL);
- tegra_dsi_writel(dsi, 0, DSI_CONTROL);
-
- tegra_dsi_writel(dsi, 0, DSI_SOL_DELAY);
- tegra_dsi_writel(dsi, 0, DSI_MAX_THRESHOLD);
-
- tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_CONTROL);
-
- for (i = 0; i < 8; i++) {
- tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_0 + i);
- tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_8 + i);
- }
-
- for (i = 0; i < 12; i++)
- tegra_dsi_writel(dsi, 0, DSI_PKT_SEQ_0_LO + i);
-
- tegra_dsi_writel(dsi, 0, DSI_DCS_CMDS);
-
- for (i = 0; i < 4; i++)
- tegra_dsi_writel(dsi, 0, DSI_PKT_LEN_0_1 + i);
-
- tegra_dsi_writel(dsi, 0x00000000, DSI_PHY_TIMING_0);
- tegra_dsi_writel(dsi, 0x00000000, DSI_PHY_TIMING_1);
- tegra_dsi_writel(dsi, 0x000000ff, DSI_PHY_TIMING_2);
- tegra_dsi_writel(dsi, 0x00000000, DSI_BTA_TIMING);
-
- tegra_dsi_writel(dsi, 0, DSI_TIMEOUT_0);
- tegra_dsi_writel(dsi, 0, DSI_TIMEOUT_1);
- tegra_dsi_writel(dsi, 0, DSI_TO_TALLY);
-
- tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0);
- tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_CD);
- tegra_dsi_writel(dsi, 0, DSI_PAD_CD_STATUS);
- tegra_dsi_writel(dsi, 0, DSI_VIDEO_MODE_CONTROL);
- tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1);
- tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_2);
- tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_3);
- tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_4);
-
- tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_CONTROL);
- tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_START);
- tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_SIZE);
-}
-
static int tegra_dsi_host_attach(struct mipi_dsi_host *host,
struct mipi_dsi_device *device)
{
struct tegra_dsi *dsi = host_to_tegra(host);
struct tegra_output *output = &dsi->output;
+ dsi->flags = device->mode_flags;
dsi->format = device->format;
dsi->lanes = device->lanes;
@@ -829,6 +852,7 @@ static int tegra_dsi_probe(struct platform_device *pdev)
* attaches to the DSI host, the parameters will be taken from
* the attached device.
*/
+ dsi->flags = MIPI_DSI_MODE_VIDEO;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->lanes = 4;
@@ -872,6 +896,18 @@ static int tegra_dsi_probe(struct platform_device *pdev)
return err;
}
+ dsi->vdd = devm_regulator_get(&pdev->dev, "avdd-dsi-csi");
+ if (IS_ERR(dsi->vdd)) {
+ dev_err(&pdev->dev, "cannot get VDD supply\n");
+ return PTR_ERR(dsi->vdd);
+ }
+
+ err = regulator_enable(dsi->vdd);
+ if (err < 0) {
+ dev_err(&pdev->dev, "cannot enable VDD supply\n");
+ return err;
+ }
+
err = tegra_dsi_setup_clocks(dsi);
if (err < 0) {
dev_err(&pdev->dev, "cannot setup clocks\n");
@@ -883,8 +919,6 @@ static int tegra_dsi_probe(struct platform_device *pdev)
if (IS_ERR(dsi->regs))
return PTR_ERR(dsi->regs);
- tegra_dsi_initialize(dsi);
-
dsi->mipi = tegra_mipi_request(&pdev->dev);
if (IS_ERR(dsi->mipi))
return PTR_ERR(dsi->mipi);
@@ -929,9 +963,11 @@ static int tegra_dsi_remove(struct platform_device *pdev)
mipi_dsi_host_unregister(&dsi->host);
tegra_mipi_free(dsi->mipi);
+ regulator_disable(dsi->vdd);
clk_disable_unprepare(dsi->clk_parent);
clk_disable_unprepare(dsi->clk_lp);
clk_disable_unprepare(dsi->clk);
+ reset_control_assert(dsi->rst);
err = tegra_output_remove(&dsi->output);
if (err < 0) {
diff --git a/drivers/gpu/drm/tegra/dsi.h b/drivers/gpu/drm/tegra/dsi.h
index 1db5cc24ea91..5ce610d08d77 100644
--- a/drivers/gpu/drm/tegra/dsi.h
+++ b/drivers/gpu/drm/tegra/dsi.h
@@ -117,4 +117,14 @@
#define DSI_INIT_SEQ_DATA_14 0x5e
#define DSI_INIT_SEQ_DATA_15 0x5f
+/*
+ * pixel format as used in the DSI_CONTROL_FORMAT field
+ */
+enum tegra_dsi_format {
+ TEGRA_DSI_FORMAT_16P,
+ TEGRA_DSI_FORMAT_18NP,
+ TEGRA_DSI_FORMAT_18P,
+ TEGRA_DSI_FORMAT_24P,
+};
+
#endif
diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c
index bcf9895cef9f..aa85b7b26f10 100644
--- a/drivers/gpu/drm/tegra/gem.c
+++ b/drivers/gpu/drm/tegra/gem.c
@@ -169,7 +169,8 @@ err:
return ERR_PTR(ret);
}
-struct tegra_bo *tegra_bo_import(struct drm_device *drm, struct dma_buf *buf)
+static struct tegra_bo *tegra_bo_import(struct drm_device *drm,
+ struct dma_buf *buf)
{
struct dma_buf_attachment *attach;
struct tegra_bo *bo;
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index 6928015d11a4..fec1a63d32c9 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -42,8 +42,9 @@ struct tegra_hdmi {
struct device *dev;
bool enabled;
- struct regulator *vdd;
+ struct regulator *hdmi;
struct regulator *pll;
+ struct regulator *vdd;
void __iomem *regs;
unsigned int irq;
@@ -317,6 +318,85 @@ static const struct tmds_config tegra114_tmds_config[] = {
},
};
+static const struct tmds_config tegra124_tmds_config[] = {
+ { /* 480p/576p / 25.2MHz/27MHz modes */
+ .pclk = 27000000,
+ .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) |
+ SOR_PLL_VCOCAP(0) | SOR_PLL_RESISTORSEL,
+ .pll1 = SOR_PLL_LOADADJ(3) | SOR_PLL_TMDS_TERMADJ(0),
+ .pe_current = PE_CURRENT0(PE_CURRENT_0_mA_T114) |
+ PE_CURRENT1(PE_CURRENT_0_mA_T114) |
+ PE_CURRENT2(PE_CURRENT_0_mA_T114) |
+ PE_CURRENT3(PE_CURRENT_0_mA_T114),
+ .drive_current =
+ DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_10_400_mA_T114) |
+ DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_10_400_mA_T114) |
+ DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_10_400_mA_T114) |
+ DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_10_400_mA_T114),
+ .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) |
+ PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) |
+ PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) |
+ PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA),
+ }, { /* 720p / 74.25MHz modes */
+ .pclk = 74250000,
+ .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) |
+ SOR_PLL_VCOCAP(1) | SOR_PLL_RESISTORSEL,
+ .pll1 = SOR_PLL_PE_EN | SOR_PLL_LOADADJ(3) |
+ SOR_PLL_TMDS_TERMADJ(0),
+ .pe_current = PE_CURRENT0(PE_CURRENT_15_mA_T114) |
+ PE_CURRENT1(PE_CURRENT_15_mA_T114) |
+ PE_CURRENT2(PE_CURRENT_15_mA_T114) |
+ PE_CURRENT3(PE_CURRENT_15_mA_T114),
+ .drive_current =
+ DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_10_400_mA_T114) |
+ DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_10_400_mA_T114) |
+ DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_10_400_mA_T114) |
+ DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_10_400_mA_T114),
+ .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) |
+ PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) |
+ PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) |
+ PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA),
+ }, { /* 1080p / 148.5MHz modes */
+ .pclk = 148500000,
+ .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) |
+ SOR_PLL_VCOCAP(3) | SOR_PLL_RESISTORSEL,
+ .pll1 = SOR_PLL_PE_EN | SOR_PLL_LOADADJ(3) |
+ SOR_PLL_TMDS_TERMADJ(0),
+ .pe_current = PE_CURRENT0(PE_CURRENT_10_mA_T114) |
+ PE_CURRENT1(PE_CURRENT_10_mA_T114) |
+ PE_CURRENT2(PE_CURRENT_10_mA_T114) |
+ PE_CURRENT3(PE_CURRENT_10_mA_T114),
+ .drive_current =
+ DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_12_400_mA_T114) |
+ DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_12_400_mA_T114) |
+ DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_12_400_mA_T114) |
+ DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_12_400_mA_T114),
+ .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) |
+ PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) |
+ PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) |
+ PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA),
+ }, { /* 225/297MHz modes */
+ .pclk = UINT_MAX,
+ .pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) |
+ SOR_PLL_VCOCAP(0xf) | SOR_PLL_RESISTORSEL,
+ .pll1 = SOR_PLL_LOADADJ(3) | SOR_PLL_TMDS_TERMADJ(7)
+ | SOR_PLL_TMDS_TERM_ENABLE,
+ .pe_current = PE_CURRENT0(PE_CURRENT_0_mA_T114) |
+ PE_CURRENT1(PE_CURRENT_0_mA_T114) |
+ PE_CURRENT2(PE_CURRENT_0_mA_T114) |
+ PE_CURRENT3(PE_CURRENT_0_mA_T114),
+ .drive_current =
+ DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_25_200_mA_T114) |
+ DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_25_200_mA_T114) |
+ DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_25_200_mA_T114) |
+ DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_19_200_mA_T114),
+ .peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_3_000_mA) |
+ PEAK_CURRENT_LANE1(PEAK_CURRENT_3_000_mA) |
+ PEAK_CURRENT_LANE2(PEAK_CURRENT_3_000_mA) |
+ PEAK_CURRENT_LANE3(PEAK_CURRENT_0_800_mA),
+ },
+};
+
static const struct tegra_hdmi_audio_config *
tegra_hdmi_get_audio_config(unsigned int audio_freq, unsigned int pclk)
{
@@ -716,13 +796,9 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
return err;
}
- /*
- * This assumes that the display controller will divide its parent
- * clock by 2 to generate the pixel clock.
- */
- err = tegra_output_setup_clock(output, hdmi->clk, pclk * 2);
+ err = regulator_enable(hdmi->vdd);
if (err < 0) {
- dev_err(hdmi->dev, "failed to setup clock: %d\n", err);
+ dev_err(hdmi->dev, "failed to enable VDD regulator: %d\n", err);
return err;
}
@@ -730,7 +806,7 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
if (err < 0)
return err;
- err = clk_enable(hdmi->clk);
+ err = clk_prepare_enable(hdmi->clk);
if (err < 0) {
dev_err(hdmi->dev, "failed to enable clock: %d\n", err);
return err;
@@ -740,6 +816,17 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
usleep_range(1000, 2000);
reset_control_deassert(hdmi->rst);
+ /* power up sequence */
+ value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_PLL0);
+ value &= ~SOR_PLL_PDBG;
+ tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_PLL0);
+
+ usleep_range(10, 20);
+
+ value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_PLL0);
+ value &= ~SOR_PLL_PWR;
+ tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_PLL0);
+
tegra_dc_writel(dc, VSYNC_H_POSITION(1),
DC_DISP_DISP_TIMING_OPTIONS);
tegra_dc_writel(dc, DITHER_CONTROL_DISABLE | BASE_COLOR_SIZE888,
@@ -838,9 +925,13 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_SEQ_INST(0));
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_SEQ_INST(8));
- value = 0x1c800;
+ value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_CSTM);
value &= ~SOR_CSTM_ROTCLK(~0);
value |= SOR_CSTM_ROTCLK(2);
+ value |= SOR_CSTM_PLLDIV;
+ value &= ~SOR_CSTM_LVDS_ENABLE;
+ value &= ~SOR_CSTM_MODE_MASK;
+ value |= SOR_CSTM_MODE_TMDS;
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_CSTM);
/* start SOR */
@@ -930,10 +1021,18 @@ static int tegra_output_hdmi_disable(struct tegra_output *output)
* sure it's only executed when the output is attached to one.
*/
if (dc) {
+ /*
+ * XXX: We can't do this here because it causes HDMI to go
+ * into an erroneous state with the result that HDMI won't
+ * properly work once disabled. See also a similar symptom
+ * for the SOR output.
+ */
+ /*
value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
+ */
value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
value &= ~DISP_CTRL_MODE_MASK;
@@ -947,8 +1046,9 @@ static int tegra_output_hdmi_disable(struct tegra_output *output)
tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
}
+ clk_disable_unprepare(hdmi->clk);
reset_control_assert(hdmi->rst);
- clk_disable(hdmi->clk);
+ regulator_disable(hdmi->vdd);
regulator_disable(hdmi->pll);
hdmi->enabled = false;
@@ -957,10 +1057,10 @@ static int tegra_output_hdmi_disable(struct tegra_output *output)
}
static int tegra_output_hdmi_setup_clock(struct tegra_output *output,
- struct clk *clk, unsigned long pclk)
+ struct clk *clk, unsigned long pclk,
+ unsigned int *div)
{
struct tegra_hdmi *hdmi = to_hdmi(output);
- struct clk *base;
int err;
err = clk_set_parent(clk, hdmi->clk_parent);
@@ -969,17 +1069,12 @@ static int tegra_output_hdmi_setup_clock(struct tegra_output *output,
return err;
}
- base = clk_get_parent(hdmi->clk_parent);
-
- /*
- * This assumes that the parent clock is pll_d_out0 or pll_d2_out
- * respectively, each of which divides the base pll_d by 2.
- */
- err = clk_set_rate(base, pclk * 2);
+ err = clk_set_rate(hdmi->clk_parent, pclk);
if (err < 0)
- dev_err(output->dev,
- "failed to set base clock rate to %lu Hz\n",
- pclk * 2);
+ dev_err(output->dev, "failed to set clock rate to %lu Hz\n",
+ pclk);
+
+ *div = 0;
return 0;
}
@@ -1017,7 +1112,7 @@ static int tegra_hdmi_show_regs(struct seq_file *s, void *data)
struct tegra_hdmi *hdmi = node->info_ent->data;
int err;
- err = clk_enable(hdmi->clk);
+ err = clk_prepare_enable(hdmi->clk);
if (err)
return err;
@@ -1186,7 +1281,7 @@ static int tegra_hdmi_show_regs(struct seq_file *s, void *data)
#undef DUMP_REG
- clk_disable(hdmi->clk);
+ clk_disable_unprepare(hdmi->clk);
return 0;
}
@@ -1256,13 +1351,6 @@ static int tegra_hdmi_init(struct host1x_client *client)
struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
int err;
- err = regulator_enable(hdmi->vdd);
- if (err < 0) {
- dev_err(client->dev, "failed to enable VDD regulator: %d\n",
- err);
- return err;
- }
-
hdmi->output.type = TEGRA_OUTPUT_HDMI;
hdmi->output.dev = client->dev;
hdmi->output.ops = &hdmi_ops;
@@ -1279,6 +1367,13 @@ static int tegra_hdmi_init(struct host1x_client *client)
dev_err(client->dev, "debugfs setup failed: %d\n", err);
}
+ err = regulator_enable(hdmi->hdmi);
+ if (err < 0) {
+ dev_err(client->dev, "failed to enable HDMI regulator: %d\n",
+ err);
+ return err;
+ }
+
return 0;
}
@@ -1287,6 +1382,8 @@ static int tegra_hdmi_exit(struct host1x_client *client)
struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
int err;
+ regulator_disable(hdmi->hdmi);
+
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
err = tegra_hdmi_debugfs_exit(hdmi);
if (err < 0)
@@ -1306,8 +1403,6 @@ static int tegra_hdmi_exit(struct host1x_client *client)
return err;
}
- regulator_disable(hdmi->vdd);
-
return 0;
}
@@ -1340,7 +1435,16 @@ static const struct tegra_hdmi_config tegra114_hdmi_config = {
.has_sor_io_peak_current = true,
};
+static const struct tegra_hdmi_config tegra124_hdmi_config = {
+ .tmds = tegra124_tmds_config,
+ .num_tmds = ARRAY_SIZE(tegra124_tmds_config),
+ .fuse_override_offset = HDMI_NV_PDISP_SOR_PAD_CTLS0,
+ .fuse_override_value = 1 << 31,
+ .has_sor_io_peak_current = true,
+};
+
static const struct of_device_id tegra_hdmi_of_match[] = {
+ { .compatible = "nvidia,tegra124-hdmi", .data = &tegra124_hdmi_config },
{ .compatible = "nvidia,tegra114-hdmi", .data = &tegra114_hdmi_config },
{ .compatible = "nvidia,tegra30-hdmi", .data = &tegra30_hdmi_config },
{ .compatible = "nvidia,tegra20-hdmi", .data = &tegra20_hdmi_config },
@@ -1381,28 +1485,20 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
return PTR_ERR(hdmi->rst);
}
- err = clk_prepare(hdmi->clk);
- if (err < 0)
- return err;
-
hdmi->clk_parent = devm_clk_get(&pdev->dev, "parent");
if (IS_ERR(hdmi->clk_parent))
return PTR_ERR(hdmi->clk_parent);
- err = clk_prepare(hdmi->clk_parent);
- if (err < 0)
- return err;
-
err = clk_set_parent(hdmi->clk, hdmi->clk_parent);
if (err < 0) {
dev_err(&pdev->dev, "failed to setup clocks: %d\n", err);
return err;
}
- hdmi->vdd = devm_regulator_get(&pdev->dev, "vdd");
- if (IS_ERR(hdmi->vdd)) {
- dev_err(&pdev->dev, "failed to get VDD regulator\n");
- return PTR_ERR(hdmi->vdd);
+ hdmi->hdmi = devm_regulator_get(&pdev->dev, "hdmi");
+ if (IS_ERR(hdmi->hdmi)) {
+ dev_err(&pdev->dev, "failed to get HDMI regulator\n");
+ return PTR_ERR(hdmi->hdmi);
}
hdmi->pll = devm_regulator_get(&pdev->dev, "pll");
@@ -1411,6 +1507,12 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
return PTR_ERR(hdmi->pll);
}
+ hdmi->vdd = devm_regulator_get(&pdev->dev, "vdd");
+ if (IS_ERR(hdmi->vdd)) {
+ dev_err(&pdev->dev, "failed to get VDD regulator\n");
+ return PTR_ERR(hdmi->vdd);
+ }
+
hdmi->output.dev = &pdev->dev;
err = tegra_output_probe(&hdmi->output);
@@ -1462,8 +1564,8 @@ static int tegra_hdmi_remove(struct platform_device *pdev)
return err;
}
- clk_unprepare(hdmi->clk_parent);
- clk_unprepare(hdmi->clk);
+ clk_disable_unprepare(hdmi->clk_parent);
+ clk_disable_unprepare(hdmi->clk);
return 0;
}
diff --git a/drivers/gpu/drm/tegra/hdmi.h b/drivers/gpu/drm/tegra/hdmi.h
index 0aebc485f7fa..919a19df4e1b 100644
--- a/drivers/gpu/drm/tegra/hdmi.h
+++ b/drivers/gpu/drm/tegra/hdmi.h
@@ -190,6 +190,11 @@
#define HDMI_NV_PDISP_SOR_CSTM 0x5a
#define SOR_CSTM_ROTCLK(x) (((x) & 0xf) << 24)
+#define SOR_CSTM_PLLDIV (1 << 21)
+#define SOR_CSTM_LVDS_ENABLE (1 << 16)
+#define SOR_CSTM_MODE_LVDS (0 << 12)
+#define SOR_CSTM_MODE_TMDS (1 << 12)
+#define SOR_CSTM_MODE_MASK (3 << 12)
#define HDMI_NV_PDISP_SOR_LVDS 0x5b
#define HDMI_NV_PDISP_SOR_CRCA 0x5c
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index 0266fb40479e..d6af9be48f42 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -159,11 +159,38 @@ static int tegra_output_rgb_disable(struct tegra_output *output)
}
static int tegra_output_rgb_setup_clock(struct tegra_output *output,
- struct clk *clk, unsigned long pclk)
+ struct clk *clk, unsigned long pclk,
+ unsigned int *div)
{
struct tegra_rgb *rgb = to_rgb(output);
+ int err;
+
+ err = clk_set_parent(clk, rgb->clk_parent);
+ if (err < 0) {
+ dev_err(output->dev, "failed to set parent: %d\n", err);
+ return err;
+ }
- return clk_set_parent(clk, rgb->clk_parent);
+ /*
+ * We may not want to change the frequency of the parent clock, since
+ * it may be a parent for other peripherals. This is due to the fact
+ * that on Tegra20 there's only a single clock dedicated to display
+ * (pll_d_out0), whereas later generations have a second one that can
+ * be used to independently drive a second output (pll_d2_out0).
+ *
+ * As a way to support multiple outputs on Tegra20 as well, pll_p is
+ * typically used as the parent clock for the display controllers.
+ * But this comes at a cost: pll_p is the parent of several other
+ * peripherals, so its frequency shouldn't change out of the blue.
+ *
+ * The best we can do at this point is to use the shift clock divider
+ * and hope that the desired frequency can be matched (or at least
+ * matched sufficiently close that the panel will still work).
+ */
+
+ *div = ((clk_get_rate(clk) * 2) / pclk) - 2;
+
+ return 0;
}
static int tegra_output_rgb_check_mode(struct tegra_output *output,
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 49ef5729f435..7d66f6e53919 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -7,6 +7,7 @@
*/
#include <linux/clk.h>
+#include <linux/debugfs.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
@@ -33,7 +34,10 @@ struct tegra_sor {
struct tegra_dpaux *dpaux;
+ struct mutex lock;
bool enabled;
+
+ struct dentry *debugfs;
};
static inline struct tegra_sor *
@@ -296,14 +300,16 @@ static int tegra_output_sor_enable(struct tegra_output *output)
unsigned int vbe, vse, hbe, hse, vbs, hbs, i;
struct tegra_sor *sor = to_sor(output);
unsigned long value;
- int err;
+ int err = 0;
+
+ mutex_lock(&sor->lock);
if (sor->enabled)
- return 0;
+ goto unlock;
err = clk_prepare_enable(sor->clk);
if (err < 0)
- return err;
+ goto unlock;
reset_control_deassert(sor->rst);
@@ -385,7 +391,7 @@ static int tegra_output_sor_enable(struct tegra_output *output)
err = tegra_io_rail_power_on(TEGRA_IO_RAIL_LVDS);
if (err < 0) {
dev_err(sor->dev, "failed to power on I/O rail: %d\n", err);
- return err;
+ goto unlock;
}
usleep_range(5, 100);
@@ -509,21 +515,21 @@ static int tegra_output_sor_enable(struct tegra_output *output)
if (err < 0) {
dev_err(sor->dev, "failed to probe eDP link: %d\n",
err);
- return err;
+ goto unlock;
}
err = drm_dp_link_power_up(aux, &link);
if (err < 0) {
dev_err(sor->dev, "failed to power up eDP link: %d\n",
err);
- return err;
+ goto unlock;
}
err = drm_dp_link_configure(aux, &link);
if (err < 0) {
dev_err(sor->dev, "failed to configure eDP link: %d\n",
err);
- return err;
+ goto unlock;
}
rate = drm_dp_link_rate_to_bw_code(link.rate);
@@ -558,7 +564,7 @@ static int tegra_output_sor_enable(struct tegra_output *output)
if (err < 0) {
dev_err(sor->dev, "DP fast link training failed: %d\n",
err);
- return err;
+ goto unlock;
}
dev_dbg(sor->dev, "fast link training succeeded\n");
@@ -567,7 +573,7 @@ static int tegra_output_sor_enable(struct tegra_output *output)
err = tegra_sor_power_up(sor, 250);
if (err < 0) {
dev_err(sor->dev, "failed to power up SOR: %d\n", err);
- return err;
+ goto unlock;
}
/* start display controller in continuous mode */
@@ -632,7 +638,7 @@ static int tegra_output_sor_enable(struct tegra_output *output)
err = tegra_sor_setup_pwm(sor, 250);
if (err < 0) {
dev_err(sor->dev, "failed to setup PWM: %d\n", err);
- return err;
+ goto unlock;
}
value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
@@ -644,18 +650,20 @@ static int tegra_output_sor_enable(struct tegra_output *output)
err = tegra_sor_attach(sor);
if (err < 0) {
dev_err(sor->dev, "failed to attach SOR: %d\n", err);
- return err;
+ goto unlock;
}
err = tegra_sor_wakeup(sor);
if (err < 0) {
dev_err(sor->dev, "failed to enable DC: %d\n", err);
- return err;
+ goto unlock;
}
sor->enabled = true;
- return 0;
+unlock:
+ mutex_unlock(&sor->lock);
+ return err;
}
static int tegra_sor_detach(struct tegra_sor *sor)
@@ -783,15 +791,17 @@ static int tegra_output_sor_disable(struct tegra_output *output)
struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
struct tegra_sor *sor = to_sor(output);
unsigned long value;
- int err;
+ int err = 0;
+
+ mutex_lock(&sor->lock);
if (!sor->enabled)
- return 0;
+ goto unlock;
err = tegra_sor_detach(sor);
if (err < 0) {
dev_err(sor->dev, "failed to detach SOR: %d\n", err);
- return err;
+ goto unlock;
}
tegra_sor_writel(sor, 0, SOR_STATE_1);
@@ -832,21 +842,21 @@ static int tegra_output_sor_disable(struct tegra_output *output)
err = tegra_sor_power_down(sor);
if (err < 0) {
dev_err(sor->dev, "failed to power down SOR: %d\n", err);
- return err;
+ goto unlock;
}
if (sor->dpaux) {
err = tegra_dpaux_disable(sor->dpaux);
if (err < 0) {
dev_err(sor->dev, "failed to disable DP: %d\n", err);
- return err;
+ goto unlock;
}
}
err = tegra_io_rail_power_off(TEGRA_IO_RAIL_LVDS);
if (err < 0) {
dev_err(sor->dev, "failed to power off I/O rail: %d\n", err);
- return err;
+ goto unlock;
}
reset_control_assert(sor->rst);
@@ -854,17 +864,20 @@ static int tegra_output_sor_disable(struct tegra_output *output)
sor->enabled = false;
- return 0;
+unlock:
+ mutex_unlock(&sor->lock);
+ return err;
}
static int tegra_output_sor_setup_clock(struct tegra_output *output,
- struct clk *clk, unsigned long pclk)
+ struct clk *clk, unsigned long pclk,
+ unsigned int *div)
{
struct tegra_sor *sor = to_sor(output);
int err;
/* round to next MHz */
- pclk = DIV_ROUND_UP(pclk / 2, 1000000) * 1000000;
+ pclk = DIV_ROUND_UP(pclk, 1000000) * 1000000;
err = clk_set_parent(clk, sor->clk_parent);
if (err < 0) {
@@ -874,11 +887,12 @@ static int tegra_output_sor_setup_clock(struct tegra_output *output,
err = clk_set_rate(sor->clk_parent, pclk);
if (err < 0) {
- dev_err(sor->dev, "failed to set base clock rate to %lu Hz\n",
- pclk * 2);
+ dev_err(sor->dev, "failed to set clock rate to %lu Hz\n", pclk);
return err;
}
+ *div = 0;
+
return 0;
}
@@ -914,6 +928,120 @@ static const struct tegra_output_ops sor_ops = {
.detect = tegra_output_sor_detect,
};
+static int tegra_sor_crc_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+
+ return 0;
+}
+
+static int tegra_sor_crc_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout)
+{
+ u32 value;
+
+ timeout = jiffies + msecs_to_jiffies(timeout);
+
+ while (time_before(jiffies, timeout)) {
+ value = tegra_sor_readl(sor, SOR_CRC_A);
+ if (value & SOR_CRC_A_VALID)
+ return 0;
+
+ usleep_range(100, 200);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer,
+ size_t size, loff_t *ppos)
+{
+ struct tegra_sor *sor = file->private_data;
+ ssize_t num, err;
+ char buf[10];
+ u32 value;
+
+ mutex_lock(&sor->lock);
+
+ if (!sor->enabled) {
+ err = -EAGAIN;
+ goto unlock;
+ }
+
+ value = tegra_sor_readl(sor, SOR_STATE_1);
+ value &= ~SOR_STATE_ASY_CRC_MODE_MASK;
+ tegra_sor_writel(sor, value, SOR_STATE_1);
+
+ value = tegra_sor_readl(sor, SOR_CRC_CNTRL);
+ value |= SOR_CRC_CNTRL_ENABLE;
+ tegra_sor_writel(sor, value, SOR_CRC_CNTRL);
+
+ value = tegra_sor_readl(sor, SOR_TEST);
+ value &= ~SOR_TEST_CRC_POST_SERIALIZE;
+ tegra_sor_writel(sor, value, SOR_TEST);
+
+ err = tegra_sor_crc_wait(sor, 100);
+ if (err < 0)
+ goto unlock;
+
+ tegra_sor_writel(sor, SOR_CRC_A_RESET, SOR_CRC_A);
+ value = tegra_sor_readl(sor, SOR_CRC_B);
+
+ num = scnprintf(buf, sizeof(buf), "%08x\n", value);
+
+ err = simple_read_from_buffer(buffer, size, ppos, buf, num);
+
+unlock:
+ mutex_unlock(&sor->lock);
+ return err;
+}
+
+static const struct file_operations tegra_sor_crc_fops = {
+ .owner = THIS_MODULE,
+ .open = tegra_sor_crc_open,
+ .read = tegra_sor_crc_read,
+ .release = tegra_sor_crc_release,
+};
+
+static int tegra_sor_debugfs_init(struct tegra_sor *sor, struct dentry *root)
+{
+ struct dentry *entry;
+ int err = 0;
+
+ sor->debugfs = debugfs_create_dir("sor", root);
+ if (!sor->debugfs)
+ return -ENOMEM;
+
+ entry = debugfs_create_file("crc", 0644, sor->debugfs, sor,
+ &tegra_sor_crc_fops);
+ if (!entry) {
+ dev_err(sor->dev,
+ "cannot create /sys/kernel/debug/dri/%s/sor/crc\n",
+ root->d_name.name);
+ err = -ENOMEM;
+ goto remove;
+ }
+
+ return err;
+
+remove:
+ debugfs_remove(sor->debugfs);
+ sor->debugfs = NULL;
+ return err;
+}
+
+static int tegra_sor_debugfs_exit(struct tegra_sor *sor)
+{
+ debugfs_remove(sor->debugfs);
+ sor->debugfs = NULL;
+
+ return 0;
+}
+
static int tegra_sor_init(struct host1x_client *client)
{
struct tegra_drm *tegra = dev_get_drvdata(client->parent);
@@ -934,6 +1062,14 @@ static int tegra_sor_init(struct host1x_client *client)
return err;
}
+ if (IS_ENABLED(CONFIG_DEBUG_FS)) {
+ struct dentry *root = tegra->drm->primary->debugfs_root;
+
+ err = tegra_sor_debugfs_init(sor, root);
+ if (err < 0)
+ dev_err(sor->dev, "debugfs setup failed: %d\n", err);
+ }
+
if (sor->dpaux) {
err = tegra_dpaux_attach(sor->dpaux, &sor->output);
if (err < 0) {
@@ -964,6 +1100,12 @@ static int tegra_sor_exit(struct host1x_client *client)
}
}
+ if (IS_ENABLED(CONFIG_DEBUG_FS)) {
+ err = tegra_sor_debugfs_exit(sor);
+ if (err < 0)
+ dev_err(sor->dev, "debugfs cleanup failed: %d\n", err);
+ }
+
err = tegra_output_exit(&sor->output);
if (err < 0) {
dev_err(sor->dev, "output cleanup failed: %d\n", err);
@@ -1045,6 +1187,8 @@ static int tegra_sor_probe(struct platform_device *pdev)
sor->client.ops = &sor_client_ops;
sor->client.dev = &pdev->dev;
+ mutex_init(&sor->lock);
+
err = host1x_client_register(&sor->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to register host1x client: %d\n",
diff --git a/drivers/gpu/drm/tegra/sor.h b/drivers/gpu/drm/tegra/sor.h
index f4156d54cd05..a5f8853fedb5 100644
--- a/drivers/gpu/drm/tegra/sor.h
+++ b/drivers/gpu/drm/tegra/sor.h
@@ -47,6 +47,7 @@
#define SOR_HEAD_STATE_4(x) (0x0d + (x))
#define SOR_HEAD_STATE_5(x) (0x0f + (x))
#define SOR_CRC_CNTRL 0x11
+#define SOR_CRC_CNTRL_ENABLE (1 << 0)
#define SOR_DP_DEBUG_MVID 0x12
#define SOR_CLK_CNTRL 0x13
@@ -69,6 +70,7 @@
#define SOR_PWR_NORMAL_STATE_PU (1 << 0)
#define SOR_TEST 0x16
+#define SOR_TEST_CRC_POST_SERIALIZE (1 << 23)
#define SOR_TEST_ATTACHED (1 << 10)
#define SOR_TEST_HEAD_MODE_MASK (3 << 8)
#define SOR_TEST_HEAD_MODE_AWAKE (2 << 8)
@@ -115,6 +117,8 @@
#define SOR_LVDS 0x1c
#define SOR_CRC_A 0x1d
+#define SOR_CRC_A_VALID (1 << 0)
+#define SOR_CRC_A_RESET (1 << 0)
#define SOR_CRC_B 0x1e
#define SOR_BLANK 0x1f
#define SOR_SEQ_CTL 0x20