@@ -132,3 +132,11 @@ config OMAP2_DSS_SLEEP_AFTER_VENC_RESET
disable the sleep if it doesn't cause problems on your platform.
endif
+
+config TI_DSS6
+ tristate "TI DSS6 support"
+ select OMAP_DSS_BASE
+ select VIDEOMODE_HELPERS
+ select OMAP2_DSS_INIT
+ help
+ Enable support for Texas Instruments DSS6 IP.
@@ -18,3 +18,6 @@ omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi4.o hdmi4_core.o
omapdss-$(CONFIG_OMAP4_DSS_HDMI_CEC) += hdmi4_cec.o
omapdss-$(CONFIG_OMAP5_DSS_HDMI) += hdmi5.o hdmi5_core.o
ccflags-$(CONFIG_OMAP2_DSS_DEBUG) += -DDEBUG
+
+obj-$(CONFIG_TI_DSS6) += omapdss6.o
+omapdss6-y := dss6.o dispc6.o dpi6.o
new file mode 100644
@@ -0,0 +1,1438 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <drm/drm_fourcc.h>
+
+#include "omapdss.h"
+#include "dss6.h"
+#include "dispc6.h"
+
+#define REG_GET(dispc, idx, start, end) \
+ FLD_GET(dispc6_read(dispc, idx), start, end)
+
+#define REG_FLD_MOD(dispc, idx, val, start, end) \
+ dispc6_write(dispc, idx, FLD_MOD(dispc6_read(dispc, idx), val, start, end))
+
+#define VID_REG_GET(dispc, plane, idx, start, end) \
+ FLD_GET(dispc6_vid_read(dispc, plane, idx), start, end)
+
+#define VID_REG_FLD_MOD(dispc, plane, idx, val, start, end) \
+ dispc6_vid_write(dispc, plane, idx, FLD_MOD(dispc6_vid_read(dispc, plane, idx), val, start, end))
+
+
+#define VP_REG_GET(dispc, vp, idx, start, end) \
+ FLD_GET(dispc6_vp_read(dispc, vp, idx), start, end)
+
+#define VP_REG_FLD_MOD(dispc, vp, idx, val, start, end) \
+ dispc6_vp_write(dispc, vp, idx, FLD_MOD(dispc6_vp_read(dispc, vp, idx), val, start, end))
+
+struct dispc_features {
+ /* XXX should these come from the .dts? Min pclk is not feature of DSS IP */
+ unsigned long min_pclk;
+ unsigned long max_pclk;
+};
+
+/* Note: 9MHz is a special allowed case, and is handled separately in the code */
+static const struct dispc_features k2g_dispc_feats = {
+ .min_pclk = 43750000,
+ .max_pclk = 150000000,
+};
+
+static const struct of_device_id dispc6_of_match[];
+
+struct dispc_device {
+ struct platform_device *pdev;
+
+ struct dss6_device *dss6;
+
+ void __iomem *base_common;
+ void __iomem *base_vid1;
+ void __iomem *base_ovr1;
+ void __iomem *base_vp1;
+
+ int irq;
+ irq_handler_t user_handler;
+ void *user_data;
+
+ const struct dispc_features *feat;
+
+ struct clk *fclk;
+ struct clk *vp_clk;
+
+ bool is_enabled;
+
+ bool ctx_valid;
+ u32 ctx_vid1[0x400];
+
+ u32 gamma_table[256];
+};
+
+static void dispc6_write(struct dispc_device *dispc, u16 reg, u32 val)
+{
+ iowrite32(val, dispc->base_common + reg);
+}
+
+static u32 dispc6_read(struct dispc_device *dispc, u16 reg)
+{
+ return ioread32(dispc->base_common + reg);
+}
+
+static void dispc6_vid_write(struct dispc_device *dispc,
+ enum omap_plane_id plane, u16 reg, u32 val)
+{
+ void __iomem *base = dispc->base_vid1;
+
+ iowrite32(val, base + reg);
+}
+
+static u32 dispc6_vid_read(struct dispc_device *dispc,
+ enum omap_plane_id plane, u16 reg)
+{
+ void __iomem *base = dispc->base_vid1;
+
+ return ioread32(base + reg);
+}
+
+static void dispc6_ovr_write(struct dispc_device *dispc,
+ enum omap_channel channel, u16 reg, u32 val)
+{
+ void __iomem *base = dispc->base_ovr1;
+
+ iowrite32(val, base + reg);
+}
+#if 0 /* not used at the moment */
+static u32 dispc6_ovr_read(struct dispc_device *dispc,
+ enum omap_channel channel, u16 reg)
+{
+ void __iomem *base = dispc->base_ovr1;
+
+ return ioread32(base + reg);
+}
+#endif
+static void dispc6_vp_write(struct dispc_device *dispc,
+ enum omap_channel channel, u16 reg, u32 val)
+{
+ void __iomem *base = dispc->base_vp1;
+
+ iowrite32(val, base + reg);
+}
+
+static u32 dispc6_vp_read(struct dispc_device *dispc,
+ enum omap_channel channel, u16 reg)
+{
+ void __iomem *base = dispc->base_vp1;
+
+ return ioread32(base + reg);
+}
+
+int dispc6_runtime_get(struct dispc_device *dispc)
+{
+ int r;
+
+ dev_dbg(&dispc->pdev->dev, "dispc_runtime_get\n");
+
+ r = pm_runtime_get_sync(&dispc->pdev->dev);
+ WARN_ON(r < 0);
+ return r < 0 ? r : 0;
+}
+
+void dispc6_runtime_put(struct dispc_device *dispc)
+{
+ int r;
+
+ dev_dbg(&dispc->pdev->dev, "dispc_runtime_put\n");
+
+ r = pm_runtime_put_sync(&dispc->pdev->dev);
+ WARN_ON(r < 0);
+}
+
+static void dispc6_save_context(struct dispc_device *dispc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dispc->ctx_vid1); ++i)
+ dispc->ctx_vid1[i] = dispc6_vid_read(dispc, 0, i);
+
+ dispc->ctx_valid = true;
+}
+
+static void dispc6_restore_context(struct dispc_device *dispc)
+{
+ int i;
+
+ if (!dispc->ctx_valid)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(dispc->ctx_vid1); ++i)
+ dispc6_vid_write(dispc, 0, i, dispc->ctx_vid1[i]);
+}
+
+static irqreturn_t dispc6_irq_handler(int irq, void *arg)
+{
+ struct dispc_device *dispc = arg;
+ u32 stat;
+
+ if (!dispc->is_enabled)
+ return IRQ_NONE;
+
+ stat = dispc6_read(dispc, DISPC_IRQSTATUS);
+
+ if (stat == 0)
+ return IRQ_NONE;
+
+ dispc6_write(dispc, DISPC_IRQSTATUS, stat);
+
+ return dispc->user_handler(irq, dispc->user_data);
+}
+
+static int dispc6_request_irq(struct dispc_device *dispc,
+ irq_handler_t handler, void *dev_id)
+{
+ int r;
+
+ if (dispc->user_handler != NULL)
+ return -EBUSY;
+
+ dispc->user_handler = handler;
+ dispc->user_data = dev_id;
+
+ /* ensure the dispc6_irq_handler sees the values above */
+ smp_wmb();
+
+ r = devm_request_irq(&dispc->pdev->dev, dispc->irq, dispc6_irq_handler,
+ IRQF_SHARED, "DISPC", dispc);
+ if (r) {
+ dispc->user_handler = NULL;
+ dispc->user_data = NULL;
+ }
+
+ return r;
+}
+
+static void dispc6_free_irq(struct dispc_device *dispc, void *dev_id)
+{
+ dispc6_write(dispc, DISPC_IRQENABLE_CLR, 0xffffffff);
+
+ devm_free_irq(&dispc->pdev->dev, dispc->irq, &dispc);
+
+ dispc->user_handler = NULL;
+ dispc->user_data = NULL;
+}
+
+u64 dispc6_vp_irq_from_raw(u32 stat)
+{
+ uint channel = 0;
+ u64 vp_stat = 0;
+
+ if (stat & BIT(0))
+ vp_stat |= DSS_IRQ_MGR_FRAME_DONE(channel);
+ if (stat & BIT(1))
+ vp_stat |= DSS_IRQ_MGR_VSYNC_EVEN(channel);
+ if (stat & BIT(2))
+ vp_stat |= DSS_IRQ_MGR_VSYNC_ODD(channel);
+ if (stat & BIT(4))
+ vp_stat |= DSS_IRQ_MGR_SYNC_LOST(channel);
+
+ return vp_stat;
+}
+
+static u32 dispc6_vp_irq_to_raw(u64 vpstat)
+{
+ uint channel = 0;
+ u32 stat = 0;
+
+ if (vpstat & DSS_IRQ_MGR_FRAME_DONE(channel))
+ stat |= BIT(0);
+ if (vpstat & DSS_IRQ_MGR_VSYNC_EVEN(channel))
+ stat |= BIT(1);
+ if (vpstat & DSS_IRQ_MGR_VSYNC_ODD(channel))
+ stat |= BIT(2);
+ if (vpstat & DSS_IRQ_MGR_SYNC_LOST(channel))
+ stat |= BIT(4);
+
+ return stat;
+}
+
+static u64 dispc6_vid_irq_from_raw(u32 stat)
+{
+ uint plane = 0;
+ u64 vid_stat = 0;
+
+ if (stat & BIT(0))
+ vid_stat |= DSS_IRQ_OVL_FIFO_UNDERFLOW(plane);
+
+ return vid_stat;
+}
+
+static u32 dispc6_vid_irq_to_raw(u64 vidstat)
+{
+ uint plane = 0;
+ u32 stat = 0;
+
+ if (vidstat & DSS_IRQ_OVL_FIFO_UNDERFLOW(plane))
+ stat |= BIT(0);
+
+ return stat;
+}
+
+
+static u64 dispc6_vp_read_irqstatus(struct dispc_device *dispc,
+ enum omap_channel channel)
+{
+ u32 stat = dispc6_vp_read(dispc, channel, DISPC_VP_IRQSTATUS);
+
+ return dispc6_vp_irq_from_raw(stat);
+}
+
+static void dispc6_vp_write_irqstatus(struct dispc_device *dispc,
+ enum omap_channel channel,
+ u64 vpstat)
+{
+ u32 stat = dispc6_vp_irq_to_raw(vpstat);
+
+ dispc6_vp_write(dispc, channel, DISPC_VP_IRQSTATUS, stat);
+}
+
+static u64 dispc6_vid_read_irqstatus(struct dispc_device *dispc,
+ enum omap_plane_id plane)
+{
+ u32 stat = dispc6_vid_read(dispc, plane, DISPC_VID_IRQSTATUS);
+
+ return dispc6_vid_irq_from_raw(stat);
+}
+
+static void dispc6_vid_write_irqstatus(struct dispc_device *dispc,
+ enum omap_plane_id plane,
+ u64 vidstat)
+{
+ u32 stat = dispc6_vid_irq_to_raw(vidstat);
+
+ dispc6_vid_write(dispc, plane, DISPC_VID_IRQSTATUS, stat);
+}
+
+
+static u64 dispc6_vp_read_irqenable(struct dispc_device *dispc,
+ enum omap_channel channel)
+{
+ u32 stat = dispc6_vp_read(dispc, channel, DISPC_VP_IRQENABLE);
+
+ return dispc6_vp_irq_from_raw(stat);
+}
+
+static void dispc6_vp_write_irqenable(struct dispc_device *dispc,
+ enum omap_channel channel,
+ u64 vpstat)
+{
+ u32 stat = dispc6_vp_irq_to_raw(vpstat);
+
+ dispc6_vp_write(dispc, channel, DISPC_VP_IRQENABLE, stat);
+}
+
+static u64 dispc6_vid_read_irqenable(struct dispc_device *dispc,
+ enum omap_plane_id plane)
+{
+ u32 stat = dispc6_vid_read(dispc, plane, DISPC_VID_IRQENABLE);
+
+ return dispc6_vid_irq_from_raw(stat);
+}
+
+static void dispc6_vid_write_irqenable(struct dispc_device *dispc,
+ enum omap_plane_id plane,
+ u64 vidstat)
+{
+ u32 stat = dispc6_vid_irq_to_raw(vidstat);
+
+ dispc6_vid_write(dispc, plane, DISPC_VID_IRQENABLE, stat);
+}
+
+
+static void dispc6_clear_irqstatus(struct dispc_device *dispc, u64 mask)
+{
+ dispc6_vp_write_irqstatus(dispc, 0, mask);
+ dispc6_vid_write_irqstatus(dispc, 0, mask);
+}
+
+static u64 dispc6_read_and_clear_irqstatus(struct dispc_device *dispc)
+{
+ u64 stat = 0;
+
+ stat |= dispc6_vp_read_irqstatus(dispc, 0);
+ stat |= dispc6_vid_read_irqstatus(dispc, 0);
+
+ dispc6_clear_irqstatus(dispc, stat);
+
+ return stat;
+}
+
+static u64 dispc6_read_irqenable(struct dispc_device *dispc)
+{
+ u64 stat = 0;
+
+ stat |= dispc6_vp_read_irqenable(dispc, 0);
+ stat |= dispc6_vid_read_irqenable(dispc, 0);
+
+ return stat;
+}
+
+static void dispc6_write_irqenable(struct dispc_device *dispc, u64 mask)
+{
+ u64 old_mask = dispc6_read_irqenable(dispc);
+
+ /* clear the irqstatus for newly enabled irqs */
+ dispc6_clear_irqstatus(dispc, (mask ^ old_mask) & mask);
+
+ dispc6_vp_write_irqenable(dispc, 0, mask);
+ dispc6_vid_write_irqenable(dispc, 0, mask);
+
+ dispc6_write(dispc, DISPC_IRQENABLE_SET, (1 << 0) | (1 << 7));
+
+ /* flush posted write */
+ dispc6_read_irqenable(dispc);
+}
+
+
+static bool dispc6_mgr_go_busy(struct dispc_device *dispc,
+ enum omap_channel channel)
+{
+ return VP_REG_GET(dispc, channel, DISPC_VP_CONTROL, 5, 5);
+}
+
+static void dispc6_mgr_go(struct dispc_device *dispc,
+ enum omap_channel channel)
+{
+ VP_REG_FLD_MOD(dispc, channel, DISPC_VP_CONTROL, 1, 5, 5);
+}
+
+static void dispc6_mgr_enable(struct dispc_device *dispc,
+ enum omap_channel channel, bool enable)
+{
+ VP_REG_FLD_MOD(dispc, channel, DISPC_VP_CONTROL, !!enable, 0, 0);
+}
+
+static bool dispc6_mgr_is_enabled(struct dispc_device *dispc,
+ enum omap_channel channel)
+{
+ return VP_REG_GET(dispc, channel, DISPC_VP_CONTROL, 0, 0);
+}
+
+static u16 c8_to_c12(u8 c8)
+{
+ u16 c12;
+
+ c12 = c8 << 4;
+
+ /* Replication logic: Copy c8 4 MSB to 4 LSB for full scale c12 */
+ c12 = c8 >> 4;
+
+ return c12;
+}
+
+static u64 argb8888_to_argb12121212(u32 argb8888)
+{
+ u8 a, r, g, b;
+ u64 v;
+
+ a = (argb8888 >> 24) & 0xff;
+ r = (argb8888 >> 16) & 0xff;
+ g = (argb8888 >> 8) & 0xff;
+ b = (argb8888 >> 0) & 0xff;
+
+ v = ((u64)c8_to_c12(a) << 36) | ((u64)c8_to_c12(r) << 24) |
+ ((u64)c8_to_c12(g) << 12) | (u64)c8_to_c12(b);
+
+ return v;
+}
+
+static void dispc6_mgr_setup(struct dispc_device *dispc,
+ enum omap_channel channel,
+ const struct omap_overlay_manager_info *info)
+{
+ u64 v;
+
+ v = argb8888_to_argb12121212(info->default_color);
+
+ dispc6_ovr_write(dispc, 0, DISPC_OVR_DEFAULT_COLOR, v & 0xffffffff);
+ dispc6_ovr_write(dispc, 0, DISPC_OVR_DEFAULT_COLOR2, (v >> 32) & 0xffff);
+}
+
+static void dispc6_set_num_datalines(struct dispc_device *dispc,
+ enum omap_channel channel, int num_lines)
+{
+ int v;
+
+ switch (num_lines) {
+ case 12:
+ v = 0; break;
+ case 16:
+ v = 1; break;
+ case 18:
+ v = 2; break;
+ case 24:
+ v = 3; break;
+ case 30:
+ v = 4; break;
+ case 36:
+ v = 5; break;
+ default:
+ __WARN();
+ v = 3;
+ break;
+ }
+
+ VP_REG_FLD_MOD(dispc, channel, DISPC_VP_CONTROL, v, 10, 8);
+}
+
+static void dispc6_mgr_set_lcd_config(struct dispc_device *dispc,
+ enum omap_channel channel,
+ const struct dss_lcd_mgr_config *config)
+{
+ dispc6_set_num_datalines(dispc, channel, config->video_port_width);
+}
+
+static bool dispc6_lcd_timings_ok(int hsw, int hfp, int hbp,
+ int vsw, int vfp, int vbp)
+{
+ if (hsw < 1 || hsw > 256 ||
+ hfp < 1 || hfp > 4096 ||
+ hbp < 1 || hbp > 4096 ||
+ vsw < 1 || vsw > 256 ||
+ vfp < 0 || vfp > 4095 ||
+ vbp < 0 || vbp > 4095)
+ return false;
+ return true;
+}
+
+bool dispc6_mgr_timings_ok(struct dispc_device *dispc,
+ enum omap_channel channel,
+ const struct videomode *vm)
+{
+ if (vm->pixelclock < dispc->feat->min_pclk && vm->pixelclock != 9000000)
+ return false;
+
+ if (vm->pixelclock > dispc->feat->max_pclk)
+ return false;
+
+ if (vm->hactive > 4096)
+ return false;
+
+ if (vm->vactive > 4096)
+ return false;
+
+ /* TODO: add interlace support */
+ if (vm->flags & DISPLAY_FLAGS_INTERLACED)
+ return false;
+
+ if (!dispc6_lcd_timings_ok(vm->hsync_len, vm->hfront_porch, vm->hback_porch,
+ vm->vsync_len, vm->vfront_porch, vm->vback_porch))
+ return false;
+
+ return true;
+}
+
+static void dispc6_mgr_set_timings(struct dispc_device *dispc,
+ enum omap_channel channel,
+ const struct videomode *vm)
+{
+ bool align, onoff, rf, ieo, ipc, ihs, ivs;
+
+ dispc6_vp_write(dispc, channel, DISPC_VP_TIMING_H,
+ FLD_VAL(vm->hsync_len - 1, 7, 0) |
+ FLD_VAL(vm->hfront_porch - 1, 19, 8) |
+ FLD_VAL(vm->hback_porch - 1, 31, 20));
+
+ dispc6_vp_write(dispc, channel, DISPC_VP_TIMING_V,
+ FLD_VAL(vm->vsync_len - 1, 7, 0) |
+ FLD_VAL(vm->vfront_porch, 19, 8) |
+ FLD_VAL(vm->vback_porch, 31, 20));
+
+ if (vm->flags & DISPLAY_FLAGS_VSYNC_HIGH)
+ ivs = false;
+ else
+ ivs = true;
+
+ if (vm->flags & DISPLAY_FLAGS_HSYNC_HIGH)
+ ihs = false;
+ else
+ ihs = true;
+
+ if (vm->flags & DISPLAY_FLAGS_DE_HIGH)
+ ieo = false;
+ else
+ ieo = true;
+
+ if (vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
+ ipc = false;
+ else
+ ipc = true;
+
+ /* always use the 'rf' setting */
+ onoff = true;
+
+ if (vm->flags & DISPLAY_FLAGS_SYNC_POSEDGE)
+ rf = true;
+ else
+ rf = false;
+
+ /* always use aligned syncs */
+ align = true;
+
+ dispc6_vp_write(dispc, channel, DISPC_VP_POL_FREQ,
+ FLD_VAL(align, 18, 18) |
+ FLD_VAL(onoff, 17, 17) |
+ FLD_VAL(rf, 16, 16) |
+ FLD_VAL(ieo, 15, 15) |
+ FLD_VAL(ipc, 14, 14) |
+ FLD_VAL(ihs, 13, 13) |
+ FLD_VAL(ivs, 12, 12));
+
+ dispc6_vp_write(dispc, channel, DISPC_VP_SIZE_SCREEN,
+ FLD_VAL(vm->hactive - 1, 11, 0) |
+ FLD_VAL(vm->vactive - 1, 27, 16));
+}
+
+int dispc6_vp_enable_clk(struct dispc_device *dispc, enum omap_channel channel)
+{
+ return clk_prepare_enable(dispc->vp_clk);
+}
+void dispc6_vp_disable_clk(struct dispc_device *dispc,
+ enum omap_channel channel)
+{
+ clk_disable_unprepare(dispc->vp_clk);
+}
+
+int dispc6_vp_set_clk_rate(struct dispc_device *dispc,
+ enum omap_channel channel, unsigned long rate)
+{
+ int r;
+ unsigned long new_rate;
+
+ r = clk_set_rate(dispc->vp_clk, rate);
+ if (r) {
+ dev_err(&dispc->pdev->dev, "Failed to set vp clk rate to %lu\n",
+ rate);
+ return r;
+ }
+
+ new_rate = clk_get_rate(dispc->vp_clk);
+
+ if (rate != new_rate)
+ dev_warn(&dispc->pdev->dev,
+ "Failed to get exact pix clock %lu != %lu\n",
+ rate, new_rate);
+
+ dev_dbg(&dispc->pdev->dev, "New VP rate %lu Hz (requested %lu Hz)\n",
+ clk_get_rate(dispc->vp_clk), rate);
+
+ return 0;
+}
+
+/* CSC */
+
+struct color_conv_coef {
+ int ry, rcb, rcr;
+ int gy, gcb, gcr;
+ int by, bcb, bcr;
+ int roffset, goffset, boffset;
+ bool full_range;
+};
+
+static void dispc6_vid_write_color_conv_coefs(struct dispc_device *dispc,
+ enum omap_plane_id plane,
+ const struct color_conv_coef *ct)
+{
+#define CVAL(x, y) (FLD_VAL(x, 26, 16) | FLD_VAL(y, 10, 0))
+
+ dispc6_vid_write(dispc, plane, DISPC_VID_CONV_COEF(0), CVAL(ct->rcr, ct->ry));
+ dispc6_vid_write(dispc, plane, DISPC_VID_CONV_COEF(1), CVAL(ct->gy, ct->rcb));
+ dispc6_vid_write(dispc, plane, DISPC_VID_CONV_COEF(2), CVAL(ct->gcb, ct->gcr));
+ dispc6_vid_write(dispc, plane, DISPC_VID_CONV_COEF(3), CVAL(ct->bcr, ct->by));
+ dispc6_vid_write(dispc, plane, DISPC_VID_CONV_COEF(4), CVAL(0, ct->bcb));
+
+ dispc6_vid_write(dispc, plane, DISPC_VID_CONV_COEF(5),
+ FLD_VAL(ct->roffset, 15, 3) | FLD_VAL(ct->goffset, 31, 19));
+ dispc6_vid_write(dispc, plane, DISPC_VID_CONV_COEF(6),
+ FLD_VAL(ct->boffset, 15, 3));
+
+ VID_REG_FLD_MOD(dispc, plane, DISPC_VID_ATTRIBUTES, ct->full_range, 11, 11);
+
+#undef CVAL
+}
+
+static void dispc6_vid_csc_setup(struct dispc_device *dispc)
+{
+ /* YUV -> RGB, ITU-R BT.601, full range */
+ const struct color_conv_coef coefs_yuv2rgb_bt601_full = {
+ 256, 0, 358,
+ 256, -88, -182,
+ 256, 452, 0,
+ 0, -2048, -2048,
+ true,
+ };
+
+ dispc6_vid_write_color_conv_coefs(dispc, 0, &coefs_yuv2rgb_bt601_full);
+}
+
+static void dispc6_vid_csc_enable(struct dispc_device *dispc,
+ enum omap_plane_id plane, bool enable)
+{
+ VID_REG_FLD_MOD(dispc, plane, DISPC_VID_ATTRIBUTES, !!enable, 9, 9);
+}
+
+/* SCALER */
+
+static u32 dispc6_calc_fir_inc(u32 in, u32 out)
+{
+ return (u32)div_u64(0x200000ull * in, out);
+}
+
+struct dispc6_vid_fir_coefs {
+ s16 c2[16];
+ s16 c1[16];
+ u16 c0[9];
+};
+
+static const struct dispc6_vid_fir_coefs dispc6_fir_coefs_null = {
+ .c2 = { 0 },
+ .c1 = { 0 },
+ .c0 = { 512, 512, 512, 512, 512, 512, 512, 512, 256, },
+};
+
+/* M=8, Upscale x >= 1 */
+static const struct dispc6_vid_fir_coefs dispc6_fir_coefs_m8 = {
+ .c2 = { 0, -4, -8, -16, -24, -32, -40, -48, 0, 2, 4, 6, 8, 6, 4, 2, },
+ .c1 = { 0, 28, 56, 94, 132, 176, 220, 266, -56, -60, -64, -62, -60, -50, -40, -20, },
+ .c0 = { 512, 506, 500, 478, 456, 424, 392, 352, 312, },
+};
+
+/* 5-tap, M=22, Downscale Ratio 2.5 < x < 3 */
+static const struct dispc6_vid_fir_coefs dispc6_fir_coefs_m22_5tap = {
+ .c2 = { 16, 20, 24, 30, 36, 42, 48, 56, 0, 0, 0, 2, 4, 8, 12, 14, },
+ .c1 = { 132, 140, 148, 156, 164, 172, 180, 186, 64, 72, 80, 88, 96, 104, 112, 122, },
+ .c0 = { 216, 216, 216, 214, 212, 208, 204, 198, 192, },
+};
+
+/* 3-tap, M=22, Downscale Ratio 2.5 < x < 3 */
+static const struct dispc6_vid_fir_coefs dispc6_fir_coefs_m22_3tap = {
+ .c1 = { 100, 118, 136, 156, 176, 196, 216, 236, 0, 10, 20, 30, 40, 54, 68, 84, },
+ .c0 = { 312, 310, 308, 302, 296, 286, 276, 266, 256, },
+};
+
+enum dispc6_vid_fir_coef_set {
+ DISPC6_VID_FIR_COEF_HORIZ,
+ DISPC6_VID_FIR_COEF_HORIZ_UV,
+ DISPC6_VID_FIR_COEF_VERT,
+ DISPC6_VID_FIR_COEF_VERT_UV,
+};
+
+static void dispc6_vid_write_fir_coefs(struct dispc_device *dispc,
+ enum omap_plane_id plane,
+ enum dispc6_vid_fir_coef_set coef_set,
+ const struct dispc6_vid_fir_coefs *coefs)
+{
+ static const u16 c0_regs[] = {
+ [DISPC6_VID_FIR_COEF_HORIZ] = DISPC_VID_FIR_COEFS_H0,
+ [DISPC6_VID_FIR_COEF_HORIZ_UV] = DISPC_VID_FIR_COEFS_H0_C,
+ [DISPC6_VID_FIR_COEF_VERT] = DISPC_VID_FIR_COEFS_V0,
+ [DISPC6_VID_FIR_COEF_VERT_UV] = DISPC_VID_FIR_COEFS_V0_C,
+ };
+
+ static const u16 c12_regs[] = {
+ [DISPC6_VID_FIR_COEF_HORIZ] = DISPC_VID_FIR_COEFS_H12,
+ [DISPC6_VID_FIR_COEF_HORIZ_UV] = DISPC_VID_FIR_COEFS_H12_C,
+ [DISPC6_VID_FIR_COEF_VERT] = DISPC_VID_FIR_COEFS_V12,
+ [DISPC6_VID_FIR_COEF_VERT_UV] = DISPC_VID_FIR_COEFS_V12_C,
+ };
+
+ const u16 c0_base = c0_regs[coef_set];
+ const u16 c12_base = c12_regs[coef_set];
+ int phase;
+
+ for (phase = 0; phase <= 8; ++phase) {
+ u16 reg = c0_base + phase * 4;
+ u16 c0 = coefs->c0[phase];
+
+ dispc6_vid_write(dispc, plane, reg, c0);
+ }
+
+ for (phase = 0; phase <= 15; ++phase) {
+ u16 reg = c12_base + phase * 4;
+ s16 c1, c2;
+ u32 c12;
+
+ c1 = coefs->c1[phase];
+ c2 = coefs->c2[phase];
+ c12 = FLD_VAL(c1, 19, 10) | FLD_VAL(c2, 29, 20);
+
+ dispc6_vid_write(dispc, plane, reg, c12);
+ }
+}
+
+static void dispc6_vid_write_scale_coefs(struct dispc_device *dispc,
+ enum omap_plane_id plane)
+{
+ dispc6_vid_write_fir_coefs(dispc, plane, DISPC6_VID_FIR_COEF_HORIZ, &dispc6_fir_coefs_null);
+ dispc6_vid_write_fir_coefs(dispc, plane, DISPC6_VID_FIR_COEF_HORIZ_UV, &dispc6_fir_coefs_null);
+ dispc6_vid_write_fir_coefs(dispc, plane, DISPC6_VID_FIR_COEF_VERT, &dispc6_fir_coefs_null);
+ dispc6_vid_write_fir_coefs(dispc, plane, DISPC6_VID_FIR_COEF_VERT_UV, &dispc6_fir_coefs_null);
+}
+
+static void dispc6_vid_set_scaling(struct dispc_device *dispc,
+ enum omap_plane_id plane,
+ u32 orig_width, u32 orig_height,
+ u32 out_width, u32 out_height,
+ u32 fourcc)
+{
+ u32 in_w, in_h, in_w_uv, in_h_uv;
+ u32 fir_hinc, fir_vinc, fir_hinc_uv, fir_vinc_uv;
+ bool scale_x, scale_y;
+ bool five_taps = false; /* XXX always 3-tap for now */
+
+ in_w = in_w_uv = orig_width;
+ in_h = in_h_uv = orig_height;
+
+ switch (fourcc) {
+ case DRM_FORMAT_NV12:
+ /* UV is subsampled by 2 horizontally and vertically */
+ in_h_uv >>= 1;
+ in_w_uv >>= 1;
+ break;
+
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_UYVY:
+ /* UV is subsampled by 2 horizontally */
+ in_w_uv >>= 1;
+ break;
+
+ default:
+ break;
+ }
+
+ scale_x = in_w != out_width || in_w_uv != out_width;
+ scale_y = in_h != out_height || in_h_uv != out_height;
+
+ /* HORIZONTAL RESIZE ENABLE */
+ VID_REG_FLD_MOD(dispc, plane, DISPC_VID_ATTRIBUTES, scale_x, 7, 7);
+
+ /* VERTICAL RESIZE ENABLE */
+ VID_REG_FLD_MOD(dispc, plane, DISPC_VID_ATTRIBUTES, scale_y, 8, 8);
+
+ /* Skip the rest if no scaling is used */
+ if (!scale_x && !scale_y)
+ return;
+
+ /* VERTICAL 5-TAPS */
+ VID_REG_FLD_MOD(dispc, plane, DISPC_VID_ATTRIBUTES, five_taps, 21, 21);
+
+ /* FIR INC */
+
+ fir_hinc = dispc6_calc_fir_inc(in_w, out_width);
+ fir_vinc = dispc6_calc_fir_inc(in_h, out_height);
+ fir_hinc_uv = dispc6_calc_fir_inc(in_w_uv, out_width);
+ fir_vinc_uv = dispc6_calc_fir_inc(in_h_uv, out_height);
+
+ dispc6_vid_write(dispc, plane, DISPC_VID_FIRH, fir_hinc);
+ dispc6_vid_write(dispc, plane, DISPC_VID_FIRV, fir_vinc);
+ dispc6_vid_write(dispc, plane, DISPC_VID_FIRH2, fir_hinc_uv);
+ dispc6_vid_write(dispc, plane, DISPC_VID_FIRV2, fir_vinc_uv);
+
+ dispc6_vid_write_scale_coefs(dispc, plane);
+}
+
+/* OTHER */
+
+static const enum omap_color_mode dispc6_supported_formats[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_RGBX8888,
+ DRM_FORMAT_RGBA8888,
+ DRM_FORMAT_YUYV,
+ DRM_FORMAT_UYVY,
+ DRM_FORMAT_NV12,
+ 0
+};
+
+static const struct {
+ u32 fourcc;
+ u8 dss_code;
+ u8 bytespp;
+} dispc6_color_formats[] = {
+ { DRM_FORMAT_XRGB8888, 0x27, 4, },
+ { DRM_FORMAT_ARGB8888, 0x7, 4, },
+
+ { DRM_FORMAT_RGBX8888, 0x29, 4, },
+ { DRM_FORMAT_RGBA8888, 0x9, 4, },
+
+ { DRM_FORMAT_YUYV, 0x3e, 2, },
+ { DRM_FORMAT_UYVY, 0x3f, 2, },
+
+ { DRM_FORMAT_NV12, 0x3d, 2, },
+};
+
+static bool dispc6_fourcc_is_yuv(u32 fourcc)
+{
+ switch (fourcc) {
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_UYVY:
+ case DRM_FORMAT_NV12:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void dispc6_ovl_set_pixel_format(struct dispc_device *dispc,
+ enum omap_plane_id plane, u32 fourcc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dispc6_color_formats); ++i) {
+ if (dispc6_color_formats[i].fourcc == fourcc) {
+ VID_REG_FLD_MOD(dispc, plane, DISPC_VID_ATTRIBUTES,
+ dispc6_color_formats[i].dss_code,
+ 6, 1);
+ return;
+ }
+ }
+
+ __WARN();
+}
+
+static int dispc6_fourcc_to_bytespp(u32 fourcc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dispc6_color_formats); ++i) {
+ if (dispc6_color_formats[i].fourcc == fourcc)
+ return dispc6_color_formats[i].bytespp;
+ }
+
+ __WARN();
+ return 4;
+}
+
+static s32 pixinc(int pixels, u8 ps)
+{
+ if (pixels == 1)
+ return 1;
+ else if (pixels > 1)
+ return 1 + (pixels - 1) * ps;
+ else if (pixels < 0)
+ return 1 - (-pixels + 1) * ps;
+
+ __WARN();
+ return 0;
+}
+
+static int dispc6_ovl_setup(struct dispc_device *dispc,
+ enum omap_plane_id plane,
+ const struct omap_overlay_info *oi,
+ const struct videomode *vm, bool mem_to_mem,
+ enum omap_channel channel)
+{
+ u32 fourcc = oi->fourcc;
+ int bytespp = dispc6_fourcc_to_bytespp(fourcc);
+
+ dispc6_ovl_set_pixel_format(dispc, plane, fourcc);
+
+ dispc6_vid_write(dispc, plane, DISPC_VID_BA_0, oi->paddr);
+ dispc6_vid_write(dispc, plane, DISPC_VID_BA_1, oi->paddr);
+
+ dispc6_vid_write(dispc, plane, DISPC_VID_BA_UV_0, oi->p_uv_addr);
+ dispc6_vid_write(dispc, plane, DISPC_VID_BA_UV_1, oi->p_uv_addr);
+
+ dispc6_vid_write(dispc, plane, DISPC_VID_PICTURE_SIZE,
+ (oi->width - 1) | ((oi->height - 1) << 16));
+
+ dispc6_vid_write(dispc, plane, DISPC_VID_PIXEL_INC, pixinc(1, bytespp));
+ dispc6_vid_write(dispc, plane, DISPC_VID_ROW_INC,
+ pixinc(1 + oi->screen_width - oi->width, bytespp));
+
+ dispc6_vid_write(dispc, plane, DISPC_VID_POSITION,
+ oi->pos_x | (oi->pos_y << 16));
+
+ dispc6_vid_write(dispc, plane, DISPC_VID_SIZE,
+ (oi->out_width - 1) | ((oi->out_height - 1) << 16));
+
+ dispc6_vid_set_scaling(dispc, plane,
+ oi->width, oi->height, oi->out_width, oi->out_height,
+ fourcc);
+
+ /* enable YUV->RGB color conversion */
+ if (dispc6_fourcc_is_yuv(fourcc))
+ dispc6_vid_csc_enable(dispc, plane, true);
+ else
+ dispc6_vid_csc_enable(dispc, plane, false);
+
+ /* channel */
+ VID_REG_FLD_MOD(dispc, plane, DISPC_VID_ATTRIBUTES, 0, 16, 14);
+
+ return 0;
+}
+
+static int dispc6_ovl_enable(struct dispc_device *dispc,
+ enum omap_plane_id plane, bool enable)
+{
+ VID_REG_FLD_MOD(dispc, plane, DISPC_VID_ATTRIBUTES, !!enable, 0, 0);
+ return 0;
+}
+
+static u32 dispc6_vid_get_fifo_size(struct dispc_device *dispc,
+ enum omap_plane_id plane)
+{
+ const u32 unit_size = 16; /* 128-bits */
+
+ return VID_REG_GET(dispc, plane, DISPC_VID_BUF_SIZE_STATUS, 15, 0) * unit_size;
+}
+
+static void dispc6_vid_set_mflag_threshold(struct dispc_device *dispc,
+ enum omap_plane_id plane,
+ u32 low, u32 high)
+{
+ dispc6_vid_write(dispc, plane, DISPC_VID_MFLAG_THRESHOLD,
+ FLD_VAL(high, 31, 16) | FLD_VAL(low, 15, 0));
+}
+
+static void dispc6_mflag_setup(struct dispc_device *dispc)
+{
+ enum omap_plane_id plane = 0;
+ const u32 unit_size = 16; /* 128-bits */
+ u32 size = dispc6_vid_get_fifo_size(dispc, plane);
+ u32 low, high;
+
+ /* MFLAG_CTRL */
+ REG_FLD_MOD(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE, 1, 1, 0);
+ /* MFLAG_START */
+ REG_FLD_MOD(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE, 0, 2, 2);
+
+ /*
+ * Simulation team suggests below thesholds:
+ * HT = fifosize * 5 / 8;
+ * LT = fifosize * 4 / 8;
+ */
+
+ low = size * 4 / 8 / unit_size;
+ high = size * 5 / 8 / unit_size;
+
+ dispc6_vid_set_mflag_threshold(dispc, plane, low, high);
+}
+
+static void dispc6_vp_setup(struct dispc_device *dispc)
+{
+ /* Enable the gamma Shadow bit-field */
+ VP_REG_FLD_MOD(dispc, 0, DISPC_VP_CONFIG, 1, 2, 2);
+}
+
+static void dispc6_initial_config(struct dispc_device *dispc)
+{
+ dispc6_vid_csc_setup(dispc);
+ dispc6_mflag_setup(dispc);
+ dispc6_vp_setup(dispc);
+}
+
+static int dispc6_init_features(struct dispc_device *dispc)
+{
+ struct platform_device *pdev = dispc->pdev;
+ const struct of_device_id *match;
+
+ match = of_match_node(dispc6_of_match, pdev->dev.of_node);
+ if (!match) {
+ dev_err(&pdev->dev, "Unsupported DISPC version\n");
+ return -ENODEV;
+ }
+
+ dispc->feat = match->data;
+
+ return 0;
+}
+
+static enum omap_dss_output_id
+dispc6_mgr_get_supported_outputs(struct dispc_device *dispc,
+ enum omap_channel channel)
+{
+ return OMAP_DSS_OUTPUT_DPI;
+}
+
+static const u32 *dispc6_ovl_get_color_modes(struct dispc_device *dispc,
+ enum omap_plane_id plane)
+{
+ return dispc6_supported_formats;
+}
+
+static int dispc6_get_num_ovls(struct dispc_device *dispc)
+{
+ return 1;
+}
+
+static int dispc6_get_num_mgrs(struct dispc_device *dispc)
+{
+ return 1;
+}
+
+static u32 dispc6_mgr_gamma_size(struct dispc_device *dispc,
+ enum omap_channel channel)
+{
+ return ARRAY_SIZE(dispc->gamma_table);
+}
+
+static void dispc6_mgr_write_gamma_table(struct dispc_device *dispc,
+ enum omap_channel channel)
+{
+ u32 *table = dispc->gamma_table;
+ uint hwlen = ARRAY_SIZE(dispc->gamma_table);
+ unsigned int i;
+
+ dev_dbg(&dispc->pdev->dev, "%s: channel %d\n", __func__, channel);
+
+ for (i = 0; i < hwlen; ++i) {
+ u32 v = table[i];
+
+ v |= i << 24;
+
+ dispc6_vp_write(dispc, channel, DISPC_VP_GAMMA_TABLE, v);
+ }
+}
+
+static void dispc6_restore_gamma_tables(struct dispc_device *dispc)
+{
+ dev_dbg(&dispc->pdev->dev, "%s()\n", __func__);
+
+ dispc6_mgr_write_gamma_table(dispc, 0);
+}
+
+static const struct drm_color_lut dispc6_mgr_gamma_default_lut[] = {
+ { .red = 0, .green = 0, .blue = 0, },
+ { .red = U16_MAX, .green = U16_MAX, .blue = U16_MAX, },
+};
+
+static void dispc6_mgr_set_gamma(struct dispc_device *dispc,
+ enum omap_channel channel,
+ const struct drm_color_lut *lut,
+ unsigned int length)
+{
+ u32 *table = dispc->gamma_table;
+ uint hwlen = ARRAY_SIZE(dispc->gamma_table);
+ static const uint hwbits = 8;
+ uint i;
+
+ dev_dbg(&dispc->pdev->dev, "%s: channel %d, lut len %u, hw len %u\n",
+ __func__, channel, length, hwlen);
+
+ if (lut == NULL || length < 2) {
+ lut = dispc6_mgr_gamma_default_lut;
+ length = ARRAY_SIZE(dispc6_mgr_gamma_default_lut);
+ }
+
+ for (i = 0; i < length - 1; ++i) {
+ uint first = i * (hwlen - 1) / (length - 1);
+ uint last = (i + 1) * (hwlen - 1) / (length - 1);
+ uint w = last - first;
+ u16 r, g, b;
+ uint j;
+
+ if (w == 0)
+ continue;
+
+ for (j = 0; j <= w; j++) {
+ r = (lut[i].red * (w - j) + lut[i+1].red * j) / w;
+ g = (lut[i].green * (w - j) + lut[i+1].green * j) / w;
+ b = (lut[i].blue * (w - j) + lut[i+1].blue * j) / w;
+
+ r >>= 16 - hwbits;
+ g >>= 16 - hwbits;
+ b >>= 16 - hwbits;
+
+ table[first + j] = (r << (hwbits * 2)) |
+ (g << hwbits) | b;
+ }
+ }
+
+ if (dispc->is_enabled)
+ dispc6_mgr_write_gamma_table(dispc, channel);
+}
+
+static int dispc6_init_gamma_tables(struct dispc_device *dispc)
+{
+ dispc6_mgr_set_gamma(dispc, 0, NULL, 0);
+
+ return 0;
+}
+
+static u32 dispc6_get_memory_bandwidth_limit(struct dispc_device *dispc)
+{
+ u32 limit = 0;
+
+ /* Optional maximum memory bandwidth */
+ of_property_read_u32(dispc->pdev->dev.of_node, "max-memory-bandwidth",
+ &limit);
+
+ return limit;
+}
+
+static const char *dispc6_ovl_name(struct dispc_device *dispc,
+ enum omap_plane_id plane)
+{
+ return "vid1";
+}
+
+static const char *dispc6_mgr_name(struct dispc_device *dispc,
+ enum omap_channel channel)
+{
+ return "vp1";
+}
+
+static bool dispc6_mgr_has_framedone(struct dispc_device *dispc,
+ enum omap_channel channel)
+{
+ return true;
+}
+
+static const struct dispc_ops dispc6_ops = {
+ .read_and_clear_irqstatus = dispc6_read_and_clear_irqstatus,
+ .write_irqenable = dispc6_write_irqenable,
+
+ .request_irq = dispc6_request_irq,
+ .free_irq = dispc6_free_irq,
+
+ .runtime_get = dispc6_runtime_get,
+ .runtime_put = dispc6_runtime_put,
+
+ .get_num_ovls = dispc6_get_num_ovls,
+ .get_num_mgrs = dispc6_get_num_mgrs,
+
+ .ovl_name = dispc6_ovl_name,
+ .mgr_name = dispc6_mgr_name,
+
+ .mgr_has_framedone = dispc6_mgr_has_framedone,
+
+ .get_memory_bandwidth_limit = dispc6_get_memory_bandwidth_limit,
+
+ .mgr_enable = dispc6_mgr_enable,
+ .mgr_is_enabled = dispc6_mgr_is_enabled,
+ .mgr_go_busy = dispc6_mgr_go_busy,
+ .mgr_go = dispc6_mgr_go,
+ .mgr_set_lcd_config = dispc6_mgr_set_lcd_config,
+ .mgr_set_timings = dispc6_mgr_set_timings,
+ .mgr_setup = dispc6_mgr_setup,
+ .mgr_get_supported_outputs = dispc6_mgr_get_supported_outputs,
+ .mgr_gamma_size = dispc6_mgr_gamma_size,
+ .mgr_set_gamma = dispc6_mgr_set_gamma,
+
+ .ovl_enable = dispc6_ovl_enable,
+ .ovl_setup = dispc6_ovl_setup,
+ .ovl_get_color_modes = dispc6_ovl_get_color_modes,
+};
+
+static int dispc6_iomap_resource(struct platform_device *pdev, const char *name,
+ void __iomem **base)
+{
+ struct resource *res;
+ void __iomem *b;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+ if (!res) {
+ dev_err(&pdev->dev, "cannot get mem resource '%s'\n", name);
+ return -EINVAL;
+ }
+
+ b = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(b)) {
+ dev_err(&pdev->dev, "cannot ioremap resource '%s'\n", name);
+ return PTR_ERR(b);
+ }
+
+ *base = b;
+
+ return 0;
+}
+
+static int dispc6_bind(struct device *dev, struct device *master, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct dss6_device *dss6 = dss6_get_device(master);
+ struct dispc_device *dispc;
+ int r = 0;
+
+ dispc = kzalloc(sizeof(*dispc), GFP_KERNEL);
+ if (!dispc)
+ return -ENOMEM;
+
+ dispc->pdev = pdev;
+ platform_set_drvdata(pdev, dispc);
+ dispc->dss6 = dss6;
+
+ r = dispc6_init_features(dispc);
+ if (r)
+ goto err_free;
+
+ r = dispc6_iomap_resource(pdev, "common", &dispc->base_common);
+ if (r)
+ goto err_free;
+
+ r = dispc6_iomap_resource(pdev, "vid1", &dispc->base_vid1);
+ if (r)
+ goto err_free;
+
+ r = dispc6_iomap_resource(pdev, "ovr1", &dispc->base_ovr1);
+ if (r)
+ goto err_free;
+
+ r = dispc6_iomap_resource(pdev, "vp1", &dispc->base_vp1);
+ if (r)
+ goto err_free;
+
+ dispc->irq = platform_get_irq(dispc->pdev, 0);
+ if (dispc->irq < 0) {
+ dev_err(dev, "platform_get_irq failed\n");
+ r = -ENODEV;
+ goto err_free;
+ }
+
+ dispc->fclk = devm_clk_get(dev, "fck");
+ if (IS_ERR(dispc->fclk)) {
+ dev_err(dev, "Failed to get fclk\n");
+ r = PTR_ERR(dispc->fclk);
+ goto err_free;
+ }
+
+ dispc->vp_clk = devm_clk_get(dev, "vp");
+ if (IS_ERR(dispc->vp_clk)) {
+ dev_err(dev, "Failed to get vp clk\n");
+ r = PTR_ERR(dispc->vp_clk);
+ goto err_free;
+ }
+
+ r = dispc6_init_gamma_tables(dispc);
+ if (r)
+ goto err_free;
+
+ pm_runtime_enable(&pdev->dev);
+
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 200);
+ pm_runtime_use_autosuspend(&pdev->dev);
+
+ dss6->data.dispc_ops = &dispc6_ops;
+ dss6->data.dispc = dispc;
+
+ return 0;
+err_free:
+ kfree(dispc);
+ return r;
+}
+
+static void dispc6_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct dispc_device *dispc = dev_get_drvdata(dev);
+ struct dss6_device *dss6 = dispc->dss6;
+
+ dss6->data.dispc_ops = NULL;
+ dss6->data.dispc = NULL;
+
+ pm_runtime_disable(dev);
+
+ kfree(dispc);
+}
+
+static const struct component_ops dispc6_component_ops = {
+ .bind = dispc6_bind,
+ .unbind = dispc6_unbind,
+};
+
+static int dispc6_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &dispc6_component_ops);
+}
+
+static int dispc6_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &dispc6_component_ops);
+ return 0;
+}
+
+static int dispc6_runtime_suspend(struct device *dev)
+{
+ struct dispc_device *dispc = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "suspend\n");
+
+ dispc->is_enabled = false;
+ /* ensure the dispc6_irq_handler sees the is_enabled value */
+ smp_wmb();
+ /* wait for current handler to finish before turning the DISPC off */
+ synchronize_irq(dispc->irq);
+
+ dispc6_save_context(dispc);
+
+ clk_disable_unprepare(dispc->fclk);
+
+ return 0;
+}
+
+static int dispc6_runtime_resume(struct device *dev)
+{
+ struct dispc_device *dispc = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "resume\n");
+
+ clk_prepare_enable(dispc->fclk);
+
+ if (REG_GET(dispc, DISPC_SYSSTATUS, 0, 0) == 0)
+ dev_warn(dev, "DISPC FUNC RESET not done!\n");
+ if (REG_GET(dispc, DISPC_SYSSTATUS, 1, 1) == 0)
+ dev_warn(dev, "DISPC VP RESET not done!\n");
+
+ dispc6_initial_config(dispc);
+
+ dispc6_restore_context(dispc);
+
+ dispc6_restore_gamma_tables(dispc);
+
+ dispc->is_enabled = true;
+ /* ensure the dispc6_irq_handler sees the is_enabled value */
+ smp_wmb();
+
+ return 0;
+}
+
+static const struct dev_pm_ops dispc_pm_ops = {
+ .runtime_suspend = dispc6_runtime_suspend,
+ .runtime_resume = dispc6_runtime_resume,
+};
+
+static const struct of_device_id dispc6_of_match[] = {
+ { .compatible = "ti,k2g-dispc", .data = &k2g_dispc_feats, },
+ {},
+};
+
+static struct platform_driver dispc6_driver = {
+ .probe = dispc6_probe,
+ .remove = dispc6_remove,
+ .driver = {
+ .name = "omap_dispc6",
+ .pm = &dispc_pm_ops,
+ .of_match_table = dispc6_of_match,
+ .suppress_bind_attrs = true,
+ },
+};
+
+int __init dispc6_init_platform_driver(void)
+{
+ return platform_driver_register(&dispc6_driver);
+}
+
+void dispc6_uninit_platform_driver(void)
+{
+ platform_driver_unregister(&dispc6_driver);
+}
new file mode 100644
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ */
+
+#ifndef __OMAP2_DISPC6_REG_H
+#define __OMAP2_DISPC6_REG_H
+
+/* COMMON */
+
+#define DISPC_REVISION 0x000
+#define DISPC_SYSCONFIG 0x004
+#define DISPC_SYSSTATUS 0x008
+
+#define DISPC_IRQ_EOI 0x020
+#define DISPC_IRQSTATUS_RAW 0x024
+#define DISPC_IRQSTATUS 0x028
+#define DISPC_IRQENABLE_SET 0x02c
+#define DISPC_IRQENABLE_CLR 0x030
+#define DISPC_IRQWAKEEN 0x034
+
+#define DISPC_GLOBAL_MFLAG_ATTRIBUTE 0x040
+#define DISPC_GLOBAL_BUFFER 0x044
+#define DISPC_BA0_FLIPIMMEDIATE_EN 0x048
+
+#define DISPC_DBG_CONTROL 0x04c
+#define DISPC_DBG_STATUS 0x050
+
+#define DISPC_CLKGATING_DISABLE 0x054
+
+/* VID */
+
+#define DISPC_VID_ACCUH_0 0x0
+#define DISPC_VID_ACCUH_1 0x4
+#define DISPC_VID_ACCUH2_0 0x8
+#define DISPC_VID_ACCUH2_1 0xc
+
+#define DISPC_VID_ACCUV_0 0x10
+#define DISPC_VID_ACCUV_1 0x14
+#define DISPC_VID_ACCUV2_0 0x18
+#define DISPC_VID_ACCUV2_1 0x1c
+
+#define DISPC_VID_ATTRIBUTES 0x20
+#define DISPC_VID_ATTRIBUTES2 0x24
+
+#define DISPC_VID_BA_0 0x28
+#define DISPC_VID_BA_1 0x2c
+#define DISPC_VID_BA_UV_0 0x30
+#define DISPC_VID_BA_UV_1 0x34
+#define DISPC_VID_BUF_SIZE_STATUS 0x38
+#define DISPC_VID_BUF_THRESHOLD 0x3c
+
+#define DISPC_VID_CONV_COEF(n) (0x40 + (n) * 4)
+
+#define DISPC_VID_FIRH 0x5c
+#define DISPC_VID_FIRH2 0x60
+#define DISPC_VID_FIRV 0x64
+#define DISPC_VID_FIRV2 0x68
+
+#define DISPC_VID_FIR_COEFS_H0 0x6c
+#define DISPC_VID_FIR_COEF_H0(phase) (0x6c + (phase) * 4)
+#define DISPC_VID_FIR_COEFS_H0_C 0x90
+#define DISPC_VID_FIR_COEF_H0_C(phase) (0x90 + (phase) * 4)
+
+#define DISPC_VID_FIR_COEFS_H12 0xb4
+#define DISPC_VID_FIR_COEF_H12(phase) (0xb4 + (phase) * 4)
+#define DISPC_VID_FIR_COEFS_H12_C 0xf4
+#define DISPC_VID_FIR_COEF_H12_C(phase) (0xf4 + (phase) * 4)
+
+#define DISPC_VID_FIR_COEFS_V0 0x134
+#define DISPC_VID_FIR_COEF_V0(phase) (0x134 + (phase) * 4)
+#define DISPC_VID_FIR_COEFS_V0_C 0x158
+#define DISPC_VID_FIR_COEF_V0_C(phase) (0x158 + (phase) * 4)
+
+#define DISPC_VID_FIR_COEFS_V12 0x17c
+#define DISPC_VID_FIR_COEF_V12(phase) (0x17c + (phase) * 4)
+#define DISPC_VID_FIR_COEFS_V12_C 0x1bc
+#define DISPC_VID_FIR_COEF_V12_C(phase) (0x1bc + (phase) * 4)
+
+#define DISPC_VID_IRQENABLE 0x200
+#define DISPC_VID_IRQSTATUS 0x204
+
+#define DISPC_VID_MFLAG_THRESHOLD 0x208
+#define DISPC_VID_PICTURE_SIZE 0x20c
+#define DISPC_VID_PIXEL_INC 0x210
+#define DISPC_VID_POSITION 0x214
+#define DISPC_VID_PRELOAD 0x218
+#define DISPC_VID_ROW_INC 0x21c
+#define DISPC_VID_SIZE 0x220
+
+/* OVR */
+
+#define DISPC_OVR_DEFAULT_COLOR 0x08
+#define DISPC_OVR_DEFAULT_COLOR2 0x0c
+
+/* VP */
+
+#define DISPC_VP_CONFIG 0x00
+#define DISPC_VP_CONTROL 0x04
+#define DISPC_VP_GAMMA_TABLE 0x20
+#define DISPC_VP_IRQENABLE 0x3c
+#define DISPC_VP_IRQSTATUS 0x40
+#define DISPC_VP_POL_FREQ 0x4c
+#define DISPC_VP_SIZE_SCREEN 0x50
+#define DISPC_VP_TIMING_H 0x54
+#define DISPC_VP_TIMING_V 0x58
+
+#endif
new file mode 100644
@@ -0,0 +1,283 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include "omapdss.h"
+#include "dss6.h"
+
+struct dpi_data {
+ struct platform_device *pdev;
+
+ struct mutex lock;
+
+ struct videomode vm;
+ int data_lines;
+
+ struct omap_dss_device output;
+
+ bool port_initialized;
+
+ struct dss6_device *dss6;
+};
+
+static struct dpi_data *dpi6_get_data_from_dssdev(struct omap_dss_device *out)
+{
+ return container_of(out, struct dpi_data, output);
+}
+
+static void dpi6_config_lcd_manager(struct dpi_data *dpi)
+{
+ struct dss_lcd_mgr_config mgr_config = {
+ .io_pad_mode = DSS_IO_PAD_MODE_BYPASS,
+ .stallmode = false,
+ .fifohandcheck = false,
+ .video_port_width = dpi->data_lines,
+ .lcden_sig_polarity = 0,
+ };
+
+ dss_mgr_set_lcd_config(&dpi->output, &mgr_config);
+}
+
+static int dpi6_connect(struct omap_dss_device *out,
+ struct omap_dss_device *dst)
+{
+ struct dpi_data *dpi = dpi6_get_data_from_dssdev(out);
+ int r;
+
+ r = dss_mgr_connect(&dpi->output, out);
+ if (r)
+ return r;
+
+ r = omapdss_output_set_device(out, dst);
+ if (r) {
+ dev_err(&dpi->pdev->dev,
+ "failed to connect output to new device: %s\n",
+ dst->name);
+ dss_mgr_disconnect(&dpi->output, out);
+ return r;
+ }
+
+ return 0;
+}
+
+static void dpi6_disconnect(struct omap_dss_device *out,
+ struct omap_dss_device *dst)
+{
+ struct dpi_data *dpi = dpi6_get_data_from_dssdev(out);
+
+ WARN_ON(dst != out->dst);
+
+ if (dst != out->dst)
+ return;
+
+ omapdss_output_unset_device(out);
+
+ dss_mgr_disconnect(&dpi->output, out);
+}
+
+static int dpi6_display_enable(struct omap_dss_device *out)
+{
+ struct dpi_data *dpi = dpi6_get_data_from_dssdev(out);
+ enum omap_channel channel = out->dispc_channel;
+ int r;
+
+ mutex_lock(&dpi->lock);
+
+ if (!out->dispc_channel_connected) {
+ dev_err(&dpi->pdev->dev, "failed to enable display: no output channel set\n");
+ r = -ENODEV;
+ goto err_no_out_mgr;
+ }
+
+ r = dispc6_runtime_get(dpi->dss6->data.dispc);
+ if (r)
+ goto err_get_dispc;
+
+ r = dispc6_vp_set_clk_rate(dpi->dss6->data.dispc, channel,
+ dpi->vm.pixelclock);
+ if (r)
+ goto err_set_clk;
+
+ r = dispc6_vp_enable_clk(dpi->dss6->data.dispc, channel);
+ if (r)
+ goto err_enable_clk;
+
+ dpi6_config_lcd_manager(dpi);
+
+ r = dss_mgr_enable(&dpi->output);
+ if (r)
+ goto err_mgr_enable;
+
+ dss6_ungate_dpi_clk(&dpi->pdev->dev, out->port_num);
+
+ mutex_unlock(&dpi->lock);
+
+ return 0;
+
+err_mgr_enable:
+ dispc6_vp_disable_clk(dpi->dss6->data.dispc, channel);
+err_enable_clk:
+err_set_clk:
+ dispc6_runtime_put(dpi->dss6->data.dispc);
+err_get_dispc:
+err_no_out_mgr:
+ mutex_unlock(&dpi->lock);
+ return r;
+}
+
+static void dpi6_display_disable(struct omap_dss_device *out)
+{
+ struct dpi_data *dpi = dpi6_get_data_from_dssdev(out);
+ enum omap_channel channel = out->dispc_channel;
+
+ mutex_lock(&dpi->lock);
+
+ dss6_gate_dpi_clk(&dpi->pdev->dev, out->port_num);
+
+ dss_mgr_disable(&dpi->output);
+
+ dispc6_vp_disable_clk(dpi->dss6->data.dispc, channel);
+
+ dispc6_runtime_put(dpi->dss6->data.dispc);
+
+ mutex_unlock(&dpi->lock);
+}
+
+static int dpi6_check_timings(struct omap_dss_device *out,
+ struct videomode *vm)
+{
+ struct dpi_data *dpi = dpi6_get_data_from_dssdev(out);
+ enum omap_channel channel = out->dispc_channel;
+
+ if (!dispc6_mgr_timings_ok(dpi->dss6->data.dispc, channel, vm))
+ return -EINVAL;
+
+ return 0;
+}
+
+static void dpi6_set_timings(struct omap_dss_device *out,
+ struct videomode *vm)
+{
+ struct dpi_data *dpi = dpi6_get_data_from_dssdev(out);
+
+ mutex_lock(&dpi->lock);
+
+ dpi->vm = *vm;
+
+ mutex_unlock(&dpi->lock);
+}
+
+static void dpi6_get_timings(struct omap_dss_device *out,
+ struct videomode *vm)
+{
+ struct dpi_data *dpi = dpi6_get_data_from_dssdev(out);
+
+ mutex_lock(&dpi->lock);
+
+ *vm = dpi->vm;
+
+ mutex_unlock(&dpi->lock);
+}
+
+
+static const struct omapdss_dpi_ops dpi6_ops = {
+ .connect = dpi6_connect,
+ .disconnect = dpi6_disconnect,
+
+ .enable = dpi6_display_enable,
+ .disable = dpi6_display_disable,
+
+ .check_timings = dpi6_check_timings,
+ .set_timings = dpi6_set_timings,
+ .get_timings = dpi6_get_timings,
+};
+
+static void dpi6_setup_output_port(struct platform_device *pdev,
+ struct device_node *port)
+{
+ struct dpi_data *dpi = port->data;
+ struct omap_dss_device *out = &dpi->output;
+ int r;
+ u32 port_num;
+
+ r = of_property_read_u32(port, "reg", &port_num);
+ if (r)
+ port_num = 0;
+
+ switch (port_num) {
+ case 0:
+ default:
+ out->name = "dpi.0";
+ break;
+ }
+
+ out->dev = &pdev->dev;
+ out->id = OMAP_DSS_OUTPUT_DPI;
+ out->output_type = OMAP_DISPLAY_TYPE_DPI;
+ out->dispc_channel = OMAP_DSS_CHANNEL_LCD;
+ out->port_num = port_num;
+ out->ops.dpi = &dpi6_ops;
+ out->owner = THIS_MODULE;
+
+ omapdss_register_output(out);
+}
+
+int dpi6_init_port(struct platform_device *pdev, struct device_node *port)
+{
+ struct dss6_device *dss6 = dss6_get_device(&pdev->dev);
+ struct dpi_data *dpi;
+ struct device_node *ep;
+ u32 datalines;
+ int r;
+
+ dpi = devm_kzalloc(&pdev->dev, sizeof(*dpi), GFP_KERNEL);
+ if (!dpi)
+ return -ENOMEM;
+
+ ep = of_get_next_child(port, NULL);
+ if (!ep)
+ return 0;
+
+ r = of_property_read_u32(ep, "data-lines", &datalines);
+ if (r) {
+ dev_err(&dpi->pdev->dev, "failed to parse datalines\n");
+ goto err_datalines;
+ }
+
+ dpi->data_lines = datalines;
+
+ of_node_put(ep);
+
+ dpi->pdev = pdev;
+ port->data = dpi;
+
+ mutex_init(&dpi->lock);
+
+ dpi6_setup_output_port(pdev, port);
+
+ dpi->dss6 = dss6;
+ dpi->port_initialized = true;
+
+ return 0;
+
+err_datalines:
+ of_node_put(ep);
+
+ return r;
+}
+
+void dpi6_uninit_port(struct device_node *port)
+{
+ struct dpi_data *dpi = port->data;
+
+ if (!dpi->port_initialized)
+ return;
+
+ omapdss_unregister_output(&dpi->output);
+}
new file mode 100644
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+
+#include "omapdss.h"
+#include "dss6.h"
+
+#define DSS_REVISION 0x00
+#define DSS_SYSCONFIG 0x10
+#define DSS_SYSSTATUS 0x14
+#define DSS_RFBI_CTRL 0x18
+#define DSS_DPI_CTRL 0x1c
+#define DSS_DEBUG_CFG 0x40
+
+#define REG_GET(data, idx, start, end) \
+ FLD_GET(dss6_read(data, idx), start, end)
+
+#define REG_FLD_MOD(data, idx, val, start, end) \
+ dss6_write(data, idx, FLD_MOD(dss6_read(data, idx), val, start, end))
+
+struct dss_features {
+ int num_ports;
+};
+
+static const struct dss_features k2g_dss_feats = {
+ .num_ports = 1,
+};
+
+static const struct of_device_id dss6_of_match[];
+
+/* omapdrm device */
+
+static struct platform_device *omap_drm_device;
+
+static int initialize_omapdrm_device(void)
+{
+ omap_drm_device = platform_device_register_simple("omapdrm", 0, NULL, 0);
+ if (IS_ERR(omap_drm_device))
+ return PTR_ERR(omap_drm_device);
+
+ return 0;
+}
+
+/* omapdrm device end */
+
+static inline void dss6_write(struct dss6_device *dss6, u16 reg, u32 val)
+{
+ iowrite32(val, dss6->base + reg);
+}
+
+static inline u32 dss6_read(struct dss6_device *dss6, u16 reg)
+{
+ return ioread32(dss6->base + reg);
+}
+
+void dss6_ungate_dpi_clk(struct device *dev, int port)
+{
+ struct dss6_device *dss6 = dss6_get_device(dev);
+
+ REG_FLD_MOD(dss6, DSS_DPI_CTRL, 1, 1, 0);
+}
+
+void dss6_gate_dpi_clk(struct device *dev, int port)
+{
+ struct dss6_device *dss6 = dss6_get_device(dev);
+
+ REG_FLD_MOD(dss6, DSS_DPI_CTRL, 0, 1, 0);
+}
+
+static int dss6_init_features(struct platform_device *pdev)
+{
+ struct dss6_device *dss6 = dss6_get_device(&pdev->dev);
+ const struct of_device_id *match;
+
+ match = of_match_node(dss6_of_match, pdev->dev.of_node);
+ if (!match) {
+ dev_err(&pdev->dev, "Unsupported DSS version\n");
+ return -ENODEV;
+ }
+
+ dss6->feat = match->data;
+
+ return 0;
+}
+
+static int dss6_init_ports(struct platform_device *pdev)
+{
+ struct dss6_device *dss6 = dss6_get_device(&pdev->dev);
+ struct device_node *parent = pdev->dev.of_node;
+ struct device_node *port;
+ int i, ret;
+
+ for (i = 0; i < dss6->feat->num_ports; i++) {
+ port = of_graph_get_port_by_id(parent, i);
+ if (!port)
+ continue;
+
+ ret = dpi6_init_port(pdev, port);
+ of_node_put(port);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static void dss6_uninit_ports(struct platform_device *pdev)
+{
+ struct dss6_device *dss6 = dss6_get_device(&pdev->dev);
+ struct device_node *parent = pdev->dev.of_node;
+ struct device_node *port;
+ int i;
+
+ for (i = 0; i < dss6->feat->num_ports; i++) {
+ port = of_graph_get_port_by_id(parent, i);
+ if (!port)
+ continue;
+
+ dpi6_uninit_port(port);
+ of_node_put(port);
+ }
+}
+
+static int dss6_bind(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct dss6_device *dss6;
+ struct resource *dss_mem;
+ int r;
+
+ dss6 = devm_kzalloc(dev, sizeof(*dss6), GFP_KERNEL);
+ if (!dss6)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, dss6);
+ dss6->pdev = pdev;
+
+ r = dss6_init_features(dss6->pdev);
+ if (r)
+ goto err_features;
+
+ dss_mem = platform_get_resource(dss6->pdev, IORESOURCE_MEM, 0);
+ if (!dss_mem) {
+ dev_err(dev, "Failed to get IORESOURCE_MEM\n");
+ r = -EINVAL;
+ goto err_resource;
+ }
+
+ dss6->base = devm_ioremap(&pdev->dev, dss_mem->start,
+ resource_size(dss_mem));
+ if (!dss6->base) {
+ dev_err(dev, "Failed to ioremap\n");
+ r = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ r = dss6_init_ports(pdev);
+ if (r)
+ goto err_init_ports;
+
+ dss6->fclk = devm_clk_get(dev, "fck");
+ if (IS_ERR(dss6->fclk)) {
+ dev_err(dev, "Failed to get fclk\n");
+ r = PTR_ERR(dss6->fclk);
+ goto err_clk_get;
+ }
+
+ dev_dbg(&pdev->dev, "DSS fclk %lu Hz\n", clk_get_rate(dss6->fclk));
+
+ pm_runtime_enable(&pdev->dev);
+
+ r = component_bind_all(&pdev->dev, NULL);
+ if (r)
+ goto err_component;
+
+ r = initialize_omapdrm_device();
+ if (r)
+ goto err_omapdrm_device;
+
+ omapdss_gather_components(dev);
+ omapdss_set_dss(&dss6->data);
+
+ return 0;
+
+err_omapdrm_device:
+ component_unbind_all(&pdev->dev, NULL);
+err_component:
+ pm_runtime_disable(&pdev->dev);
+err_clk_get:
+err_init_ports:
+err_ioremap:
+err_resource:
+err_features:
+ kfree(dss6);
+ return r;
+}
+
+static void dss6_unbind(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ omapdss_set_dss(NULL);
+
+ platform_device_unregister(omap_drm_device);
+
+ component_unbind_all(&pdev->dev, NULL);
+
+ dss6_uninit_ports(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+}
+
+static const struct component_master_ops dss6_component_ops = {
+ .bind = dss6_bind,
+ .unbind = dss6_unbind,
+};
+
+static int dss6_component_compare(struct device *dev, void *data)
+{
+ struct device *child = data;
+ return dev == child;
+}
+
+static int dss6_add_child_component(struct device *dev, void *data)
+{
+ struct component_match **match = data;
+
+ component_match_add(dev->parent, match, dss6_component_compare, dev);
+
+ return 0;
+}
+
+static int dss6_probe(struct platform_device *pdev)
+{
+ struct component_match *match = NULL;
+ int r;
+
+ /* add all the child devices as components */
+ device_for_each_child(&pdev->dev, &match, dss6_add_child_component);
+
+ if (!match) {
+ dev_err(&pdev->dev, "No submodules found\n");
+ return -EINVAL;
+ }
+
+ r = component_master_add_with_match(&pdev->dev, &dss6_component_ops, match);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+static int dss6_remove(struct platform_device *pdev)
+{
+ component_master_del(&pdev->dev, &dss6_component_ops);
+ return 0;
+}
+
+static int dss6_runtime_suspend(struct device *dev)
+{
+ struct dss6_device *dss6 = dss6_get_device(dev);
+
+ clk_disable_unprepare(dss6->fclk);
+
+ return 0;
+}
+
+static int dss6_runtime_resume(struct device *dev)
+{
+ struct dss6_device *dss6 = dss6_get_device(dev);
+
+
+ clk_prepare_enable(dss6->fclk);
+
+ if (REG_GET(dss6, DSS_SYSSTATUS, 0, 0) == 0)
+ dev_warn(dev, "DSS FUNC RESET not done!\n");
+
+ return 0;
+}
+
+static const struct dev_pm_ops dss6_pm_ops = {
+ .runtime_suspend = dss6_runtime_suspend,
+ .runtime_resume = dss6_runtime_resume,
+};
+
+static const struct of_device_id dss6_of_match[] = {
+ { .compatible = "ti,k2g-dss", .data = &k2g_dss_feats, },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, dss6_of_match);
+
+static struct platform_driver dss6_driver = {
+ .probe = dss6_probe,
+ .remove = dss6_remove,
+ .driver = {
+ .name = "omap_dss6",
+ .pm = &dss6_pm_ops,
+ .of_match_table = dss6_of_match,
+ .suppress_bind_attrs = true,
+ },
+};
+
+static int __init omap_dss_init(void)
+{
+ int r;
+
+ r = platform_driver_register(&dss6_driver);
+ if (r)
+ return r;
+
+ r = dispc6_init_platform_driver();
+ if (r) {
+ platform_driver_unregister(&dss6_driver);
+ return r;
+ }
+
+ return 0;
+}
+
+static void __exit omap_dss_exit(void)
+{
+ dispc6_uninit_platform_driver();
+ platform_driver_unregister(&dss6_driver);
+}
+
+module_init(omap_dss_init);
+module_exit(omap_dss_exit);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
+MODULE_DESCRIPTION("OMAP6 Display Subsystem");
+MODULE_LICENSE("GPL v2");
new file mode 100644
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ */
+
+#ifndef __TI_DSS6_H
+#define __TI_DSS6_H
+
+#include "omapdss.h"
+
+struct dss6_device {
+ struct platform_device *pdev;
+ void __iomem *base;
+
+ struct clk *fclk;
+
+ const struct dss_features *feat;
+
+ struct dss_data data;
+};
+
+static inline struct dss6_device *dss6_get_device(struct device *dev)
+{
+ return dev_get_drvdata(dev);
+}
+
+/* DSS6 */
+void dss6_ungate_dpi_clk(struct device *dev, int port);
+void dss6_gate_dpi_clk(struct device *dev, int port);
+
+/* DISPC6 */
+int __init dispc6_init_platform_driver(void);
+void dispc6_uninit_platform_driver(void);
+
+int dispc6_runtime_get(struct dispc_device *dispc);
+void dispc6_runtime_put(struct dispc_device *dispc);
+
+bool dispc6_mgr_timings_ok(struct dispc_device *dispc,
+ enum omap_channel channel,
+ const struct videomode *vm);
+
+int dispc6_vp_set_clk_rate(struct dispc_device *dispc,
+ enum omap_channel channel, unsigned long rate);
+int dispc6_vp_enable_clk(struct dispc_device *dispc,
+ enum omap_channel channel);
+void dispc6_vp_disable_clk(struct dispc_device *dispc,
+ enum omap_channel channel);
+
+/* DPI6 */
+int dpi6_init_port(struct platform_device *pdev, struct device_node *port);
+void dpi6_uninit_port(struct device_node *port);
+
+#endif /* __TI_DSS6_H */