1
0
Fork 0

Allwinner sun4i DRM fixes for 4.9

A few fixes for the sun4i drm driver that range, including some fixes that
 might prevent multiple planes from working depending on the sequence where
 they are enabled.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJX44UwAAoJEBx+YmzsjxAgo+AQAKiv91r8qNidJ9I5dK4bLHVs
 8wUkGgmdMSN/TIDtdxRwcNKkCG6D+ieyUCkeU2Tvop9QSK4vO5ZVh23d8/H9GEPh
 ebhHoPUQ76JwrLLxD4bJZ7+dcgOExLOFuM54275SYh+YVzEzSdwY7z4kD3O0H9fm
 6RiuPVnehnkgyQGtUMpU/HfFhlwDEwpsJPbDb/RWnpN4C7o4kGP6+kDsJLh4vhyT
 2wd9XXCXMKQrrxx7krppqFlAcBDUIfOpQcvceDopG8/xGJaGRyH64UGMwjnPrdGS
 5kOqNg1/YwzSM2q/WSXziIh3o6RHuh9pyBnrcVPgpA731R744n9GrrclR/8Vkybp
 Bka1Rl4w7A/pw1HB2uOgH89KSFbb0rwEb5Zg0Oh4BfK6h2JUXkiscx4Q9JT5/H4T
 pUr51cxazGeHwV6KeMGt5ogpQcgtu93bcJnlbFtulnh/LQIXDVgFA/0oUK4vdnaz
 VRW8G8ZMl8sikMwxvyNzrf6iF5aXhM7eKa4xr4HqdND5skXe4CDZyvxZ/kvqO0eG
 0Pf4VmlcMP8VV/I1lyHPw9ggQLsEO0dgLvvWmkOu1uOc6yYTfKrf8GnGjfUabxYj
 3l3DIHnEBr6mFOFI/1PuD69urHt/IwnWsMCMdmbru+G74mKJ8cLLlEgF3FcyXEbV
 F8eSyPkSXVBTp4mV8kh3
 =Ondz
 -----END PGP SIGNATURE-----

Merge tag 'sunxi-drm-fixes-for-4.9' of https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux into drm-next

Allwinner sun4i DRM fixes for 4.9

A few fixes for the sun4i drm driver that range, including some fixes that
might prevent multiple planes from working depending on the sequence where
they are enabled.

* tag 'sunxi-drm-fixes-for-4.9' of https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux:
  drm/sun4i: Fix the high buffer address mask
  drm/sun4i: tv: Check mode pointer
  drm/sun4i: Fix formats usable by the primary plane
  drm/sun4i: dotclock: Round to closest clock rate
  drm/sun4i: Fix sparse warnings
  drm/sun4i: dotclock: Allow divider = 127
  drm/sun4i: dotclock: Fix clock rate read back calcation
  drm/sun4i: backend: remove redundant dev_err call in sun4i_backend_bind()
hifive-unleashed-5.1
Dave Airlie 2016-09-28 11:23:15 +10:00
commit a4a7fbb401
5 changed files with 80 additions and 48 deletions

View File

@ -83,8 +83,13 @@ void sun4i_backend_layer_enable(struct sun4i_backend *backend,
}
EXPORT_SYMBOL(sun4i_backend_layer_enable);
static int sun4i_backend_drm_format_to_layer(u32 format, u32 *mode)
static int sun4i_backend_drm_format_to_layer(struct drm_plane *plane,
u32 format, u32 *mode)
{
if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
(format == DRM_FORMAT_ARGB8888))
format = DRM_FORMAT_XRGB8888;
switch (format) {
case DRM_FORMAT_ARGB8888:
*mode = SUN4I_BACKEND_LAY_FBFMT_ARGB8888;
@ -164,7 +169,7 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend,
DRM_DEBUG_DRIVER("Switching display backend interlaced mode %s\n",
interlaced ? "on" : "off");
ret = sun4i_backend_drm_format_to_layer(fb->pixel_format, &val);
ret = sun4i_backend_drm_format_to_layer(plane, fb->pixel_format, &val);
if (ret) {
DRM_DEBUG_DRIVER("Invalid format\n");
return val;
@ -288,10 +293,8 @@ static int sun4i_backend_bind(struct device *dev, struct device *master,
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(dev, res);
if (IS_ERR(regs)) {
dev_err(dev, "Couldn't map the backend registers\n");
if (IS_ERR(regs))
return PTR_ERR(regs);
}
backend->regs = devm_regmap_init_mmio(dev, regs,
&sun4i_backend_regmap_config);

View File

@ -52,8 +52,8 @@
#define SUN4I_BACKEND_LAYFB_L32ADD_REG(l) (0x850 + (0x4 * (l)))
#define SUN4I_BACKEND_LAYFB_H4ADD_REG 0x860
#define SUN4I_BACKEND_LAYFB_H4ADD_MSK(l) GENMASK(3 + ((l) * 8), 0)
#define SUN4I_BACKEND_LAYFB_H4ADD(l, val) ((val) << ((l) * 8))
#define SUN4I_BACKEND_LAYFB_H4ADD_MSK(l) GENMASK(3 + ((l) * 8), (l) * 8)
#define SUN4I_BACKEND_LAYFB_H4ADD(l, val) ((val) << ((l) * 8))
#define SUN4I_BACKEND_REGBUFFCTL_REG 0x870
#define SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS BIT(1)

View File

@ -62,7 +62,7 @@ static unsigned long sun4i_dclk_recalc_rate(struct clk_hw *hw,
regmap_read(dclk->regmap, SUN4I_TCON0_DCLK_REG, &val);
val >>= SUN4I_TCON0_DCLK_DIV_SHIFT;
val &= SUN4I_TCON0_DCLK_DIV_WIDTH;
val &= (1 << SUN4I_TCON0_DCLK_DIV_WIDTH) - 1;
if (!val)
val = 1;
@ -77,7 +77,7 @@ static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
u8 best_div = 1;
int i;
for (i = 6; i < 127; i++) {
for (i = 6; i <= 127; i++) {
unsigned long ideal = rate * i;
unsigned long rounded;
@ -90,7 +90,8 @@ static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
goto out;
}
if ((rounded < ideal) && (rounded > best_parent)) {
if (abs(rate - rounded / i) <
abs(rate - best_parent / best_div)) {
best_parent = rounded;
best_div = i;
}

View File

@ -19,7 +19,12 @@
#include "sun4i_drv.h"
#include "sun4i_layer.h"
#define SUN4I_NUM_LAYERS 2
struct sun4i_plane_desc {
enum drm_plane_type type;
u8 pipe;
const uint32_t *formats;
uint32_t nformats;
};
static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
struct drm_plane_state *state)
@ -65,14 +70,35 @@ static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
.update_plane = drm_atomic_helper_update_plane,
};
static const uint32_t sun4i_backend_layer_formats[] = {
static const uint32_t sun4i_backend_layer_formats_primary[] = {
DRM_FORMAT_ARGB8888,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_RGB888,
DRM_FORMAT_XRGB8888,
};
static const uint32_t sun4i_backend_layer_formats_overlay[] = {
DRM_FORMAT_ARGB8888,
DRM_FORMAT_RGB888,
DRM_FORMAT_XRGB8888,
};
static const struct sun4i_plane_desc sun4i_backend_planes[] = {
{
.type = DRM_PLANE_TYPE_PRIMARY,
.pipe = 0,
.formats = sun4i_backend_layer_formats_primary,
.nformats = ARRAY_SIZE(sun4i_backend_layer_formats_primary),
},
{
.type = DRM_PLANE_TYPE_OVERLAY,
.pipe = 1,
.formats = sun4i_backend_layer_formats_overlay,
.nformats = ARRAY_SIZE(sun4i_backend_layer_formats_overlay),
},
};
static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
enum drm_plane_type type)
const struct sun4i_plane_desc *plane)
{
struct sun4i_drv *drv = drm->dev_private;
struct sun4i_layer *layer;
@ -84,10 +110,8 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
&sun4i_backend_layer_funcs,
sun4i_backend_layer_formats,
ARRAY_SIZE(sun4i_backend_layer_formats),
type,
NULL);
plane->formats, plane->nformats,
plane->type, NULL);
if (ret) {
dev_err(drm->dev, "Couldn't initialize layer\n");
return ERR_PTR(ret);
@ -97,7 +121,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
&sun4i_backend_layer_helper_funcs);
layer->drv = drv;
if (type == DRM_PLANE_TYPE_PRIMARY)
if (plane->type == DRM_PLANE_TYPE_PRIMARY)
drv->primary = &layer->plane;
return layer;
@ -109,8 +133,8 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
struct sun4i_layer **layers;
int i;
layers = devm_kcalloc(drm->dev, SUN4I_NUM_LAYERS, sizeof(**layers),
GFP_KERNEL);
layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun4i_backend_planes),
sizeof(**layers), GFP_KERNEL);
if (!layers)
return ERR_PTR(-ENOMEM);
@ -135,13 +159,11 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
* SoCs that support it, sprites could fill the need for more
* layers.
*/
for (i = 0; i < SUN4I_NUM_LAYERS; i++) {
enum drm_plane_type type = (i == 0)
? DRM_PLANE_TYPE_PRIMARY
: DRM_PLANE_TYPE_OVERLAY;
for (i = 0; i < ARRAY_SIZE(sun4i_backend_planes); i++) {
const struct sun4i_plane_desc *plane = &sun4i_backend_planes[i];
struct sun4i_layer *layer = layers[i];
layer = sun4i_layer_init_one(drm, type);
layer = sun4i_layer_init_one(drm, plane);
if (IS_ERR(layer)) {
dev_err(drm->dev, "Couldn't initialize %s plane\n",
i ? "overlay" : "primary");
@ -149,10 +171,10 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
};
DRM_DEBUG_DRIVER("Assigning %s plane to pipe %d\n",
i ? "overlay" : "primary", i);
i ? "overlay" : "primary", plane->pipe);
regmap_update_bits(drv->backend->regs, SUN4I_BACKEND_ATTCTL_REG0(i),
SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL_MASK,
SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(i));
SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(plane->pipe));
layer->id = i;
};

View File

@ -161,10 +161,10 @@ struct tv_mode {
bool dac3_en;
bool dac_bit25_en;
struct color_gains *color_gains;
struct burst_levels *burst_levels;
struct video_levels *video_levels;
struct resync_parameters *resync_params;
const struct color_gains *color_gains;
const struct burst_levels *burst_levels;
const struct video_levels *video_levels;
const struct resync_parameters *resync_params;
};
struct sun4i_tv {
@ -178,39 +178,39 @@ struct sun4i_tv {
struct sun4i_drv *drv;
};
struct video_levels ntsc_video_levels = {
static const struct video_levels ntsc_video_levels = {
.black = 282, .blank = 240,
};
struct video_levels pal_video_levels = {
static const struct video_levels pal_video_levels = {
.black = 252, .blank = 252,
};
struct burst_levels ntsc_burst_levels = {
static const struct burst_levels ntsc_burst_levels = {
.cb = 79, .cr = 0,
};
struct burst_levels pal_burst_levels = {
static const struct burst_levels pal_burst_levels = {
.cb = 40, .cr = 40,
};
struct color_gains ntsc_color_gains = {
static const struct color_gains ntsc_color_gains = {
.cb = 160, .cr = 160,
};
struct color_gains pal_color_gains = {
static const struct color_gains pal_color_gains = {
.cb = 224, .cr = 224,
};
struct resync_parameters ntsc_resync_parameters = {
static const struct resync_parameters ntsc_resync_parameters = {
.field = false, .line = 14, .pixel = 12,
};
struct resync_parameters pal_resync_parameters = {
static const struct resync_parameters pal_resync_parameters = {
.field = true, .line = 13, .pixel = 12,
};
struct tv_mode tv_modes[] = {
static const struct tv_mode tv_modes[] = {
{
.name = "NTSC",
.mode = SUN4I_TVE_CFG0_RES_480i,
@ -289,13 +289,13 @@ drm_connector_to_sun4i_tv(struct drm_connector *connector)
* So far, it doesn't seem to be preserved when the mode is passed by
* to mode_set for some reason.
*/
static struct tv_mode *sun4i_tv_find_tv_by_mode(struct drm_display_mode *mode)
static const struct tv_mode *sun4i_tv_find_tv_by_mode(const struct drm_display_mode *mode)
{
int i;
/* First try to identify the mode by name */
for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
struct tv_mode *tv_mode = &tv_modes[i];
const struct tv_mode *tv_mode = &tv_modes[i];
DRM_DEBUG_DRIVER("Comparing mode %s vs %s",
mode->name, tv_mode->name);
@ -306,7 +306,7 @@ static struct tv_mode *sun4i_tv_find_tv_by_mode(struct drm_display_mode *mode)
/* Then by number of lines */
for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
struct tv_mode *tv_mode = &tv_modes[i];
const struct tv_mode *tv_mode = &tv_modes[i];
DRM_DEBUG_DRIVER("Comparing mode %s vs %s (X: %d vs %d)",
mode->name, tv_mode->name,
@ -319,7 +319,7 @@ static struct tv_mode *sun4i_tv_find_tv_by_mode(struct drm_display_mode *mode)
return NULL;
}
static void sun4i_tv_mode_to_drm_mode(struct tv_mode *tv_mode,
static void sun4i_tv_mode_to_drm_mode(const struct tv_mode *tv_mode,
struct drm_display_mode *mode)
{
DRM_DEBUG_DRIVER("Creating mode %s\n", mode->name);
@ -386,7 +386,7 @@ static void sun4i_tv_mode_set(struct drm_encoder *encoder,
struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
struct sun4i_drv *drv = tv->drv;
struct sun4i_tcon *tcon = drv->tcon;
struct tv_mode *tv_mode = sun4i_tv_find_tv_by_mode(mode);
const struct tv_mode *tv_mode = sun4i_tv_find_tv_by_mode(mode);
sun4i_tcon1_mode_set(tcon, mode);
@ -507,8 +507,14 @@ static int sun4i_tv_comp_get_modes(struct drm_connector *connector)
int i;
for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
struct drm_display_mode *mode = drm_mode_create(connector->dev);
struct tv_mode *tv_mode = &tv_modes[i];
struct drm_display_mode *mode;
const struct tv_mode *tv_mode = &tv_modes[i];
mode = drm_mode_create(connector->dev);
if (!mode) {
DRM_ERROR("Failed to create a new display mode\n");
return 0;
}
strcpy(mode->name, tv_mode->name);