diff options
Diffstat (limited to 'drivers/gpu/drm/atmel-hlcdc')
-rw-r--r-- | drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | 100 | ||||
-rw-r--r-- | drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | 92 | ||||
-rw-r--r-- | drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 5 |
4 files changed, 157 insertions, 41 deletions
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c index d73281095fac..9e34bce089d0 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c @@ -101,18 +101,34 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c) (adj->crtc_hdisplay - 1) | ((adj->crtc_vdisplay - 1) << 16)); - cfg = 0; + cfg = ATMEL_HLCDC_CLKSEL; - prate = clk_get_rate(crtc->dc->hlcdc->sys_clk); + prate = 2 * clk_get_rate(crtc->dc->hlcdc->sys_clk); mode_rate = adj->crtc_clock * 1000; - if ((prate / 2) < mode_rate) { - prate *= 2; - cfg |= ATMEL_HLCDC_CLKSEL; - } div = DIV_ROUND_UP(prate, mode_rate); - if (div < 2) + if (div < 2) { div = 2; + } else if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK) { + /* The divider ended up too big, try a lower base rate. */ + cfg &= ~ATMEL_HLCDC_CLKSEL; + prate /= 2; + div = DIV_ROUND_UP(prate, mode_rate); + if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK) + div = ATMEL_HLCDC_CLKDIV_MASK; + } else { + int div_low = prate / mode_rate; + + if (div_low >= 2 && + ((prate / div_low - mode_rate) < + 10 * (mode_rate - prate / div))) + /* + * At least 10 times better when using a higher + * frequency than requested, instead of a lower. + * So, go with that. + */ + div = div_low; + } cfg |= ATMEL_HLCDC_CLKDIV(div); @@ -226,6 +242,55 @@ static void atmel_hlcdc_crtc_atomic_enable(struct drm_crtc *c, #define ATMEL_HLCDC_RGB888_OUTPUT BIT(3) #define ATMEL_HLCDC_OUTPUT_MODE_MASK GENMASK(3, 0) +static int atmel_hlcdc_connector_output_mode(struct drm_connector_state *state) +{ + struct drm_connector *connector = state->connector; + struct drm_display_info *info = &connector->display_info; + struct drm_encoder *encoder; + unsigned int supported_fmts = 0; + int j; + + encoder = state->best_encoder; + if (!encoder) + encoder = connector->encoder; + + switch (atmel_hlcdc_encoder_get_bus_fmt(encoder)) { + case 0: + break; + case MEDIA_BUS_FMT_RGB444_1X12: + return ATMEL_HLCDC_RGB444_OUTPUT; + case MEDIA_BUS_FMT_RGB565_1X16: + return ATMEL_HLCDC_RGB565_OUTPUT; + case MEDIA_BUS_FMT_RGB666_1X18: + return ATMEL_HLCDC_RGB666_OUTPUT; + case MEDIA_BUS_FMT_RGB888_1X24: + return ATMEL_HLCDC_RGB888_OUTPUT; + default: + return -EINVAL; + } + + for (j = 0; j < info->num_bus_formats; j++) { + switch (info->bus_formats[j]) { + case MEDIA_BUS_FMT_RGB444_1X12: + supported_fmts |= ATMEL_HLCDC_RGB444_OUTPUT; + break; + case MEDIA_BUS_FMT_RGB565_1X16: + supported_fmts |= ATMEL_HLCDC_RGB565_OUTPUT; + break; + case MEDIA_BUS_FMT_RGB666_1X18: + supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT; + break; + case MEDIA_BUS_FMT_RGB888_1X24: + supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT; + break; + default: + break; + } + } + + return supported_fmts; +} + static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state) { unsigned int output_fmts = ATMEL_HLCDC_OUTPUT_MODE_MASK; @@ -238,31 +303,12 @@ static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state) crtc = drm_crtc_to_atmel_hlcdc_crtc(state->crtc); for_each_new_connector_in_state(state->state, connector, cstate, i) { - struct drm_display_info *info = &connector->display_info; unsigned int supported_fmts = 0; - int j; if (!cstate->crtc) continue; - for (j = 0; j < info->num_bus_formats; j++) { - switch (info->bus_formats[j]) { - case MEDIA_BUS_FMT_RGB444_1X12: - supported_fmts |= ATMEL_HLCDC_RGB444_OUTPUT; - break; - case MEDIA_BUS_FMT_RGB565_1X16: - supported_fmts |= ATMEL_HLCDC_RGB565_OUTPUT; - break; - case MEDIA_BUS_FMT_RGB666_1X18: - supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT; - break; - case MEDIA_BUS_FMT_RGB888_1X24: - supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT; - break; - default: - break; - } - } + supported_fmts = atmel_hlcdc_connector_output_mode(cstate); if (crtc->dc->desc->conflicting_output_formats) output_fmts &= supported_fmts; diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h index 60c937f42114..4cc1e03f0aee 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h @@ -441,5 +441,6 @@ void atmel_hlcdc_crtc_irq(struct drm_crtc *c); int atmel_hlcdc_crtc_create(struct drm_device *dev); int atmel_hlcdc_create_outputs(struct drm_device *dev); +int atmel_hlcdc_encoder_get_bus_fmt(struct drm_encoder *encoder); #endif /* DRM_ATMEL_HLCDC_H */ diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c index 8db51fb131db..f73d8a92274e 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c @@ -27,33 +27,94 @@ #include "atmel_hlcdc_dc.h" +struct atmel_hlcdc_rgb_output { + struct drm_encoder encoder; + int bus_fmt; +}; + static const struct drm_encoder_funcs atmel_hlcdc_panel_encoder_funcs = { .destroy = drm_encoder_cleanup, }; +static struct atmel_hlcdc_rgb_output * +atmel_hlcdc_encoder_to_rgb_output(struct drm_encoder *encoder) +{ + return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder); +} + +int atmel_hlcdc_encoder_get_bus_fmt(struct drm_encoder *encoder) +{ + struct atmel_hlcdc_rgb_output *output; + + output = atmel_hlcdc_encoder_to_rgb_output(encoder); + + return output->bus_fmt; +} + +static int atmel_hlcdc_of_bus_fmt(const struct device_node *ep) +{ + u32 bus_width; + int ret; + + ret = of_property_read_u32(ep, "bus-width", &bus_width); + if (ret == -EINVAL) + return 0; + if (ret) + return ret; + + switch (bus_width) { + case 12: + return MEDIA_BUS_FMT_RGB444_1X12; + case 16: + return MEDIA_BUS_FMT_RGB565_1X16; + case 18: + return MEDIA_BUS_FMT_RGB666_1X18; + case 24: + return MEDIA_BUS_FMT_RGB888_1X24; + default: + return -EINVAL; + } +} + static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) { - struct drm_encoder *encoder; + struct atmel_hlcdc_rgb_output *output; + struct device_node *ep; struct drm_panel *panel; struct drm_bridge *bridge; int ret; + ep = of_graph_get_endpoint_by_regs(dev->dev->of_node, 0, endpoint); + if (!ep) + return -ENODEV; + ret = drm_of_find_panel_or_bridge(dev->dev->of_node, 0, endpoint, &panel, &bridge); - if (ret) + if (ret) { + of_node_put(ep); return ret; + } - encoder = devm_kzalloc(dev->dev, sizeof(*encoder), GFP_KERNEL); - if (!encoder) + output = devm_kzalloc(dev->dev, sizeof(*output), GFP_KERNEL); + if (!output) { + of_node_put(ep); + return -ENOMEM; + } + + output->bus_fmt = atmel_hlcdc_of_bus_fmt(ep); + of_node_put(ep); + if (output->bus_fmt < 0) { + dev_err(dev->dev, "endpoint %d: invalid bus width\n", endpoint); return -EINVAL; + } - ret = drm_encoder_init(dev, encoder, + ret = drm_encoder_init(dev, &output->encoder, &atmel_hlcdc_panel_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL); if (ret) return ret; - encoder->possible_crtcs = 0x1; + output->encoder.possible_crtcs = 0x1; if (panel) { bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_Unknown); @@ -62,7 +123,7 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) } if (bridge) { - ret = drm_bridge_attach(encoder, bridge, NULL); + ret = drm_bridge_attach(&output->encoder, bridge, NULL); if (!ret) return 0; @@ -70,7 +131,7 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) drm_panel_bridge_remove(bridge); } - drm_encoder_cleanup(encoder); + drm_encoder_cleanup(&output->encoder); return ret; } @@ -78,12 +139,23 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint) int atmel_hlcdc_create_outputs(struct drm_device *dev) { int endpoint, ret = 0; + int attached = 0; - for (endpoint = 0; !ret; endpoint++) + /* + * Always scan the first few endpoints even if we get -ENODEV, + * but keep going after that as long as we keep getting hits. + */ + for (endpoint = 0; !ret || endpoint < 4; endpoint++) { ret = atmel_hlcdc_attach_endpoint(dev, endpoint); + if (ret == -ENODEV) + continue; + if (ret) + break; + attached++; + } /* At least one device was successfully attached.*/ - if (ret == -ENODEV && endpoint) + if (ret == -ENODEV && attached) return 0; return ret; diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c index 04440064b9b7..9330a076e15a 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c @@ -942,10 +942,7 @@ static void atmel_hlcdc_plane_reset(struct drm_plane *p) "Failed to allocate initial plane state\n"); return; } - - p->state = &state->base; - p->state->alpha = DRM_BLEND_ALPHA_OPAQUE; - p->state->plane = p; + __drm_atomic_helper_plane_reset(p, &state->base); } } |