Merge tag 'drm-misc-next-2019-03-21' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for 5.2: UAPI Changes: - Add Colorspace connector property (Uma) - fourcc: Several new YUV formats from ARM (Brian & Ayan) - fourcc: Fix merge conflicts between new formats above and Swati's that went in via topic/hdr-formats-2019-03-07 branch (Maarten) Cross-subsystem Changes: - Typed component support via topic/component-typed-2019-02-11 (Maxime/Daniel) Core Changes: - Improve component helper documentation (Daniel) - Avoid calling drm_dev_unregister() twice on unplugged devices (Noralf) - Add device managed (devm) drm_device init function (Noralf) - Graduate TINYDRM_MODE to DRM_SIMPLE_MODE in core (Noralf) - Move MIPI/DSI rate control params computation into core from i915 (David) - Add support for shmem backed gem objects (Noralf) Driver Changes: - various: Use of_node_name_eq for node name comparisons (Rob Herring) - sun4i: Add DSI burst mode support (Konstantin) - panel: Add Ronbo RB070D30 MIPI/DSI panel support (Konstantin) - virtio: A few prime improvements (Gerd) - tinydrm: Remove tinydrm_device (Noralf) - vc4: Add load tracker to driver to detect underflow in atomic check (Boris) - vboxvideo: Move it out of staging \o/ (Hans) - v3d: Add support for V3D v4.2 (Eric) Cc: Konstantin Sudakov <k.sudakov@integrasources.com> Cc: Rob Herring <robh@kernel.org> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Cc: Maxime Ripard <maxime.ripard@bootlin.com> Cc: Uma Shankar <uma.shankar@intel.com> Cc: Noralf Trønnes <noralf@tronnes.org> Cc: Gerd Hoffmann <kraxel@redhat.com> Cc: David Francis <David.Francis@amd.com> Cc: Boris Brezillon <boris.brezillon@bootlin.com> Cc: Eric Anholt <eric@anholt.net> Cc: Hans de Goede <hdegoede@redhat.com> Cc: Brian Starkey <brian.starkey@arm.com> Cc: Ayan Kumar Halder <ayan.halder@arm.com> Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> From: Sean Paul <sean@poorly.run> Link: https://patchwork.freedesktop.org/patch/msgid/20190321170805.GA50145@art_vandelay
This commit is contained in:
@@ -720,33 +720,22 @@ static int sun4i_backend_free_sat(struct device *dev) {
|
||||
*/
|
||||
static int sun4i_backend_of_get_id(struct device_node *node)
|
||||
{
|
||||
struct device_node *port, *ep;
|
||||
int ret = -EINVAL;
|
||||
struct device_node *ep, *remote;
|
||||
struct of_endpoint of_ep;
|
||||
|
||||
/* input is port 0 */
|
||||
port = of_graph_get_port_by_id(node, 0);
|
||||
if (!port)
|
||||
/* Input port is 0, and we want the first endpoint. */
|
||||
ep = of_graph_get_endpoint_by_regs(node, 0, -1);
|
||||
if (!ep)
|
||||
return -EINVAL;
|
||||
|
||||
/* try finding an upstream endpoint */
|
||||
for_each_available_child_of_node(port, ep) {
|
||||
struct device_node *remote;
|
||||
u32 reg;
|
||||
remote = of_graph_get_remote_endpoint(ep);
|
||||
of_node_put(ep);
|
||||
if (!remote)
|
||||
return -EINVAL;
|
||||
|
||||
remote = of_graph_get_remote_endpoint(ep);
|
||||
if (!remote)
|
||||
continue;
|
||||
|
||||
ret = of_property_read_u32(remote, "reg", ®);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
ret = reg;
|
||||
}
|
||||
|
||||
of_node_put(port);
|
||||
|
||||
return ret;
|
||||
of_graph_parse_endpoint(remote, &of_ep);
|
||||
of_node_put(remote);
|
||||
return of_ep.id;
|
||||
}
|
||||
|
||||
/* TODO: This needs to take multiple pipelines into account */
|
||||
|
@@ -20,7 +20,7 @@ struct sun4i_lvds {
|
||||
struct drm_connector connector;
|
||||
struct drm_encoder encoder;
|
||||
|
||||
struct sun4i_tcon *tcon;
|
||||
struct drm_panel *panel;
|
||||
};
|
||||
|
||||
static inline struct sun4i_lvds *
|
||||
@@ -41,9 +41,8 @@ static int sun4i_lvds_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct sun4i_lvds *lvds =
|
||||
drm_connector_to_sun4i_lvds(connector);
|
||||
struct sun4i_tcon *tcon = lvds->tcon;
|
||||
|
||||
return drm_panel_get_modes(tcon->panel);
|
||||
return drm_panel_get_modes(lvds->panel);
|
||||
}
|
||||
|
||||
static struct drm_connector_helper_funcs sun4i_lvds_con_helper_funcs = {
|
||||
@@ -54,9 +53,8 @@ static void
|
||||
sun4i_lvds_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct sun4i_lvds *lvds = drm_connector_to_sun4i_lvds(connector);
|
||||
struct sun4i_tcon *tcon = lvds->tcon;
|
||||
|
||||
drm_panel_detach(tcon->panel);
|
||||
drm_panel_detach(lvds->panel);
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
@@ -71,26 +69,24 @@ static const struct drm_connector_funcs sun4i_lvds_con_funcs = {
|
||||
static void sun4i_lvds_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
|
||||
struct sun4i_tcon *tcon = lvds->tcon;
|
||||
|
||||
DRM_DEBUG_DRIVER("Enabling LVDS output\n");
|
||||
|
||||
if (tcon->panel) {
|
||||
drm_panel_prepare(tcon->panel);
|
||||
drm_panel_enable(tcon->panel);
|
||||
if (lvds->panel) {
|
||||
drm_panel_prepare(lvds->panel);
|
||||
drm_panel_enable(lvds->panel);
|
||||
}
|
||||
}
|
||||
|
||||
static void sun4i_lvds_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
|
||||
struct sun4i_tcon *tcon = lvds->tcon;
|
||||
|
||||
DRM_DEBUG_DRIVER("Disabling LVDS output\n");
|
||||
|
||||
if (tcon->panel) {
|
||||
drm_panel_disable(tcon->panel);
|
||||
drm_panel_unprepare(tcon->panel);
|
||||
if (lvds->panel) {
|
||||
drm_panel_disable(lvds->panel);
|
||||
drm_panel_unprepare(lvds->panel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,11 +109,10 @@ int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
|
||||
lvds = devm_kzalloc(drm->dev, sizeof(*lvds), GFP_KERNEL);
|
||||
if (!lvds)
|
||||
return -ENOMEM;
|
||||
lvds->tcon = tcon;
|
||||
encoder = &lvds->encoder;
|
||||
|
||||
ret = drm_of_find_panel_or_bridge(tcon->dev->of_node, 1, 0,
|
||||
&tcon->panel, &bridge);
|
||||
&lvds->panel, &bridge);
|
||||
if (ret) {
|
||||
dev_info(drm->dev, "No panel or bridge found... LVDS output disabled\n");
|
||||
return 0;
|
||||
@@ -138,7 +133,7 @@ int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
|
||||
/* The LVDS encoder can only work with the TCON channel 0 */
|
||||
lvds->encoder.possible_crtcs = drm_crtc_mask(&tcon->crtc->crtc);
|
||||
|
||||
if (tcon->panel) {
|
||||
if (lvds->panel) {
|
||||
drm_connector_helper_add(&lvds->connector,
|
||||
&sun4i_lvds_con_helper_funcs);
|
||||
ret = drm_connector_init(drm, &lvds->connector,
|
||||
@@ -152,7 +147,7 @@ int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
|
||||
drm_connector_attach_encoder(&lvds->connector,
|
||||
&lvds->encoder);
|
||||
|
||||
ret = drm_panel_attach(tcon->panel, &lvds->connector);
|
||||
ret = drm_panel_attach(lvds->panel, &lvds->connector);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "Couldn't attach our panel\n");
|
||||
goto err_cleanup_connector;
|
||||
|
@@ -27,6 +27,8 @@ struct sun4i_rgb {
|
||||
struct drm_encoder encoder;
|
||||
|
||||
struct sun4i_tcon *tcon;
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge *bridge;
|
||||
};
|
||||
|
||||
static inline struct sun4i_rgb *
|
||||
@@ -47,11 +49,18 @@ static int sun4i_rgb_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct sun4i_rgb *rgb =
|
||||
drm_connector_to_sun4i_rgb(connector);
|
||||
struct sun4i_tcon *tcon = rgb->tcon;
|
||||
|
||||
return drm_panel_get_modes(tcon->panel);
|
||||
return drm_panel_get_modes(rgb->panel);
|
||||
}
|
||||
|
||||
/*
|
||||
* VESA DMT defines a tolerance of 0.5% on the pixel clock, while the
|
||||
* CVT spec reuses that tolerance in its examples, so it looks to be a
|
||||
* good default tolerance for the EDID-based modes. Define it to 5 per
|
||||
* mille to avoid floating point operations.
|
||||
*/
|
||||
#define SUN4I_RGB_DOTCLOCK_TOLERANCE_PER_MILLE 5
|
||||
|
||||
static enum drm_mode_status sun4i_rgb_mode_valid(struct drm_encoder *crtc,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
@@ -59,8 +68,9 @@ static enum drm_mode_status sun4i_rgb_mode_valid(struct drm_encoder *crtc,
|
||||
struct sun4i_tcon *tcon = rgb->tcon;
|
||||
u32 hsync = mode->hsync_end - mode->hsync_start;
|
||||
u32 vsync = mode->vsync_end - mode->vsync_start;
|
||||
unsigned long rate = mode->clock * 1000;
|
||||
long rounded_rate;
|
||||
unsigned long long rate = mode->clock * 1000;
|
||||
unsigned long long lowest, highest;
|
||||
unsigned long long rounded_rate;
|
||||
|
||||
DRM_DEBUG_DRIVER("Validating modes...\n");
|
||||
|
||||
@@ -92,15 +102,39 @@ static enum drm_mode_status sun4i_rgb_mode_valid(struct drm_encoder *crtc,
|
||||
|
||||
DRM_DEBUG_DRIVER("Vertical parameters OK\n");
|
||||
|
||||
/*
|
||||
* TODO: We should use the struct display_timing if available
|
||||
* and / or trying to stretch the timings within that
|
||||
* tolerancy to take care of panels that we wouldn't be able
|
||||
* to have a exact match for.
|
||||
*/
|
||||
if (rgb->panel) {
|
||||
DRM_DEBUG_DRIVER("RGB panel used, skipping clock rate checks");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* That shouldn't ever happen unless something is really wrong, but it
|
||||
* doesn't harm to check.
|
||||
*/
|
||||
if (!rgb->bridge)
|
||||
goto out;
|
||||
|
||||
tcon->dclk_min_div = 6;
|
||||
tcon->dclk_max_div = 127;
|
||||
rounded_rate = clk_round_rate(tcon->dclk, rate);
|
||||
if (rounded_rate < rate)
|
||||
|
||||
lowest = rate * (1000 - SUN4I_RGB_DOTCLOCK_TOLERANCE_PER_MILLE);
|
||||
do_div(lowest, 1000);
|
||||
if (rounded_rate < lowest)
|
||||
return MODE_CLOCK_LOW;
|
||||
|
||||
if (rounded_rate > rate)
|
||||
highest = rate * (1000 + SUN4I_RGB_DOTCLOCK_TOLERANCE_PER_MILLE);
|
||||
do_div(highest, 1000);
|
||||
if (rounded_rate > highest)
|
||||
return MODE_CLOCK_HIGH;
|
||||
|
||||
out:
|
||||
DRM_DEBUG_DRIVER("Clock rate OK\n");
|
||||
|
||||
return MODE_OK;
|
||||
@@ -114,9 +148,8 @@ static void
|
||||
sun4i_rgb_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct sun4i_rgb *rgb = drm_connector_to_sun4i_rgb(connector);
|
||||
struct sun4i_tcon *tcon = rgb->tcon;
|
||||
|
||||
drm_panel_detach(tcon->panel);
|
||||
drm_panel_detach(rgb->panel);
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
@@ -131,26 +164,24 @@ static const struct drm_connector_funcs sun4i_rgb_con_funcs = {
|
||||
static void sun4i_rgb_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(encoder);
|
||||
struct sun4i_tcon *tcon = rgb->tcon;
|
||||
|
||||
DRM_DEBUG_DRIVER("Enabling RGB output\n");
|
||||
|
||||
if (tcon->panel) {
|
||||
drm_panel_prepare(tcon->panel);
|
||||
drm_panel_enable(tcon->panel);
|
||||
if (rgb->panel) {
|
||||
drm_panel_prepare(rgb->panel);
|
||||
drm_panel_enable(rgb->panel);
|
||||
}
|
||||
}
|
||||
|
||||
static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(encoder);
|
||||
struct sun4i_tcon *tcon = rgb->tcon;
|
||||
|
||||
DRM_DEBUG_DRIVER("Disabling RGB output\n");
|
||||
|
||||
if (tcon->panel) {
|
||||
drm_panel_disable(tcon->panel);
|
||||
drm_panel_unprepare(tcon->panel);
|
||||
if (rgb->panel) {
|
||||
drm_panel_disable(rgb->panel);
|
||||
drm_panel_unprepare(rgb->panel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +203,6 @@ static struct drm_encoder_funcs sun4i_rgb_enc_funcs = {
|
||||
int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
|
||||
{
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_bridge *bridge;
|
||||
struct sun4i_rgb *rgb;
|
||||
int ret;
|
||||
|
||||
@@ -183,7 +213,7 @@ int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
|
||||
encoder = &rgb->encoder;
|
||||
|
||||
ret = drm_of_find_panel_or_bridge(tcon->dev->of_node, 1, 0,
|
||||
&tcon->panel, &bridge);
|
||||
&rgb->panel, &rgb->bridge);
|
||||
if (ret) {
|
||||
dev_info(drm->dev, "No panel or bridge found... RGB output disabled\n");
|
||||
return 0;
|
||||
@@ -204,7 +234,7 @@ int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
|
||||
/* The RGB encoder can only work with the TCON channel 0 */
|
||||
rgb->encoder.possible_crtcs = drm_crtc_mask(&tcon->crtc->crtc);
|
||||
|
||||
if (tcon->panel) {
|
||||
if (rgb->panel) {
|
||||
drm_connector_helper_add(&rgb->connector,
|
||||
&sun4i_rgb_con_helper_funcs);
|
||||
ret = drm_connector_init(drm, &rgb->connector,
|
||||
@@ -218,15 +248,15 @@ int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
|
||||
drm_connector_attach_encoder(&rgb->connector,
|
||||
&rgb->encoder);
|
||||
|
||||
ret = drm_panel_attach(tcon->panel, &rgb->connector);
|
||||
ret = drm_panel_attach(rgb->panel, &rgb->connector);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "Couldn't attach our panel\n");
|
||||
goto err_cleanup_connector;
|
||||
}
|
||||
}
|
||||
|
||||
if (bridge) {
|
||||
ret = drm_bridge_attach(encoder, bridge, NULL);
|
||||
if (rgb->bridge) {
|
||||
ret = drm_bridge_attach(encoder, rgb->bridge, NULL);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "Couldn't attach our bridge\n");
|
||||
goto err_cleanup_connector;
|
||||
|
@@ -341,8 +341,8 @@ static void sun4i_tcon0_mode_set_cpu(struct sun4i_tcon *tcon,
|
||||
u32 block_space, start_delay;
|
||||
u32 tcon_div;
|
||||
|
||||
tcon->dclk_min_div = 4;
|
||||
tcon->dclk_max_div = 127;
|
||||
tcon->dclk_min_div = SUN6I_DSI_TCON_DIV;
|
||||
tcon->dclk_max_div = SUN6I_DSI_TCON_DIV;
|
||||
|
||||
sun4i_tcon0_mode_set_common(tcon, mode);
|
||||
|
||||
|
@@ -257,8 +257,6 @@ struct sun4i_tcon {
|
||||
struct reset_control *lcd_rst;
|
||||
struct reset_control *lvds_rst;
|
||||
|
||||
struct drm_panel *panel;
|
||||
|
||||
/* Platform adjustments */
|
||||
const struct sun4i_tcon_quirks *quirks;
|
||||
|
||||
|
@@ -24,7 +24,9 @@
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
|
||||
#include "sun4i_crtc.h"
|
||||
#include "sun4i_drv.h"
|
||||
#include "sun4i_tcon.h"
|
||||
#include "sun6i_mipi_dsi.h"
|
||||
|
||||
#include <video/mipi_display.h>
|
||||
@@ -33,6 +35,8 @@
|
||||
#define SUN6I_DSI_CTL_EN BIT(0)
|
||||
|
||||
#define SUN6I_DSI_BASIC_CTL_REG 0x00c
|
||||
#define SUN6I_DSI_BASIC_CTL_TRAIL_INV(n) (((n) & 0xf) << 4)
|
||||
#define SUN6I_DSI_BASIC_CTL_TRAIL_FILL BIT(3)
|
||||
#define SUN6I_DSI_BASIC_CTL_HBP_DIS BIT(2)
|
||||
#define SUN6I_DSI_BASIC_CTL_HSA_HSE_DIS BIT(1)
|
||||
#define SUN6I_DSI_BASIC_CTL_VIDEO_BURST BIT(0)
|
||||
@@ -153,6 +157,8 @@
|
||||
|
||||
#define SUN6I_DSI_CMD_TX_REG(n) (0x300 + (n) * 0x04)
|
||||
|
||||
#define SUN6I_DSI_SYNC_POINT 40
|
||||
|
||||
enum sun6i_dsi_start_inst {
|
||||
DSI_START_LPRX,
|
||||
DSI_START_LPTX,
|
||||
@@ -358,7 +364,54 @@ static void sun6i_dsi_inst_init(struct sun6i_dsi *dsi,
|
||||
static u16 sun6i_dsi_get_video_start_delay(struct sun6i_dsi *dsi,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
return mode->vtotal - (mode->vsync_end - mode->vdisplay) + 1;
|
||||
u16 start = clamp(mode->vtotal - mode->vdisplay - 10, 8, 100);
|
||||
u16 delay = mode->vtotal - (mode->vsync_end - mode->vdisplay) + start;
|
||||
|
||||
if (delay > mode->vtotal)
|
||||
delay = delay % mode->vtotal;
|
||||
|
||||
return max_t(u16, delay, 1);
|
||||
}
|
||||
|
||||
static u16 sun6i_dsi_get_line_num(struct sun6i_dsi *dsi,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct mipi_dsi_device *device = dsi->device;
|
||||
unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8;
|
||||
|
||||
return mode->htotal * Bpp / device->lanes;
|
||||
}
|
||||
|
||||
static u16 sun6i_dsi_get_drq_edge0(struct sun6i_dsi *dsi,
|
||||
struct drm_display_mode *mode,
|
||||
u16 line_num, u16 edge1)
|
||||
{
|
||||
u16 edge0 = edge1;
|
||||
|
||||
edge0 += (mode->hdisplay + 40) * SUN6I_DSI_TCON_DIV / 8;
|
||||
|
||||
if (edge0 > line_num)
|
||||
return edge0 - line_num;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static u16 sun6i_dsi_get_drq_edge1(struct sun6i_dsi *dsi,
|
||||
struct drm_display_mode *mode,
|
||||
u16 line_num)
|
||||
{
|
||||
struct mipi_dsi_device *device = dsi->device;
|
||||
unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8;
|
||||
unsigned int hbp = mode->htotal - mode->hsync_end;
|
||||
u16 edge1;
|
||||
|
||||
edge1 = SUN6I_DSI_SYNC_POINT;
|
||||
edge1 += (mode->hdisplay + hbp + 20) * Bpp / device->lanes;
|
||||
|
||||
if (edge1 > line_num)
|
||||
return line_num;
|
||||
|
||||
return edge1;
|
||||
}
|
||||
|
||||
static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi,
|
||||
@@ -367,7 +420,23 @@ static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi,
|
||||
struct mipi_dsi_device *device = dsi->device;
|
||||
u32 val = 0;
|
||||
|
||||
if ((mode->hsync_end - mode->hdisplay) > 20) {
|
||||
if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
|
||||
u16 line_num = sun6i_dsi_get_line_num(dsi, mode);
|
||||
u16 edge0, edge1;
|
||||
|
||||
edge1 = sun6i_dsi_get_drq_edge1(dsi, mode, line_num);
|
||||
edge0 = sun6i_dsi_get_drq_edge0(dsi, mode, line_num, edge1);
|
||||
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BURST_DRQ_REG,
|
||||
SUN6I_DSI_BURST_DRQ_EDGE0(edge0) |
|
||||
SUN6I_DSI_BURST_DRQ_EDGE1(edge1));
|
||||
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BURST_LINE_REG,
|
||||
SUN6I_DSI_BURST_LINE_NUM(line_num) |
|
||||
SUN6I_DSI_BURST_LINE_SYNC_POINT(SUN6I_DSI_SYNC_POINT));
|
||||
|
||||
val = SUN6I_DSI_TCON_DRQ_ENABLE_MODE;
|
||||
} else if ((mode->hsync_end - mode->hdisplay) > 20) {
|
||||
/* Maaaaaagic */
|
||||
u16 drq = (mode->hsync_end - mode->hdisplay) - 20;
|
||||
|
||||
@@ -384,8 +453,19 @@ static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi,
|
||||
static void sun6i_dsi_setup_inst_loop(struct sun6i_dsi *dsi,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct mipi_dsi_device *device = dsi->device;
|
||||
u16 delay = 50 - 1;
|
||||
|
||||
if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
|
||||
delay = (mode->htotal - mode->hdisplay) * 150;
|
||||
delay /= (mode->clock / 1000) * 8;
|
||||
delay -= 50;
|
||||
}
|
||||
|
||||
regmap_write(dsi->regs, SUN6I_DSI_INST_LOOP_SEL_REG,
|
||||
2 << (4 * DSI_INST_ID_LP11) |
|
||||
3 << (4 * DSI_INST_ID_DLY));
|
||||
|
||||
regmap_write(dsi->regs, SUN6I_DSI_INST_LOOP_NUM_REG(0),
|
||||
SUN6I_DSI_INST_LOOP_NUM_N0(50 - 1) |
|
||||
SUN6I_DSI_INST_LOOP_NUM_N1(delay));
|
||||
@@ -451,49 +531,68 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi,
|
||||
{
|
||||
struct mipi_dsi_device *device = dsi->device;
|
||||
unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8;
|
||||
u16 hbp, hfp, hsa, hblk, vblk;
|
||||
u16 hbp = 0, hfp = 0, hsa = 0, hblk = 0, vblk = 0;
|
||||
u32 basic_ctl = 0;
|
||||
size_t bytes;
|
||||
u8 *buffer;
|
||||
|
||||
/* Do all timing calculations up front to allocate buffer space */
|
||||
|
||||
/*
|
||||
* A sync period is composed of a blanking packet (4 bytes +
|
||||
* payload + 2 bytes) and a sync event packet (4 bytes). Its
|
||||
* minimal size is therefore 10 bytes
|
||||
*/
|
||||
if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
|
||||
hblk = mode->hdisplay * Bpp;
|
||||
basic_ctl = SUN6I_DSI_BASIC_CTL_VIDEO_BURST |
|
||||
SUN6I_DSI_BASIC_CTL_HSA_HSE_DIS |
|
||||
SUN6I_DSI_BASIC_CTL_HBP_DIS;
|
||||
|
||||
if (device->lanes == 4)
|
||||
basic_ctl |= SUN6I_DSI_BASIC_CTL_TRAIL_FILL |
|
||||
SUN6I_DSI_BASIC_CTL_TRAIL_INV(0xc);
|
||||
} else {
|
||||
/*
|
||||
* A sync period is composed of a blanking packet (4
|
||||
* bytes + payload + 2 bytes) and a sync event packet
|
||||
* (4 bytes). Its minimal size is therefore 10 bytes
|
||||
*/
|
||||
#define HSA_PACKET_OVERHEAD 10
|
||||
hsa = max((unsigned int)HSA_PACKET_OVERHEAD,
|
||||
(mode->hsync_end - mode->hsync_start) * Bpp - HSA_PACKET_OVERHEAD);
|
||||
hsa = max((unsigned int)HSA_PACKET_OVERHEAD,
|
||||
(mode->hsync_end - mode->hsync_start) * Bpp - HSA_PACKET_OVERHEAD);
|
||||
|
||||
/*
|
||||
* The backporch is set using a blanking packet (4 bytes +
|
||||
* payload + 2 bytes). Its minimal size is therefore 6 bytes
|
||||
*/
|
||||
/*
|
||||
* The backporch is set using a blanking packet (4
|
||||
* bytes + payload + 2 bytes). Its minimal size is
|
||||
* therefore 6 bytes
|
||||
*/
|
||||
#define HBP_PACKET_OVERHEAD 6
|
||||
hbp = max((unsigned int)HBP_PACKET_OVERHEAD,
|
||||
(mode->hsync_start - mode->hdisplay) * Bpp - HBP_PACKET_OVERHEAD);
|
||||
hbp = max((unsigned int)HBP_PACKET_OVERHEAD,
|
||||
(mode->htotal - mode->hsync_end) * Bpp - HBP_PACKET_OVERHEAD);
|
||||
|
||||
/*
|
||||
* The frontporch is set using a blanking packet (4 bytes +
|
||||
* payload + 2 bytes). Its minimal size is therefore 6 bytes
|
||||
*/
|
||||
/*
|
||||
* The frontporch is set using a blanking packet (4
|
||||
* bytes + payload + 2 bytes). Its minimal size is
|
||||
* therefore 6 bytes
|
||||
*/
|
||||
#define HFP_PACKET_OVERHEAD 6
|
||||
hfp = max((unsigned int)HFP_PACKET_OVERHEAD,
|
||||
(mode->htotal - mode->hsync_end) * Bpp - HFP_PACKET_OVERHEAD);
|
||||
hfp = max((unsigned int)HFP_PACKET_OVERHEAD,
|
||||
(mode->hsync_start - mode->hdisplay) * Bpp - HFP_PACKET_OVERHEAD);
|
||||
|
||||
/*
|
||||
* hblk seems to be the line + porches length.
|
||||
*/
|
||||
hblk = mode->htotal * Bpp - hsa;
|
||||
/*
|
||||
* The blanking is set using a sync event (4 bytes)
|
||||
* and a blanking packet (4 bytes + payload + 2
|
||||
* bytes). Its minimal size is therefore 10 bytes.
|
||||
*/
|
||||
#define HBLK_PACKET_OVERHEAD 10
|
||||
hblk = max((unsigned int)HBLK_PACKET_OVERHEAD,
|
||||
(mode->htotal - (mode->hsync_end - mode->hsync_start)) * Bpp -
|
||||
HBLK_PACKET_OVERHEAD);
|
||||
|
||||
/*
|
||||
* And I'm not entirely sure what vblk is about. The driver in
|
||||
* Allwinner BSP is using a rather convoluted calculation
|
||||
* there only for 4 lanes. However, using 0 (the !4 lanes
|
||||
* case) even with a 4 lanes screen seems to work...
|
||||
*/
|
||||
vblk = 0;
|
||||
/*
|
||||
* And I'm not entirely sure what vblk is about. The driver in
|
||||
* Allwinner BSP is using a rather convoluted calculation
|
||||
* there only for 4 lanes. However, using 0 (the !4 lanes
|
||||
* case) even with a 4 lanes screen seems to work...
|
||||
*/
|
||||
vblk = 0;
|
||||
}
|
||||
|
||||
/* How many bytes do we need to send all payloads? */
|
||||
bytes = max_t(size_t, max(max(hfp, hblk), max(hsa, hbp)), vblk);
|
||||
@@ -501,7 +600,7 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi,
|
||||
if (WARN_ON(!buffer))
|
||||
return;
|
||||
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, 0);
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, basic_ctl);
|
||||
|
||||
regmap_write(dsi->regs, SUN6I_DSI_SYNC_HSS_REG,
|
||||
sun6i_dsi_build_sync_pkt(MIPI_DSI_H_SYNC_START,
|
||||
@@ -526,8 +625,8 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi,
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BASIC_SIZE0_REG,
|
||||
SUN6I_DSI_BASIC_SIZE0_VSA(mode->vsync_end -
|
||||
mode->vsync_start) |
|
||||
SUN6I_DSI_BASIC_SIZE0_VBP(mode->vsync_start -
|
||||
mode->vdisplay));
|
||||
SUN6I_DSI_BASIC_SIZE0_VBP(mode->vtotal -
|
||||
mode->vsync_end));
|
||||
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BASIC_SIZE1_REG,
|
||||
SUN6I_DSI_BASIC_SIZE1_VACT(mode->vdisplay) |
|
||||
|
@@ -13,6 +13,8 @@
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
|
||||
#define SUN6I_DSI_TCON_DIV 4
|
||||
|
||||
struct sun6i_dsi {
|
||||
struct drm_connector connector;
|
||||
struct drm_encoder encoder;
|
||||
|
@@ -325,38 +325,22 @@ static struct regmap_config sun8i_mixer_regmap_config = {
|
||||
|
||||
static int sun8i_mixer_of_get_id(struct device_node *node)
|
||||
{
|
||||
struct device_node *port, *ep;
|
||||
int ret = -EINVAL;
|
||||
struct device_node *ep, *remote;
|
||||
struct of_endpoint of_ep;
|
||||
|
||||
/* output is port 1 */
|
||||
port = of_graph_get_port_by_id(node, 1);
|
||||
if (!port)
|
||||
/* Output port is 1, and we want the first endpoint. */
|
||||
ep = of_graph_get_endpoint_by_regs(node, 1, -1);
|
||||
if (!ep)
|
||||
return -EINVAL;
|
||||
|
||||
/* try to find downstream endpoint */
|
||||
for_each_available_child_of_node(port, ep) {
|
||||
struct device_node *remote;
|
||||
u32 reg;
|
||||
remote = of_graph_get_remote_endpoint(ep);
|
||||
of_node_put(ep);
|
||||
if (!remote)
|
||||
return -EINVAL;
|
||||
|
||||
remote = of_graph_get_remote_endpoint(ep);
|
||||
if (!remote)
|
||||
continue;
|
||||
|
||||
ret = of_property_read_u32(remote, "reg", ®);
|
||||
if (!ret) {
|
||||
of_node_put(remote);
|
||||
of_node_put(ep);
|
||||
of_node_put(port);
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
of_node_put(remote);
|
||||
}
|
||||
|
||||
of_node_put(port);
|
||||
|
||||
return ret;
|
||||
of_graph_parse_endpoint(remote, &of_ep);
|
||||
of_node_put(remote);
|
||||
return of_ep.id;
|
||||
}
|
||||
|
||||
static int sun8i_mixer_bind(struct device *dev, struct device *master,
|
||||
@@ -554,6 +538,7 @@ static int sun8i_mixer_remove(struct platform_device *pdev)
|
||||
static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = {
|
||||
.ccsc = 0,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 2048,
|
||||
.ui_num = 3,
|
||||
.vi_num = 1,
|
||||
};
|
||||
@@ -561,6 +546,7 @@ static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = {
|
||||
static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = {
|
||||
.ccsc = 1,
|
||||
.scaler_mask = 0x3,
|
||||
.scanline_yuv = 2048,
|
||||
.ui_num = 1,
|
||||
.vi_num = 1,
|
||||
};
|
||||
@@ -569,6 +555,7 @@ static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = {
|
||||
.ccsc = 0,
|
||||
.mod_rate = 432000000,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 2048,
|
||||
.ui_num = 3,
|
||||
.vi_num = 1,
|
||||
};
|
||||
@@ -577,6 +564,7 @@ static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = {
|
||||
.ccsc = 0,
|
||||
.mod_rate = 297000000,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 2048,
|
||||
.ui_num = 3,
|
||||
.vi_num = 1,
|
||||
};
|
||||
@@ -585,6 +573,7 @@ static const struct sun8i_mixer_cfg sun8i_r40_mixer1_cfg = {
|
||||
.ccsc = 1,
|
||||
.mod_rate = 297000000,
|
||||
.scaler_mask = 0x3,
|
||||
.scanline_yuv = 2048,
|
||||
.ui_num = 1,
|
||||
.vi_num = 1,
|
||||
};
|
||||
@@ -593,6 +582,7 @@ static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
|
||||
.vi_num = 2,
|
||||
.ui_num = 1,
|
||||
.scaler_mask = 0x3,
|
||||
.scanline_yuv = 2048,
|
||||
.ccsc = 0,
|
||||
.mod_rate = 150000000,
|
||||
};
|
||||
@@ -601,6 +591,7 @@ static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = {
|
||||
.ccsc = 0,
|
||||
.mod_rate = 297000000,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 4096,
|
||||
.ui_num = 3,
|
||||
.vi_num = 1,
|
||||
};
|
||||
@@ -609,6 +600,7 @@ static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = {
|
||||
.ccsc = 1,
|
||||
.mod_rate = 297000000,
|
||||
.scaler_mask = 0x3,
|
||||
.scanline_yuv = 2048,
|
||||
.ui_num = 1,
|
||||
.vi_num = 1,
|
||||
};
|
||||
@@ -618,6 +610,7 @@ static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = {
|
||||
.is_de3 = true,
|
||||
.mod_rate = 600000000,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 4096,
|
||||
.ui_num = 3,
|
||||
.vi_num = 1,
|
||||
};
|
||||
|
@@ -159,6 +159,7 @@ struct de2_fmt_info {
|
||||
* @mod_rate: module clock rate that needs to be set in order to have
|
||||
* a functional block.
|
||||
* @is_de3: true, if this is next gen display engine 3.0, false otherwise.
|
||||
* @scaline_yuv: size of a scanline for VI scaler for YUV formats.
|
||||
*/
|
||||
struct sun8i_mixer_cfg {
|
||||
int vi_num;
|
||||
@@ -167,6 +168,7 @@ struct sun8i_mixer_cfg {
|
||||
int ccsc;
|
||||
unsigned long mod_rate;
|
||||
unsigned int is_de3 : 1;
|
||||
unsigned int scanline_yuv;
|
||||
};
|
||||
|
||||
struct sun8i_mixer {
|
||||
|
@@ -80,6 +80,8 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
|
||||
u32 bld_base, ch_base;
|
||||
u32 outsize, insize;
|
||||
u32 hphase, vphase;
|
||||
u32 hn = 0, hm = 0;
|
||||
u32 vn = 0, vm = 0;
|
||||
bool subsampled;
|
||||
|
||||
DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
|
||||
@@ -137,12 +139,41 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
|
||||
subsampled = format->hsub > 1 || format->vsub > 1;
|
||||
|
||||
if (insize != outsize || subsampled || hphase || vphase) {
|
||||
u32 hscale, vscale;
|
||||
unsigned int scanline, required;
|
||||
struct drm_display_mode *mode;
|
||||
u32 hscale, vscale, fps;
|
||||
u64 ability;
|
||||
|
||||
DRM_DEBUG_DRIVER("HW scaling is enabled\n");
|
||||
|
||||
hscale = state->src_w / state->crtc_w;
|
||||
vscale = state->src_h / state->crtc_h;
|
||||
mode = &plane->state->crtc->state->mode;
|
||||
fps = (mode->clock * 1000) / (mode->vtotal * mode->htotal);
|
||||
ability = clk_get_rate(mixer->mod_clk);
|
||||
/* BSP algorithm assumes 80% efficiency of VI scaler unit */
|
||||
ability *= 80;
|
||||
do_div(ability, mode->vdisplay * fps * max(src_w, dst_w));
|
||||
|
||||
required = src_h * 100 / dst_h;
|
||||
|
||||
if (ability < required) {
|
||||
DRM_DEBUG_DRIVER("Using vertical coarse scaling\n");
|
||||
vm = src_h;
|
||||
vn = (u32)ability * dst_h / 100;
|
||||
src_h = vn;
|
||||
}
|
||||
|
||||
/* it seems that every RGB scaler has buffer for 2048 pixels */
|
||||
scanline = subsampled ? mixer->cfg->scanline_yuv : 2048;
|
||||
|
||||
if (src_w > scanline) {
|
||||
DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n");
|
||||
hm = src_w;
|
||||
hn = scanline;
|
||||
src_w = hn;
|
||||
}
|
||||
|
||||
hscale = (src_w << 16) / dst_w;
|
||||
vscale = (src_h << 16) / dst_h;
|
||||
|
||||
sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w,
|
||||
dst_h, hscale, vscale, hphase, vphase,
|
||||
@@ -153,6 +184,23 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
|
||||
sun8i_vi_scaler_enable(mixer, channel, false);
|
||||
}
|
||||
|
||||
regmap_write(mixer->engine.regs,
|
||||
SUN8I_MIXER_CHAN_VI_HDS_Y(ch_base),
|
||||
SUN8I_MIXER_CHAN_VI_DS_N(hn) |
|
||||
SUN8I_MIXER_CHAN_VI_DS_M(hm));
|
||||
regmap_write(mixer->engine.regs,
|
||||
SUN8I_MIXER_CHAN_VI_HDS_UV(ch_base),
|
||||
SUN8I_MIXER_CHAN_VI_DS_N(hn) |
|
||||
SUN8I_MIXER_CHAN_VI_DS_M(hm));
|
||||
regmap_write(mixer->engine.regs,
|
||||
SUN8I_MIXER_CHAN_VI_VDS_Y(ch_base),
|
||||
SUN8I_MIXER_CHAN_VI_DS_N(vn) |
|
||||
SUN8I_MIXER_CHAN_VI_DS_M(vm));
|
||||
regmap_write(mixer->engine.regs,
|
||||
SUN8I_MIXER_CHAN_VI_VDS_UV(ch_base),
|
||||
SUN8I_MIXER_CHAN_VI_DS_N(vn) |
|
||||
SUN8I_MIXER_CHAN_VI_DS_M(vm));
|
||||
|
||||
/* Set base coordinates */
|
||||
DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
|
||||
state->dst.x1, state->dst.y1);
|
||||
|
@@ -24,6 +24,14 @@
|
||||
((base) + 0x30 * (layer) + 0x18 + 4 * (plane))
|
||||
#define SUN8I_MIXER_CHAN_VI_OVL_SIZE(base) \
|
||||
((base) + 0xe8)
|
||||
#define SUN8I_MIXER_CHAN_VI_HDS_Y(base) \
|
||||
((base) + 0xf0)
|
||||
#define SUN8I_MIXER_CHAN_VI_HDS_UV(base) \
|
||||
((base) + 0xf4)
|
||||
#define SUN8I_MIXER_CHAN_VI_VDS_Y(base) \
|
||||
((base) + 0xf8)
|
||||
#define SUN8I_MIXER_CHAN_VI_VDS_UV(base) \
|
||||
((base) + 0xfc)
|
||||
|
||||
#define SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN BIT(0)
|
||||
/* RGB mode should be set for RGB formats and cleared for YCbCr */
|
||||
@@ -33,6 +41,9 @@
|
||||
#define SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK GENMASK(31, 24)
|
||||
#define SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA(x) ((x) << 24)
|
||||
|
||||
#define SUN8I_MIXER_CHAN_VI_DS_N(x) ((x) << 16)
|
||||
#define SUN8I_MIXER_CHAN_VI_DS_M(x) ((x) << 0)
|
||||
|
||||
struct sun8i_mixer;
|
||||
|
||||
struct sun8i_vi_layer {
|
||||
|
Reference in New Issue
Block a user