On 09/06/2014 18:04, Boris BREZILLON wrote:
The Atmel HLCDC (High LCD Controller) IP available on some Atmel SoCs (i.e. at91sam9n12, at91sam9x5 family or sama5d3 family) provides a display controller device.
This display controller support at least one primary plane and might provide several overlays and an hardware cursor depending on the IP version.
Signed-off-by: Boris BREZILLON boris.brezillon@free-electrons.com
.../devicetree/bindings/drm/atmel-hlcdc-dc.txt | 59 ++ drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/atmel-hlcdc/Kconfig | 11 + drivers/gpu/drm/atmel-hlcdc/Makefile | 7 + drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | 529 ++++++++++++++++ drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 477 ++++++++++++++ drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h | 178 ++++++ drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c | 701 +++++++++++++++++++++ drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h | 417 ++++++++++++ drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_panel.c | 351 +++++++++++ drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c | 658 +++++++++++++++++++
drivers/gpu/drm/atmel_hlcdc/Kconfig | 11 + drivers/gpu/drm/atmel_hlcdc/Makefile | 8 +
These two files should not be part of the driver. I'll fix that.
14 files changed, 3410 insertions(+) create mode 100644 Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt create mode 100644 drivers/gpu/drm/atmel-hlcdc/Kconfig create mode 100644 drivers/gpu/drm/atmel-hlcdc/Makefile create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_panel.c create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c create mode 100644 drivers/gpu/drm/atmel_hlcdc/Kconfig create mode 100644 drivers/gpu/drm/atmel_hlcdc/Makefile
diff --git a/Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt b/Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt new file mode 100644 index 0000000..594bdb2 --- /dev/null +++ b/Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt @@ -0,0 +1,59 @@ +Device-Tree bindings for Atmel's HLCDC (High LCD Controller) DRM driver
+The Atmel HLCDC Display Controller is subdevice of the HLCDC MFD device. +See Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt for more details.
+Required properties:
- compatible: value should be one of the following:
- "atmel,hlcdc-dc"
- interrupts: the HLCDC interrupt definition
- pinctrl-names: the pin control state names. Should contain "default",
- "rgb-444", "rgb-565", "rgb-666" and "rgb-888".
- pinctrl-[0-4]: should contain the pinctrl states described by pinctrl
- names.
- atmel,panel: Should contain a phandle with 2 parameters.
- The first cell is a phandle to a DRM panel device
- The second cell encodes the RGB mode, which can take the following values:
- 0: RGB444
- 1: RGB565
- 2: RGB666
- 3: RGB888
- The third cell encodes specific flags describing LCD signals configuration
- (see Atmel's datasheet for a full description of these fields):
- bit 0: HSPOL: Horizontal Synchronization Pulse Polarity
- bit 1: VSPOL: Vertical Synchronization Pulse Polarity
- bit 2: VSPDLYS: Vertical Synchronization Pulse Start
- bit 3: VSPDLYE: Vertical Synchronization Pulse End
- bit 4: DISPPOL: Display Signal Polarity
- bit 7: DISPDLY: LCD Controller Display Power Signal Synchronization
- bit 12: VSPSU: LCD Controller Vertical synchronization Pulse Setup Configuration
- bit 13: VSPHO: LCD Controller Vertical synchronization Pulse Hold Configuration
- bit 16-20: GUARDTIME: LCD DISPLAY Guard Time
+Example:
- hlcdc: hlcdc@f0030000 {
compatible = "atmel,sama5d3-hlcdc";
reg = <0xf0030000 0x2000>;
clocks = <&lcdc_clk>, <&lcdck>, <&clk32k>;
clock-names = "periph_clk","sys_clk", "slow_clk";
status = "disabled";
hlcdc-display-controller {
compatible = "atmel,hlcdc-dc";
interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>;
pinctrl-names = "default", "rgb-444", "rgb-565", "rgb-666", "rgb-888";
pinctrl-0 = <&pinctrl_lcd_base>;
pinctrl-1 = <&pinctrl_lcd_base &pinctrl_lcd_rgb444>;
pinctrl-2 = <&pinctrl_lcd_base &pinctrl_lcd_rgb565>;
pinctrl-3 = <&pinctrl_lcd_base &pinctrl_lcd_rgb666>;
pinctrl-4 = <&pinctrl_lcd_base &pinctrl_lcd_rgb888>;
};
hlcdc_pwm: hlcdc-pwm {
compatible = "atmel,hlcdc-pwm";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_lcd_pwm>;
#pwm-cells = <3>;
};
- };
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index d1cc2f6..df6f0c1 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -182,6 +182,8 @@ source "drivers/gpu/drm/cirrus/Kconfig"
source "drivers/gpu/drm/armada/Kconfig"
+source "drivers/gpu/drm/atmel-hlcdc/Kconfig"
source "drivers/gpu/drm/rcar-du/Kconfig"
source "drivers/gpu/drm/shmobile/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 48e38ba..28c8a61 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_DRM_GMA500) += gma500/ obj-$(CONFIG_DRM_UDL) += udl/ obj-$(CONFIG_DRM_AST) += ast/ obj-$(CONFIG_DRM_ARMADA) += armada/ +obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel-hlcdc/ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ obj-$(CONFIG_DRM_OMAP) += omapdrm/ diff --git a/drivers/gpu/drm/atmel-hlcdc/Kconfig b/drivers/gpu/drm/atmel-hlcdc/Kconfig new file mode 100644 index 0000000..bc07315 --- /dev/null +++ b/drivers/gpu/drm/atmel-hlcdc/Kconfig @@ -0,0 +1,11 @@ +config DRM_ATMEL_HLCDC
- tristate "DRM Support for ATMEL HLCDC Display Controller"
- depends on DRM && OF && MFD_ATMEL_HLCDC && COMMON_CLK
- select DRM_GEM_CMA_HELPER
- select DRM_KMS_HELPER
- select DRM_KMS_FB_HELPER
- select DRM_KMS_CMA_HELPER
- select DRM_PANEL
- help
Choose this option if you have an ATMEL SoC with an HLCDC display
controller (i.e. at91sam9n12, at91sam9x5 family or sama5d3 family).
diff --git a/drivers/gpu/drm/atmel-hlcdc/Makefile b/drivers/gpu/drm/atmel-hlcdc/Makefile new file mode 100644 index 0000000..bf9fe0b --- /dev/null +++ b/drivers/gpu/drm/atmel-hlcdc/Makefile @@ -0,0 +1,7 @@ +atmel-hlcdc-dc-y := atmel_hlcdc_crtc.o \
atmel_hlcdc_dc.o \
atmel_hlcdc_layer.o \
atmel_hlcdc_panel.o \
atmel_hlcdc_plane.o
+obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel-hlcdc-dc.o diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c new file mode 100644 index 0000000..a18492e --- /dev/null +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c @@ -0,0 +1,529 @@ +/*
- Copyright (C) 2014 Traphandler
- Copyright (C) 2014 Free Electrons
- Author: Jean-Jacques Hiblot jjhiblot@traphandler.com
- Author: Boris BREZILLON boris.brezillon@free-electrons.com
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License version 2 as published by
- the Free Software Foundation.
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
- You should have received a copy of the GNU General Public License along with
- this program. If not, see http://www.gnu.org/licenses/.
- */
+#include <linux/clk.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h>
+#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drmP.h>
+#include <video/videomode.h>
+#include "atmel_hlcdc_dc.h"
+/**
- Structure storing hardware cursor informations like its position, its size
- or the GEM object currently used to display the HW cursor.
- @gem: the cursor GEM object
- @pitch: GEM object pitch
- @height: cursor height in pixels
- @width: cursor width in pixels
- @x: cursor x position
- @y: cursor y position
- @offset: start offset from the provided buffer
- @patched_height: patched height value after adapting the image size
depending on cursor position
- @patched_width: patched width value after adapting the image size depending
on cursor position
- @patched_x: patched x value after adapting to cursor position (positive
value)
- @patched_y: patched x value after adapting to cursor position (positive
value)
- */
+struct atmel_hlcdc_crtc_cursor_info {
- struct drm_gem_cma_object *gem;
- unsigned int pitch;
- u32 height;
- u32 width;
- int x;
- int y;
- /*
* These fields are automatically calculated by
* atmel_hlcdc_crtc_cursor_prepare_req.
*/
- unsigned int offset;
- u32 patched_height;
- u32 patched_width;
- int patched_x;
- int patched_y;
+};
+/**
- Structure storing HW cursor status.
- @status: the current cursor status
- @req: the requested cursor changes
- @plane: the hardware cursor plane
- @lock: cursor lock held when modifying cursor req or status
- */
+struct atmel_hlcdc_crtc_cursor {
- struct atmel_hlcdc_crtc_cursor_info status;
- struct atmel_hlcdc_crtc_cursor_info req;
- struct atmel_hlcdc_plane *plane;
- struct mutex lock;
+};
+/**
- Atmel HLCDC CRTC structure
- @base: base DRM CRTC structure
- @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
- @event: pointer to the current page flip event
- @id: CRTC id (returned by drm_crtc_index)
- @dpms: DPMS mode
- @cursor: hardware cursor status
- */
+struct atmel_hlcdc_crtc {
- struct drm_crtc base;
- struct atmel_hlcdc *hlcdc;
- struct drm_pending_vblank_event *event;
- int id;
- int dpms;
- struct atmel_hlcdc_crtc_cursor cursor;
+};
+static inline struct atmel_hlcdc_crtc * +drm_crtc_to_atmel_hlcdc_crtc(struct drm_crtc *crtc) +{
- return container_of(crtc, struct atmel_hlcdc_crtc, base);
+}
+static void atmel_hlcdc_crtc_dpms(struct drm_crtc *c, int mode) +{
- struct drm_device *dev = c->dev;
- if (mode != DRM_MODE_DPMS_ON)
mode = DRM_MODE_DPMS_OFF;
- pm_runtime_get_sync(dev->dev);
- if (mode == DRM_MODE_DPMS_ON)
pm_runtime_forbid(dev->dev);
- else
pm_runtime_allow(dev->dev);
- pm_runtime_put_sync(dev->dev);
+}
+static int atmel_hlcdc_crtc_mode_set(struct drm_crtc *c,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted,
int x, int y,
struct drm_framebuffer *old_fb)
+{
- struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
- struct regmap *regmap = crtc->hlcdc->regmap;
- struct drm_plane *plane = c->primary;
- struct drm_framebuffer *fb;
- struct videomode vm;
- vm.vfront_porch = mode->vsync_start - mode->vdisplay;
- vm.vback_porch = mode->vtotal - mode->vsync_end;
- vm.vsync_len = mode->vsync_end - mode->vsync_start;
- vm.hfront_porch = mode->hsync_start - mode->hdisplay;
- vm.hback_porch = mode->htotal - mode->hsync_end;
- vm.hsync_len = mode->hsync_end - mode->hsync_start;
- if (vm.hsync_len > 0x40 || vm.hsync_len < 0 ||
vm.vsync_len > 0x40 || vm.vsync_len < 0 ||
vm.vfront_porch > 0x40 || vm.vfront_porch < 0 ||
vm.vback_porch > 0x40 || vm.vback_porch < 0 ||
vm.hfront_porch > 0x200 || vm.hfront_porch < 0 ||
vm.hback_porch > 0x200 || vm.hback_porch < 0 ||
mode->hdisplay > 2048 || mode->hdisplay < 0 ||
mode->vdisplay > 2048 || mode->vdisplay < 0)
return -EINVAL;
- regmap_write(regmap, ATMEL_HLCDC_CFG(1),
(vm.hsync_len - 1) | ((vm.vsync_len - 1) << 16));
- regmap_write(regmap, ATMEL_HLCDC_CFG(2),
(vm.vfront_porch - 1) | ((vm.vback_porch - 1) << 16));
- regmap_write(regmap, ATMEL_HLCDC_CFG(3),
(vm.hfront_porch - 1) | ((vm.hback_porch - 1) << 16));
- regmap_write(regmap, ATMEL_HLCDC_CFG(4),
(mode->hdisplay - 1) | ((mode->vdisplay - 1) << 16));
- fb = plane->fb;
- plane->fb = old_fb;
- return plane->funcs->update_plane(plane, c, fb,
0, 0,
mode->hdisplay, mode->vdisplay,
c->x << 16, c->y << 16,
mode->hdisplay << 16,
mode->vdisplay << 16);
+}
+static void atmel_hlcdc_crtc_prepare(struct drm_crtc *crtc) +{
- atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+static void atmel_hlcdc_crtc_commit(struct drm_crtc *crtc) +{
- atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+}
+static bool atmel_hlcdc_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
+{
- return true;
+}
+static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = {
- .mode_fixup = atmel_hlcdc_crtc_mode_fixup,
- .dpms = atmel_hlcdc_crtc_dpms,
- .mode_set = atmel_hlcdc_crtc_mode_set,
- .prepare = atmel_hlcdc_crtc_prepare,
- .commit = atmel_hlcdc_crtc_commit,
+};
+static void atmel_hlcdc_crtc_destroy(struct drm_crtc *c) +{
- struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
- drm_crtc_cleanup(c);
- kfree(crtc);
+}
+void atmel_hlcdc_crtc_cancel_page_flip(struct drm_crtc *c,
struct drm_file *file)
+{
- struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
- struct drm_pending_vblank_event *event;
- struct drm_device *dev = c->dev;
- unsigned long flags;
- spin_lock_irqsave(&dev->event_lock, flags);
- event = crtc->event;
- if (event && event->base.file_priv == file) {
event->base.destroy(&event->base);
drm_vblank_put(dev, crtc->id);
crtc->event = NULL;
- }
- spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+static void atmel_hlcdc_crtc_finish_page_flip(void *data) +{
- struct atmel_hlcdc_crtc *crtc = data;
- struct drm_device *dev = crtc->base.dev;
- unsigned long flags;
- spin_lock_irqsave(&dev->event_lock, flags);
- if (crtc->event) {
drm_send_vblank_event(dev, crtc->id, crtc->event);
drm_vblank_put(dev, crtc->id);
crtc->event = NULL;
- }
- spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+static int atmel_hlcdc_crtc_page_flip(struct drm_crtc *c,
struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event,
uint32_t page_flip_flags)
+{
- struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
- struct drm_plane *plane = c->primary;
- if (crtc->event)
return -EBUSY;
- if (event) {
crtc->event = event;
drm_vblank_get(c->dev, crtc->id);
- }
- return plane->funcs->update_plane(plane, c, fb,
0, 0,
c->mode.hdisplay, c->mode.vdisplay,
c->x << 16, c->y << 16,
c->mode.hdisplay << 16,
c->mode.vdisplay << 16);
+}
+static void atmel_hlcdc_crtc_cursor_prepare_req(struct atmel_hlcdc_crtc *crtc) +{
- int bpp = drm_format_plane_cpp(DRM_FORMAT_ARGB8888, 0);
- struct atmel_hlcdc_crtc_cursor_info *info = &crtc->cursor.req;
- int x_offset = 0;
- int y_offset = 0;
- if (!info->gem) {
info->width = 0;
info->height = 0;
info->pitch = 0;
info->offset = 0;
return;
- }
- info->pitch = info->width * bpp;
- if (info->x + info->width > crtc->base.mode.hdisplay)
info->patched_width = crtc->base.mode.hdisplay - info->x;
- else
info->patched_width = info->width;
- if (info->x < 0) {
info->patched_width += info->x;
x_offset = -info->x;
info->patched_x = 0;
- } else {
info->patched_x = info->x;
- }
- if (info->y + info->height > crtc->base.mode.vdisplay)
info->patched_height = crtc->base.mode.vdisplay - info->y;
- else
info->patched_height = info->height;
- if (info->y < 0) {
info->patched_height += info->y;
y_offset = -info->y;
info->patched_y = 0;
- } else {
info->patched_y = info->y;
- }
- info->offset = (x_offset * bpp) +
(y_offset * info->pitch);
+}
+static int atmel_hlcdc_crtc_cursor_apply_req(struct atmel_hlcdc_crtc *crtc) +{
- struct atmel_hlcdc_plane *plane = crtc->cursor.plane;
- struct atmel_hlcdc_crtc_cursor_info *req = &crtc->cursor.req;
- struct atmel_hlcdc_crtc_cursor_info *status = &crtc->cursor.status;
- int ret;
- if (unlikely(!plane))
return -ENOTSUPP;
- if (!req->gem ||
!req->patched_width || !req->patched_height) {
ret = plane->base.funcs->disable_plane(&plane->base);
if (!ret)
goto out;
else
goto err;
- }
- ret = atmel_hlcdc_layer_update_start(&plane->layer);
- if (ret)
return ret;
- atmel_hlcdc_plane_update_general_settings(plane, DRM_FORMAT_ARGB8888);
- ret = atmel_hlcdc_plane_update_format(plane, DRM_FORMAT_ARGB8888);
- if (ret)
goto err;
- ret = atmel_hlcdc_plane_update_pos_and_size(plane, &crtc->base,
req->patched_x,
req->patched_y,
req->patched_width,
req->patched_height,
0, 0,
req->patched_width,
req->patched_height);
- if (ret)
goto err;
- ret = atmel_hlcdc_plane_update_buffers(plane,
DRM_FORMAT_ARGB8888,
&req->gem,
&req->pitch,
&req->offset,
0, 0,
req->patched_width,
req->height);
- if (ret)
goto err;
- if (!plane->base.crtc)
plane->base.crtc = &crtc->base;
- atmel_hlcdc_layer_update_commit(&plane->layer);
+out:
- if (req->gem)
drm_gem_object_reference(&req->gem->base);
- if (status->gem)
drm_gem_object_unreference_unlocked(&status->gem->base);
- *status = *req;
- return 0;
+err:
- atmel_hlcdc_layer_update_rollback(&plane->layer);
- return ret;
+}
+static int atmel_hlcdc_crtc_cursor_set(struct drm_crtc *c,
struct drm_file *file_priv,
uint32_t handle,
uint32_t width, uint32_t height)
+{
- struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
- struct atmel_hlcdc_plane *plane = crtc->cursor.plane;
- struct drm_gem_cma_object *cma_gem = NULL;
- int ret;
- if (unlikely(!plane))
return -ENOTSUPP;
- mutex_lock(&crtc->cursor.lock);
- if (handle) {
struct drm_gem_object *gem;
gem = drm_gem_object_lookup(c->dev, file_priv, handle);
if (unlikely(!gem)) {
ret = -ENOENT;
goto out;
}
cma_gem = to_drm_gem_cma_obj(gem);
- }
- crtc->cursor.req = crtc->cursor.status;
- crtc->cursor.req.gem = cma_gem;
- crtc->cursor.req.width = width;
- crtc->cursor.req.height = height;
- atmel_hlcdc_crtc_cursor_prepare_req(crtc);
- ret = atmel_hlcdc_crtc_cursor_apply_req(crtc);
- if (cma_gem)
drm_gem_object_unreference_unlocked(&cma_gem->base);
+out:
- mutex_unlock(&crtc->cursor.lock);
- return ret;
+}
+static int atmel_hlcdc_crtc_cursor_move(struct drm_crtc *c, int x, int y) +{
- struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
- struct atmel_hlcdc_plane *plane = crtc->cursor.plane;
- int ret;
- if (unlikely(!plane))
return -ENOTSUPP;
- mutex_lock(&crtc->cursor.lock);
- /*
* If there's no cursor update pending, get the current
* cursor size and buffer.
*/
- crtc->cursor.req = crtc->cursor.status;
- crtc->cursor.req.x = x;
- crtc->cursor.req.y = y;
- atmel_hlcdc_crtc_cursor_prepare_req(crtc);
- ret = atmel_hlcdc_crtc_cursor_apply_req(crtc);
- mutex_unlock(&crtc->cursor.lock);
- return ret;
+}
+static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = {
- .page_flip = atmel_hlcdc_crtc_page_flip,
- .set_config = drm_crtc_helper_set_config,
- .destroy = atmel_hlcdc_crtc_destroy,
+};
+static const struct drm_crtc_funcs atmel_hlcdc_crtc_with_cursor_funcs = {
- .page_flip = atmel_hlcdc_crtc_page_flip,
- .set_config = drm_crtc_helper_set_config,
- .destroy = atmel_hlcdc_crtc_destroy,
- .cursor_set = atmel_hlcdc_crtc_cursor_set,
- .cursor_move = atmel_hlcdc_crtc_cursor_move,
+};
+int atmel_hlcdc_crtc_create(struct drm_device *dev) +{
- struct atmel_hlcdc_dc *dc = dev->dev_private;
- struct atmel_hlcdc_planes *planes = dc->planes;
- const struct drm_crtc_funcs *funcs;
- struct atmel_hlcdc_crtc *crtc;
- int ret;
- int i;
- crtc = kzalloc(sizeof(*crtc), GFP_KERNEL);
- if (!crtc) {
dev_err(dev->dev, "allocation failed\n");
return -ENOMEM;
- }
- mutex_init(&crtc->cursor.lock);
- crtc->hlcdc = dc->hlcdc;
- crtc->cursor.plane = planes->cursor;
- if (planes->cursor)
funcs = &atmel_hlcdc_crtc_with_cursor_funcs;
- else
funcs = &atmel_hlcdc_crtc_funcs;
- ret = drm_crtc_init_with_planes(dev, &crtc->base,
&planes->primary->base,
planes->cursor ? &planes->cursor->base : NULL,
funcs);
- if (ret < 0)
goto fail;
- atmel_hlcdc_layer_set_finished(&planes->primary->layer,
atmel_hlcdc_crtc_finish_page_flip,
crtc);
- crtc->id = drm_crtc_index(&crtc->base);
- if (planes->cursor)
planes->cursor->base.possible_crtcs = 1 << crtc->id;
- for (i = 0; i < planes->noverlays; i++)
planes->overlays[i]->base.possible_crtcs = 1 << crtc->id;
- drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs);
- return 0;
+fail:
- atmel_hlcdc_crtc_destroy(&crtc->base);
- return ret;
+}
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c new file mode 100644 index 0000000..e4ce24e --- /dev/null +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -0,0 +1,477 @@ +/*
- Copyright (C) 2014 Traphandler
- Copyright (C) 2014 Free Electrons
- Copyright (C) 2014 Atmel
- Author: Jean-Jacques Hiblot jjhiblot@traphandler.com
- Author: Boris BREZILLON boris.brezillon@free-electrons.com
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License version 2 as published by
- the Free Software Foundation.
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
- You should have received a copy of the GNU General Public License along with
- this program. If not, see http://www.gnu.org/licenses/.
- */
+#include <linux/clk.h> +#include <linux/irq.h> +#include <linux/irqchip.h> +#include <linux/module.h> +#include <linux/pm_runtime.h>
+#include "atmel_hlcdc_dc.h"
+#define ATMEL_HLCDC_LAYER_IRQS_OFFSET 8
+static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data) +{
- struct drm_device *dev = data;
- struct atmel_hlcdc_dc *dc = dev->dev_private;
- unsigned long status;
- unsigned int imr, isr;
- int bit;
- regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_IMR, &imr);
- regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr);
- status = imr & isr;
- if (!status)
return IRQ_NONE;
- bit = ATMEL_HLCDC_LAYER_IRQS_OFFSET;
- for_each_set_bit_from(bit, &status, ATMEL_HLCDC_LAYER_IRQS_OFFSET +
ATMEL_HLCDC_MAX_LAYERS) {
int layerid = bit - ATMEL_HLCDC_LAYER_IRQS_OFFSET;
struct atmel_hlcdc_layer *layer = dc->layers[layerid];
if (layer)
atmel_hlcdc_layer_irq(layer);
- }
- return IRQ_HANDLED;
+}
+static struct drm_framebuffer *atmel_hlcdc_fb_create(struct drm_device *dev,
struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd)
+{
- return drm_fb_cma_create(dev, file_priv, mode_cmd);
+}
+static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev) +{
- struct atmel_hlcdc_dc *dc = dev->dev_private;
- if (dc->fbdev)
drm_fbdev_cma_hotplug_event(dc->fbdev);
+}
+static const struct drm_mode_config_funcs mode_config_funcs = {
- .fb_create = atmel_hlcdc_fb_create,
- .output_poll_changed = atmel_hlcdc_fb_output_poll_changed,
+};
+static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev) +{
- struct atmel_hlcdc_dc *dc = dev->dev_private;
- struct atmel_hlcdc_planes *planes;
- int ret;
- int i;
- drm_mode_config_init(dev);
- ret = atmel_hlcdc_panel_create(dev);
- if (ret) {
dev_err(dev->dev, "failed to create panel: %d\n", ret);
return ret;
- }
- planes = atmel_hlcdc_create_planes(dev);
- if (IS_ERR(planes)) {
dev_err(dev->dev, "failed to create planes\n");
return PTR_ERR(planes);
- }
- dc->planes = planes;
- dc->layers[planes->primary->layer.desc->id] =
&planes->primary->layer;
- if (planes->cursor)
dc->layers[planes->cursor->layer.desc->id] =
&planes->cursor->layer;
- for (i = 0; i < planes->noverlays; i++)
dc->layers[planes->overlays[i]->layer.desc->id] =
&planes->overlays[i]->layer;
- ret = atmel_hlcdc_crtc_create(dev);
- if (ret) {
dev_err(dev->dev, "failed to create crtc\n");
return ret;
- }
- dev->mode_config.min_width = dc->desc->min_width;
- dev->mode_config.min_height = dc->desc->min_height;
- dev->mode_config.max_width = dc->desc->max_width;
- dev->mode_config.max_height = dc->desc->max_height;
- dev->mode_config.funcs = &mode_config_funcs;
- return 0;
+}
+static int atmel_hlcdc_dc_load(struct drm_device *dev, unsigned long flags) +{
- struct platform_device *pdev = dev->platformdev;
- const struct atmel_hlcdc_dc_desc *desc;
- struct atmel_hlcdc_dc *dc;
- int ret;
- desc = platform_get_drvdata(pdev);
- dc = devm_kzalloc(dev->dev, sizeof(*dc), GFP_KERNEL);
- if (!dc) {
dev_err(dev->dev, "failed to allocate private data\n");
return -ENOMEM;
- }
- dc->desc = platform_get_drvdata(pdev);
- dc->hlcdc = dev_get_drvdata(dev->dev->parent);
- dev->dev_private = dc;
- ret = clk_prepare_enable(dc->hlcdc->periph_clk);
- if (ret) {
dev_err(dev->dev, "failed to enable periph_clk\n");
return ret;
- }
- pm_runtime_enable(dev->dev);
- pm_runtime_put_sync(dev->dev);
- ret = atmel_hlcdc_dc_modeset_init(dev);
- if (ret < 0) {
dev_err(dev->dev, "failed to initialize mode setting\n");
goto err_periph_clk_disable;
- }
- ret = drm_vblank_init(dev, 1);
- if (ret < 0) {
dev_err(dev->dev, "failed to initialize vblank\n");
goto err_periph_clk_disable;
- }
- pm_runtime_get_sync(dev->dev);
- ret = drm_irq_install(dev);
- pm_runtime_put_sync(dev->dev);
- if (ret < 0) {
dev_err(dev->dev, "failed to install IRQ handler\n");
goto err_periph_clk_disable;
- }
- platform_set_drvdata(pdev, dev);
- drm_kms_helper_poll_init(dev);
- dc->fbdev = drm_fbdev_cma_init(dev, 24,
dev->mode_config.num_crtc,
dev->mode_config.num_connector);
- if (IS_ERR(dc->fbdev)) {
ret = PTR_ERR(dc->fbdev);
goto err_periph_clk_disable;
- }
- return 0;
+err_periph_clk_disable:
- clk_disable_unprepare(dc->hlcdc->periph_clk);
- return ret;
+}
+static int atmel_hlcdc_dc_unload(struct drm_device *dev) +{
- struct atmel_hlcdc_dc *dc = dev->dev_private;
- drm_kms_helper_poll_fini(dev);
- drm_mode_config_cleanup(dev);
- drm_vblank_cleanup(dev);
- pm_runtime_get_sync(dev->dev);
- drm_irq_uninstall(dev);
- pm_runtime_put_sync(dev->dev);
- dev->dev_private = NULL;
- pm_runtime_disable(dev->dev);
- clk_disable_unprepare(dc->hlcdc->periph_clk);
- return 0;
+}
+static void atmel_hlcdc_dc_preclose(struct drm_device *dev,
struct drm_file *file)
+{
- struct drm_crtc *crtc;
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
atmel_hlcdc_crtc_cancel_page_flip(crtc, file);
+}
+static void atmel_hlcdc_dc_lastclose(struct drm_device *dev) +{
- struct atmel_hlcdc_dc *dc = dev->dev_private;
- drm_fbdev_cma_restore_mode(dc->fbdev);
+}
+static void atmel_hlcdc_dc_irq_preinstall(struct drm_device *dev) +{
- struct atmel_hlcdc_dc *dc = dev->dev_private;
- unsigned int isr;
- regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, 0xffffffff);
- regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr);
+}
+static int atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev) +{
- struct atmel_hlcdc_dc *dc = dev->dev_private;
- int i;
- /* Enable interrupts on activated layers */
- for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
if (dc->layers[i])
regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER,
BIT(i + 8));
- }
- return 0;
+}
+static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev) +{
+}
+static int atmel_hlcdc_dc_enable_vblank(struct drm_device *dev, int crtc) +{
- return 0;
+}
+static void atmel_hlcdc_dc_disable_vblank(struct drm_device *dev, int crtc) +{ +}
+static const struct file_operations fops = {
- .owner = THIS_MODULE,
- .open = drm_open,
- .release = drm_release,
- .unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
- .compat_ioctl = drm_compat_ioctl,
+#endif
- .poll = drm_poll,
- .read = drm_read,
- .llseek = no_llseek,
- .mmap = drm_gem_cma_mmap,
+};
+static struct drm_driver atmel_hlcdc_dc_driver = {
- .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET,
- .load = atmel_hlcdc_dc_load,
- .unload = atmel_hlcdc_dc_unload,
- .preclose = atmel_hlcdc_dc_preclose,
- .lastclose = atmel_hlcdc_dc_lastclose,
- .irq_handler = atmel_hlcdc_dc_irq_handler,
- .irq_preinstall = atmel_hlcdc_dc_irq_preinstall,
- .irq_postinstall = atmel_hlcdc_dc_irq_postinstall,
- .irq_uninstall = atmel_hlcdc_dc_irq_uninstall,
- .get_vblank_counter = drm_vblank_count,
- .enable_vblank = atmel_hlcdc_dc_enable_vblank,
- .disable_vblank = atmel_hlcdc_dc_disable_vblank,
- .gem_free_object = drm_gem_cma_free_object,
- .gem_vm_ops = &drm_gem_cma_vm_ops,
- .dumb_create = drm_gem_cma_dumb_create,
- .dumb_map_offset = drm_gem_cma_dumb_map_offset,
- .dumb_destroy = drm_gem_dumb_destroy,
- .fops = &fops,
- .name = "atmel-hlcdc",
- .desc = "Atmel HLCD Controller DRM",
- .date = "20141504",
- .major = 1,
- .minor = 0,
+};
+static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
- {
.name = "base",
.formats = &atmel_hlcdc_plane_rgb_formats,
.regs_offset = 0x40,
.id = 0,
.type = ATMEL_HLCDC_BASE_LAYER,
.nconfigs = 7,
.layout = {
.xstride = { 2 },
.default_color = 3,
.general_config = 4,
.disc_pos = 5,
.disc_size = 6,
},
- },
- {
.name = "overlay1",
.formats = &atmel_hlcdc_plane_rgb_formats,
.regs_offset = 0x140,
.id = 1,
.type = ATMEL_HLCDC_OVERLAY_LAYER,
.nconfigs = 10,
.layout = {
.pos = 2,
.size = 3,
.xstride = { 4 },
.pstride = { 5 },
.default_color = 6,
.chroma_key = 7,
.chroma_key_mask = 8,
.general_config = 9,
},
- },
- {
.name = "overlay2",
.formats = &atmel_hlcdc_plane_rgb_formats,
.regs_offset = 0x240,
.id = 2,
.type = ATMEL_HLCDC_OVERLAY_LAYER,
.nconfigs = 10,
.layout = {
.pos = 2,
.size = 3,
.xstride = { 4 },
.pstride = { 5 },
.default_color = 6,
.chroma_key = 7,
.chroma_key_mask = 8,
.general_config = 9,
},
- },
- {
.name = "high-end-overlay",
.formats = &atmel_hlcdc_plane_rgb_and_yuv_formats,
.regs_offset = 0x340,
.id = 3,
.type = ATMEL_HLCDC_OVERLAY_LAYER,
.nconfigs = 42,
.layout = {
.pos = 2,
.size = 3,
.memsize = 4,
.xstride = { 5, 7 },
.pstride = { 6, 8 },
.default_color = 9,
.chroma_key = 10,
.chroma_key_mask = 11,
.general_config = 12,
},
- },
- {
.name = "cursor",
.formats = &atmel_hlcdc_plane_rgb_formats,
.regs_offset = 0x440,
.id = 4,
.type = ATMEL_HLCDC_CURSOR_LAYER,
.nconfigs = 10,
.max_width = 128,
.max_height = 128,
.layout = {
.pos = 2,
.size = 3,
.xstride = { 4 },
.pstride = { 5 },
.default_color = 6,
.chroma_key = 7,
.chroma_key_mask = 8,
.general_config = 9,
},
- },
+};
+static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d3 = {
- .min_width = 0,
- .min_height = 0,
- .max_width = 2048,
- .max_height = 2048,
- .nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d3_layers),
- .layers = atmel_hlcdc_sama5d3_layers,
+};
+static const struct of_device_id atmel_hlcdc_of_match[] = {
- {
.compatible = "atmel,sama5d3-hlcdc",
.data = &atmel_hlcdc_dc_sama5d3
- },
- { /* sentinel */ },
+};
+static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) +{
- const struct of_device_id *match;
- int ret;
- match = of_match_node(atmel_hlcdc_of_match, pdev->dev.parent->of_node);
- if (!match) {
dev_err(&pdev->dev, "invalid compatible string\n");
return -ENODEV;
- }
- if (!match->data) {
dev_err(&pdev->dev, "invalid hlcdc description\n");
return -EINVAL;
- }
- ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
- if (ret)
return ret;
- platform_set_drvdata(pdev, (void *)match->data);
- ret = drm_platform_init(&atmel_hlcdc_dc_driver, pdev);
- if (ret)
return ret;
- return 0;
+}
+static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) +{
- drm_put_dev(platform_get_drvdata(pdev));
- return 0;
+}
+static const struct of_device_id atmel_hlcdc_dc_of_match[] = {
- { .compatible = "atmel,hlcdc-dc" },
- { },
+};
+static struct platform_driver atmel_hlcdc_dc_platform_driver = {
- .probe = atmel_hlcdc_dc_drm_probe,
- .remove = atmel_hlcdc_dc_drm_remove,
- .driver = {
.name = "atmel-hlcdc-dc",
.owner = THIS_MODULE,
.of_match_table = atmel_hlcdc_dc_of_match,
- },
+}; +module_platform_driver(atmel_hlcdc_dc_platform_driver);
+MODULE_AUTHOR("Jean-Jacques Hiblot jjhiblot@traphandler.com"); +MODULE_AUTHOR("Boris BREZILLON boris.brezillon@free-electrons.com"); +MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:atmel-hlcdc-dc"); diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h new file mode 100644 index 0000000..5d2919e --- /dev/null +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h @@ -0,0 +1,178 @@ +/*
- Copyright (C) 2014 Traphandler
- Copyright (C) 2014 Free Electrons
- Copyright (C) 2014 Atmel
- Author: Jean-Jacques Hiblot jjhiblot@traphandler.com
- Author: Boris BREZILLON boris.brezillon@free-electrons.com
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License version 2 as published by
- the Free Software Foundation.
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
- You should have received a copy of the GNU General Public License along with
- this program. If not, see http://www.gnu.org/licenses/.
- */
+#ifndef DRM_ATMEL_HLCDC_H +#define DRM_ATMEL_HLCDC_H
+#include <linux/clk.h> +#include <linux/irqdomain.h> +#include <linux/pwm.h>
+#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_panel.h> +#include <drm/drmP.h>
+#include "atmel_hlcdc_layer.h"
+#define ATMEL_HLCDC_MAX_LAYERS 5
+/**
- Atmel HLCDC Display Controller description structure.
- This structure describe the HLCDC IP capabilities and depends on the
- HLCDC IP version (or Atmel SoC family).
- @min_width: minimum width supported by the Display Controller
- @min_height: minimum height supported by the Display Controller
- @max_width: maximum width supported by the Display Controller
- @max_height: maximum height supported by the Display Controller
- @layer: a layer description table describing available layers
- @nlayers: layer description table size
- */
+struct atmel_hlcdc_dc_desc {
- int min_width;
- int min_height;
- int max_width;
- int max_height;
- const struct atmel_hlcdc_layer_desc *layers;
- int nlayers;
+};
+/**
- Atmel HLCDC Plane properties.
- This structure stores plane property definitions.
- @alpha: alpha blending (or transparency) property
- */
+struct atmel_hlcdc_plane_properties {
- struct drm_property *alpha;
+};
+/**
- Atmel HLCDC Plane.
- @base: base DRM plane structure
- @layer: HLCDC layer structure
- @properties: pointer to the property definitions structure
- @alpha: current alpha blending (or transparency) status
- */
+struct atmel_hlcdc_plane {
- struct drm_plane base;
- struct atmel_hlcdc_layer layer;
- struct atmel_hlcdc_plane_properties *properties;
- u8 alpha;
+};
+static inline struct atmel_hlcdc_plane * +drm_plane_to_atmel_hlcdc_plane(struct drm_plane *p) +{
- return container_of(p, struct atmel_hlcdc_plane, base);
+}
+static inline struct atmel_hlcdc_plane * +atmel_hlcdc_layer_to_plane(struct atmel_hlcdc_layer *l) +{
- return container_of(l, struct atmel_hlcdc_plane, layer);
+}
+/**
- Atmel HLCDC Planes.
- This structure stores the instantiated HLCDC Planes and can be accessed by
- the HLCDC Display Controller or the HLCDC CRTC.
- @primary: primary plane
- @cursor: hardware cursor plane
- @overlays: overlay plane table
- @noverlays: number of overlay planes
- */
+struct atmel_hlcdc_planes {
- struct atmel_hlcdc_plane *primary;
- struct atmel_hlcdc_plane *cursor;
- struct atmel_hlcdc_plane **overlays;
- int noverlays;
+};
+/**
- Atmel HLCDC Display Controller.
- @desc: HLCDC Display Controller description
- @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
- @fbdev: framebuffer device attached to the Display Controller
- @planes: instantiated planes
- @layers: active HLCDC layer
- */
+struct atmel_hlcdc_dc {
- struct atmel_hlcdc_dc_desc *desc;
- struct atmel_hlcdc *hlcdc;
- struct drm_fbdev_cma *fbdev;
- struct atmel_hlcdc_planes *planes;
- struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS];
+};
+extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats; +extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats;
+struct atmel_hlcdc_planes * +atmel_hlcdc_create_planes(struct drm_device *dev);
+int atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,
u32 pixel_format,
struct drm_gem_cma_object **gems,
unsigned int *pitches,
unsigned int *offsets,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h);
+void atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
u32 format);
+int atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
u32 format);
+int atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
struct drm_crtc *crtc,
int crtc_x, int crtc_y,
unsigned int crtc_w,
unsigned int crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h);
+void atmel_hlcdc_crtc_cancel_page_flip(struct drm_crtc *crtc,
struct drm_file *file);
+int atmel_hlcdc_crtc_create(struct drm_device *dev);
+int atmel_hlcdc_panel_create(struct drm_device *dev);
+struct atmel_hlcdc_pwm_chip *atmel_hlcdc_pwm_create(struct drm_device *dev,
struct clk *slow_clk,
struct clk *sys_clk,
void __iomem *regs);
+int atmel_hlcdc_pwm_destroy(struct drm_device *dev,
struct atmel_hlcdc_pwm_chip *chip);
+#endif /* DRM_ATMEL_HLCDC_H */ diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c new file mode 100644 index 0000000..b449fe1 --- /dev/null +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c @@ -0,0 +1,701 @@ +/*
- Copyright (C) 2014 Free Electrons
- Copyright (C) 2014 Atmel
- Author: Boris BREZILLON boris.brezillon@free-electrons.com
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License version 2 as published by
- the Free Software Foundation.
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
- You should have received a copy of the GNU General Public License along with
- this program. If not, see http://www.gnu.org/licenses/.
- */
+#include <linux/dma-mapping.h> +#include <linux/interrupt.h>
+#include "atmel_hlcdc_dc.h"
+static void +atmel_hlcdc_layer_gem_flip_release(struct atmel_hlcdc_layer *layer,
struct atmel_hlcdc_layer_gem_flip *flip)
+{
- int i;
- for (i = 0; i < layer->max_planes; i++) {
if (!flip->gems[i])
break;
drm_gem_object_unreference_unlocked(flip->gems[i]);
- }
- kfree(flip);
+}
+static void atmel_hlcdc_layer_gc_work(struct work_struct *work) +{
- struct atmel_hlcdc_layer_gem_flip_gc *gc =
container_of(work,
struct atmel_hlcdc_layer_gem_flip_gc,
work);
- struct atmel_hlcdc_layer *layer =
container_of(gc , struct atmel_hlcdc_layer, gc);
- while (true) {
struct atmel_hlcdc_layer_gem_flip *flip;
unsigned long flags;
spin_lock_irqsave(&gc->list_lock, flags);
flip = list_first_entry_or_null(&gc->list,
struct atmel_hlcdc_layer_gem_flip,
node);
if (flip)
list_del(&flip->node);
spin_unlock_irqrestore(&gc->list_lock, flags);
if (!flip)
break;
atmel_hlcdc_layer_gem_flip_release(layer, flip);
mutex_lock(&gc->finished_lock);
if (gc->finished)
gc->finished(gc->finished_data);
mutex_unlock(&gc->finished_lock);
- }
+}
+static void +atmel_hlcdc_layer_gem_flip_put(struct atmel_hlcdc_layer *layer,
struct atmel_hlcdc_layer_gem_flip *flip)
+{
- struct atmel_hlcdc_layer_gem_flip_gc *gc = &layer->gc;
- unsigned long flags;
- if (--flip->remaining_gems <= 0) {
spin_lock_irqsave(&gc->list_lock, flags);
list_add_tail(&flip->node,
&gc->list);
spin_unlock_irqrestore(&gc->list_lock, flags);
schedule_work(&gc->work);
- }
+}
+static void atmel_hlcdc_layer_start_queue(struct atmel_hlcdc_layer *layer) +{
- struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
- const struct atmel_hlcdc_layer_desc *desc = layer->desc;
- struct regmap *regmap = layer->hlcdc->regmap;
- int i;
- for (i = 0; i < layer->max_planes; i++) {
dma->cur[i] = dma->queue[i];
if (!dma->queue[i])
continue;
dma->queue[i] = NULL;
regmap_write(regmap,
desc->regs_offset +
ATMEL_HLCDC_LAYER_PLANE_ADDR(i),
dma->cur[i]->addr);
regmap_write(regmap,
desc->regs_offset +
ATMEL_HLCDC_LAYER_PLANE_CTRL(i),
dma->cur[i]->ctrl);
regmap_write(regmap,
desc->regs_offset +
ATMEL_HLCDC_LAYER_PLANE_NEXT(i),
dma->cur[i]->next);
- }
+}
+static void atmel_hlcdc_layer_update_apply(struct atmel_hlcdc_layer *layer) +{
- const struct atmel_hlcdc_layer_desc *desc = layer->desc;
- struct atmel_hlcdc_layer_update *upd = &layer->update;
- struct regmap *regmap = layer->hlcdc->regmap;
- struct atmel_hlcdc_layer_update_slot *slot;
- unsigned int cfg;
- u32 action = 0;
- int i;
- if (upd->pending < 0 || upd->pending > 1)
return;
- slot = &upd->slots[upd->pending];
- for_each_set_bit(cfg, slot->updated_configs, layer->desc->nconfigs) {
regmap_write(regmap,
desc->regs_offset +
ATMEL_HLCDC_LAYER_CFG(layer, cfg),
slot->configs[cfg]);
action |= ATMEL_HLCDC_LAYER_UPDATE;
- }
- if (slot->gem_flip && slot->gem_flip->remaining_gems) {
struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
for (i = 0; i < layer->max_planes; i++) {
struct atmel_hlcdc_dma_channel_dscr *dscr;
if (!slot->gem_flip->gems[i])
break;
dscr = slot->dscrs[i];
slot->dscrs[i] = NULL;
if (!dma->enabled) {
action |= ATMEL_HLCDC_LAYER_DMA_CHAN;
regmap_write(regmap,
desc->regs_offset +
ATMEL_HLCDC_LAYER_PLANE_ADDR(i),
dscr->addr);
regmap_write(regmap,
desc->regs_offset +
ATMEL_HLCDC_LAYER_PLANE_CTRL(i),
dscr->ctrl);
regmap_write(regmap,
desc->regs_offset +
ATMEL_HLCDC_LAYER_PLANE_NEXT(i),
dscr->next);
dma->cur[i] = dscr;
} else {
action |= ATMEL_HLCDC_LAYER_A2Q;
regmap_write(regmap,
desc->regs_offset +
ATMEL_HLCDC_LAYER_PLANE_HEAD(i),
dscr->next);
dma->queue[i] = dscr;
}
}
dma->enabled = true;
slot->gem_flip = NULL;
- }
- if (action)
regmap_write(regmap,
desc->regs_offset + ATMEL_HLCDC_LAYER_CHER,
action);
- for (i = 0; i < layer->max_planes; i++) {
if (!slot->dscrs[i])
continue;
slot->dscrs[i]->gem_flip = NULL;
slot->dscrs[i] = NULL;
- }
- if (slot->gem_flip) {
atmel_hlcdc_layer_gem_flip_put(layer, slot->gem_flip);
slot->gem_flip = NULL;
- }
- bitmap_clear(slot->updated_configs, 0, layer->desc->nconfigs);
- memset(slot->configs, 0,
sizeof(*slot->configs) * layer->desc->nconfigs);
- upd->pending = -1;
+}
+static bool +atmel_hlcdc_layer_dma_channel_active(struct atmel_hlcdc_layer *layer) +{
- struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
- int i;
- for (i = 0; i < layer->max_planes; i++) {
if (dma->cur[i])
return true;
- }
- return false;
+}
+void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer) +{
- struct atmel_hlcdc_layer_gem_flip *flip_gc[ATMEL_HLCDC_MAX_PLANES];
- struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
- const struct atmel_hlcdc_layer_desc *desc = layer->desc;
- struct regmap *regmap = layer->hlcdc->regmap;
- struct atmel_hlcdc_dma_channel_dscr *dscr;
- unsigned long flags;
- unsigned int isr, imr;
- unsigned int status;
- int i;
- regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IMR, &imr);
- regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR, &isr);
- status = imr & isr;
- if (!status)
return;
- memset(flip_gc, 0, sizeof(flip_gc));
- spin_lock_irqsave(&dma->lock, flags);
- for (i = 0; i < layer->max_planes; i++) {
if ((status & ATMEL_HLCDC_LAYER_DONE_IRQ(i)) ||
(status & ATMEL_HLCDC_LAYER_ADD_IRQ(i))) {
dscr = dma->cur[i];
dma->cur[i] = NULL;
flip_gc[i] = dscr->gem_flip;
dscr->gem_flip = NULL;
}
if (status & ATMEL_HLCDC_LAYER_ADD_IRQ(i)) {
dma->cur[i] = dma->queue[i];
dma->queue[i] = NULL;
}
- }
- /*
* The DMA channel might have been disabled before we were able to
* add the new frame to the DMA transfer queue.
* Try to re-enable the channel in this case.
*/
- if (!atmel_hlcdc_layer_dma_channel_active(layer)) {
if (atmel_hlcdc_layer_dma_channel_busy(layer)) {
atmel_hlcdc_layer_start_queue(layer);
regmap_write(regmap,
desc->regs_offset +
ATMEL_HLCDC_LAYER_CHDR,
ATMEL_HLCDC_LAYER_A2Q);
regmap_write(regmap,
desc->regs_offset +
ATMEL_HLCDC_LAYER_CHER,
ATMEL_HLCDC_LAYER_DMA_CHAN);
} else {
dma->enabled = false;
}
- }
- if (!atmel_hlcdc_layer_dma_channel_busy(layer)) {
struct atmel_hlcdc_layer_update *upd = &layer->update;
spin_lock(&upd->pending_lock);
atmel_hlcdc_layer_update_apply(layer);
spin_unlock(&upd->pending_lock);
- }
- spin_unlock_irqrestore(&dma->lock, flags);
- for (i = 0; i < layer->max_planes; i++) {
if (!flip_gc[i])
break;
atmel_hlcdc_layer_gem_flip_put(layer, flip_gc[i]);
- }
+}
+int atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer) +{
- struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
- unsigned long flags;
- int i;
- spin_lock_irqsave(&dma->lock, flags);
- for (i = 0; i < layer->max_planes; i++) {
if (!dma->cur[i])
break;
dma->cur[i]->ctrl = 0;
- }
- spin_unlock_irqrestore(&dma->lock, flags);
- return 0;
+}
+void atmel_hlcdc_layer_set_finished(struct atmel_hlcdc_layer *layer,
void (*finished)(void *data),
void *data)
+{
- struct atmel_hlcdc_layer_gem_flip_gc *gc = &layer->gc;
- mutex_lock(&gc->finished_lock);
- gc->finished = finished;
- gc->finished_data = data;
- mutex_unlock(&gc->finished_lock);
+}
+static void atmel_hlcdc_layer_update_reset(struct atmel_hlcdc_layer *layer) +{
- struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
- struct atmel_hlcdc_layer_update *upd = &layer->update;
- struct atmel_hlcdc_layer_update_slot *slot;
- unsigned long flags;
- int i;
- if (upd->next < 0 || upd->next > 1)
return;
- slot = &upd->slots[upd->next];
- bitmap_clear(slot->updated_configs, 0, layer->desc->nconfigs);
- memset(slot->configs, 0,
sizeof(*slot->configs) * layer->desc->nconfigs);
- spin_lock_irqsave(&dma->lock, flags);
- for (i = 0; i < layer->max_planes; i++) {
if (!slot->dscrs[i])
break;
slot->dscrs[i]->gem_flip = NULL;
slot->dscrs[i] = NULL;
- }
- spin_unlock_irqrestore(&layer->dma.lock, flags);
- if (slot->gem_flip) {
atmel_hlcdc_layer_gem_flip_release(layer, slot->gem_flip);
slot->gem_flip = NULL;
- }
- upd->next = -1;
+}
+int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer) +{
- struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
- struct atmel_hlcdc_layer_update *upd = &layer->update;
- struct regmap *regmap = layer->hlcdc->regmap;
- struct atmel_hlcdc_layer_gem_flip *gem_flip;
- struct atmel_hlcdc_layer_update_slot *slot;
- unsigned long flags;
- int i, j = 0;
- int pending;
- gem_flip = kzalloc(sizeof(*gem_flip), GFP_KERNEL);
- if (!gem_flip)
return -ENOMEM;
- mutex_lock(&upd->lock);
- spin_lock_irqsave(&upd->pending_lock, flags);
- pending = upd->pending;
- spin_unlock_irqrestore(&upd->pending_lock, flags);
- upd->next = pending ? 0 : 1;
- slot = &upd->slots[upd->next];
- spin_lock_irqsave(&dma->lock, flags);
- for (i = 0; i < layer->max_planes * 4; i++) {
if (!dma->dscrs[i].gem_flip) {
slot->dscrs[j++] = &dma->dscrs[i];
dma->dscrs[i].gem_flip = gem_flip;
if (j == layer->max_planes)
break;
}
- }
- if (j < layer->max_planes) {
for (i = 0; i < j; i++)
slot->dscrs[i]->gem_flip = NULL;
- }
- spin_unlock_irqrestore(&layer->dma.lock, flags);
- if (j < layer->max_planes) {
mutex_unlock(&upd->lock);
kfree(gem_flip);
return -EBUSY;
- }
- slot->gem_flip = gem_flip;
- spin_lock_irqsave(&upd->pending_lock, flags);
- pending = upd->pending;
- if (pending >= 0) {
memcpy(upd->slots[upd->next].configs,
upd->slots[upd->pending].configs,
layer->desc->nconfigs * sizeof(u32));
memcpy(upd->slots[upd->next].updated_configs,
upd->slots[upd->pending].updated_configs,
DIV_ROUND_UP(layer->desc->nconfigs,
BITS_PER_BYTE * sizeof(unsigned long)) *
sizeof(unsigned long));
- }
- spin_unlock_irqrestore(&upd->pending_lock, flags);
- if (pending < 0)
regmap_bulk_read(regmap, ATMEL_HLCDC_LAYER_CFG(layer, 0),
upd->slots[upd->next].configs,
layer->desc->nconfigs);
- return 0;
+}
+void atmel_hlcdc_layer_update_rollback(struct atmel_hlcdc_layer *layer) +{
- struct atmel_hlcdc_layer_update *upd = &layer->update;
- atmel_hlcdc_layer_update_reset(layer);
- mutex_unlock(&upd->lock);
+}
+int atmel_hlcdc_layer_update_set_gem(struct atmel_hlcdc_layer *layer,
int plane_id,
struct drm_gem_cma_object *gem,
unsigned int offset)
+{
- struct atmel_hlcdc_layer_update *upd = &layer->update;
- struct atmel_hlcdc_layer_gem_flip *gem_flip;
- struct atmel_hlcdc_layer_update_slot *slot;
- struct atmel_hlcdc_dma_channel_dscr *dscr;
- struct drm_gem_object *old_gem;
- if (upd->next < 0 || upd->next > 1)
return -EINVAL;
- if (plane_id >= layer->max_planes || plane_id < 0)
return -EINVAL;
- slot = &upd->slots[upd->next];
- dscr = slot->dscrs[plane_id];
- dscr->addr = gem->paddr + offset;
- dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH;
- gem_flip = dscr->gem_flip;
- old_gem = gem_flip->gems[plane_id];
- if (gem) {
drm_gem_object_reference(&gem->base);
gem_flip->gems[plane_id] = &gem->base;
gem_flip->remaining_gems++;
- } else {
gem_flip->gems[plane_id] = NULL;
- }
- if (old_gem) {
drm_gem_object_unreference_unlocked(old_gem);
gem_flip->remaining_gems--;
- }
- return 0;
+}
+void atmel_hlcdc_layer_update_cfg(struct atmel_hlcdc_layer *layer, int cfg,
u32 mask, u32 val)
+{
- struct atmel_hlcdc_layer_update *upd = &layer->update;
- struct atmel_hlcdc_layer_update_slot *slot;
- if (upd->next < 0 || upd->next > 1)
return;
- if (cfg >= layer->desc->nconfigs)
return;
- slot = &upd->slots[upd->next];
- slot->configs[cfg] &= ~mask;
- slot->configs[cfg] |= (val & mask);
- set_bit(cfg, slot->updated_configs);
+}
+void atmel_hlcdc_layer_update_commit(struct atmel_hlcdc_layer *layer) +{
- struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
- struct atmel_hlcdc_layer_update *upd = &layer->update;
- struct atmel_hlcdc_layer_update_slot *slot;
- unsigned long flags;
- int pending;
- if (upd->next < 0 || upd->next > 1)
return;
- slot = &upd->slots[upd->next];
- spin_lock_irqsave(&upd->pending_lock, flags);
- pending = upd->pending;
- upd->pending = upd->next;
- upd->next = pending;
- if (pending >= 0 && !slot->gem_flip->remaining_gems) {
struct atmel_hlcdc_layer_gem_flip *gem_flip = slot->gem_flip;
struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_MAX_PLANES];
memcpy(dscrs, slot->dscrs, sizeof(dscrs));
slot->gem_flip = upd->slots[pending].gem_flip;
memcpy(slot->dscrs, upd->slots[pending].dscrs,
sizeof(slot->dscrs));
upd->slots[pending].gem_flip = gem_flip;
memcpy(upd->slots[pending].dscrs, dscrs, sizeof(dscrs));
- }
- spin_unlock_irqrestore(&upd->pending_lock, flags);
- if (pending >= 0) {
atmel_hlcdc_layer_update_reset(layer);
mutex_unlock(&upd->lock);
return;
- }
- spin_lock_irqsave(&dma->lock, flags);
- if (!atmel_hlcdc_layer_dma_channel_busy(layer)) {
spin_lock(&upd->pending_lock);
atmel_hlcdc_layer_update_apply(layer);
spin_unlock(&upd->pending_lock);
- }
- spin_unlock_irqrestore(&dma->lock, flags);
- atmel_hlcdc_layer_update_reset(layer);
- mutex_unlock(&upd->lock);
+}
+static int atmel_hlcdc_layer_dma_init(struct drm_device *dev,
struct atmel_hlcdc_layer *layer)
+{
- struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
- dma_addr_t dma_addr;
- int i;
- dma->dscrs = dma_alloc_coherent(dev->dev,
layer->max_planes * 4 *
sizeof(*dma->dscrs),
&dma_addr, GFP_KERNEL);
- if (!dma->dscrs)
return -ENOMEM;
- for (i = 0; i < layer->max_planes * 4; i++) {
struct atmel_hlcdc_dma_channel_dscr *dscr = &dma->dscrs[i];
dscr->next = dma_addr + (i * sizeof(*dscr));
- }
- spin_lock_init(&dma->lock);
- return 0;
+}
+static void atmel_hlcdc_layer_dma_cleanup(struct drm_device *dev,
struct atmel_hlcdc_layer *layer)
+{
- struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
- int i;
- for (i = 0; i < layer->max_planes * 4; i++) {
struct atmel_hlcdc_dma_channel_dscr *dscr = &dma->dscrs[i];
if (!dscr->gem_flip)
continue;
atmel_hlcdc_layer_gem_flip_put(layer, dscr->gem_flip);
- }
- dma_free_coherent(dev->dev, layer->max_planes * 4 *
sizeof(*dma->dscrs), dma->dscrs,
dma->dscrs[0].next);
+}
+static void atmel_hlcdc_layer_gc_init(struct atmel_hlcdc_layer *layer) +{
- struct atmel_hlcdc_layer_gem_flip_gc *gc = &layer->gc;
- INIT_LIST_HEAD(&gc->list);
- spin_lock_init(&gc->list_lock);
- INIT_WORK(&gc->work, atmel_hlcdc_layer_gc_work);
- mutex_init(&gc->finished_lock);
+}
+static void atmel_hlcdc_layer_gc_cleanup(struct atmel_hlcdc_layer *layer) +{
- struct atmel_hlcdc_layer_gem_flip_gc *gc = &layer->gc;
- flush_work(&gc->work);
+}
+static int atmel_hlcdc_layer_update_init(struct drm_device *dev,
struct atmel_hlcdc_layer *layer,
const struct atmel_hlcdc_layer_desc *desc)
+{
- struct atmel_hlcdc_layer_update *upd = &layer->update;
- int updated_size;
- void *buffer;
- int i;
- updated_size = DIV_ROUND_UP(desc->nconfigs,
BITS_PER_BYTE *
sizeof(unsigned long));
- buffer = devm_kzalloc(dev->dev,
((desc->nconfigs * sizeof(u32)) +
(updated_size * sizeof(unsigned long))) * 2,
GFP_KERNEL);
- if (!buffer)
return -ENOMEM;
- for (i = 0; i < 2; i++) {
upd->slots[i].updated_configs = buffer;
buffer += updated_size * sizeof(unsigned long);
upd->slots[i].configs = buffer;
buffer += desc->nconfigs * sizeof(u32);
- }
- upd->pending = -1;
- upd->next = -1;
- spin_lock_init(&upd->pending_lock);
- mutex_init(&upd->lock);
- return 0;
+}
+int atmel_hlcdc_layer_init(struct drm_device *dev,
struct atmel_hlcdc_layer *layer,
const struct atmel_hlcdc_layer_desc *desc)
+{
- struct atmel_hlcdc_dc *dc = dev->dev_private;
- struct regmap *regmap = dc->hlcdc->regmap;
- unsigned int tmp;
- int ret;
- int i;
- layer->hlcdc = dc->hlcdc;
- layer->desc = desc;
- regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
ATMEL_HLCDC_LAYER_RST);
- for (i = 0; i < desc->formats->nformats; i++) {
int nplanes = drm_format_num_planes(desc->formats->formats[i]);
if (nplanes > layer->max_planes)
layer->max_planes = nplanes;
- }
- atmel_hlcdc_layer_gc_init(layer);
- ret = atmel_hlcdc_layer_dma_init(dev, layer);
- if (ret)
return ret;
- ret = atmel_hlcdc_layer_update_init(dev, layer, desc);
- if (ret)
return ret;
- /* Flush Status Register */
- regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IDR,
0xffffffff);
- regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR,
&tmp);
- for (i = 0; i < layer->max_planes; i++)
regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IER,
ATMEL_HLCDC_LAYER_ADD_IRQ(i) |
ATMEL_HLCDC_LAYER_DONE_IRQ(i));
- return 0;
+}
+void atmel_hlcdc_layer_cleanup(struct drm_device *dev,
struct atmel_hlcdc_layer *layer)
+{
- const struct atmel_hlcdc_layer_desc *desc = layer->desc;
- struct regmap *regmap = layer->hlcdc->regmap;
- regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IDR,
0xffffffff);
- regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
ATMEL_HLCDC_LAYER_RST);
- atmel_hlcdc_layer_dma_cleanup(dev, layer);
- atmel_hlcdc_layer_gc_cleanup(layer);
+} diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h new file mode 100644 index 0000000..868f444 --- /dev/null +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h @@ -0,0 +1,417 @@ +/*
- Copyright (C) 2014 Free Electrons
- Copyright (C) 2014 Atmel
- Author: Boris BREZILLON boris.brezillon@free-electrons.com
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License version 2 as published by
- the Free Software Foundation.
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
- You should have received a copy of the GNU General Public License along with
- this program. If not, see http://www.gnu.org/licenses/.
- */
+#ifndef DRM_ATMEL_HLCDC_LAYER_H +#define DRM_ATMEL_HLCDC_LAYER_H
+#include <linux/mfd/atmel-hlcdc.h>
+#include <drm/drm_crtc.h> +#include <drm/drmP.h>
+#define ATMEL_HLCDC_LAYER_CHER 0x0 +#define ATMEL_HLCDC_LAYER_CHDR 0x4 +#define ATMEL_HLCDC_LAYER_CHSR 0x8 +#define ATMEL_HLCDC_LAYER_DMA_CHAN BIT(0) +#define ATMEL_HLCDC_LAYER_UPDATE BIT(1) +#define ATMEL_HLCDC_LAYER_A2Q BIT(2) +#define ATMEL_HLCDC_LAYER_RST BIT(8)
+#define ATMEL_HLCDC_LAYER_IER 0xc +#define ATMEL_HLCDC_LAYER_IDR 0x10 +#define ATMEL_HLCDC_LAYER_IMR 0x14 +#define ATMEL_HLCDC_LAYER_ISR 0x18 +#define ATMEL_HLCDC_LAYER_DFETCH BIT(0) +#define ATMEL_HLCDC_LAYER_LFETCH BIT(1) +#define ATMEL_HLCDC_LAYER_DMA_IRQ(n) BIT(2 + ((n) * 8)) +#define ATMEL_HLCDC_LAYER_DSCR_IRQ(n) BIT(3 + ((n) * 8)) +#define ATMEL_HLCDC_LAYER_ADD_IRQ(n) BIT(4 + ((n) * 8)) +#define ATMEL_HLCDC_LAYER_DONE_IRQ(n) BIT(5 + ((n) * 8)) +#define ATMEL_HLCDC_LAYER_OVR_IRQ(n) BIT(6 + ((n) * 8))
+#define ATMEL_HLCDC_LAYER_PLANE_HEAD(n) (((n) * 0x10) + 0x1c) +#define ATMEL_HLCDC_LAYER_PLANE_ADDR(n) (((n) * 0x10) + 0x20) +#define ATMEL_HLCDC_LAYER_PLANE_CTRL(n) (((n) * 0x10) + 0x24) +#define ATMEL_HLCDC_LAYER_PLANE_NEXT(n) (((n) * 0x10) + 0x28) +#define ATMEL_HLCDC_LAYER_CFG(p, c) (((c) * 4) + ((p)->max_planes * 0x10) + 0x1c)
+#define ATMEL_HLCDC_LAYER_DMA_CFG_ID 0 +#define ATMEL_HLCDC_LAYER_DMA_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, ATMEL_HLCDC_LAYER_DMA_CFG_ID)
+#define ATMEL_HLCDC_LAYER_FORMAT_CFG_ID 1 +#define ATMEL_HLCDC_LAYER_FORMAT_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, ATMEL_HLCDC_LAYER_FORMAT_CFG_ID) +#define ATMEL_HLCDC_LAYER_RGB (0 << 0) +#define ATMEL_HLCDC_LAYER_CLUT (1 << 0) +#define ATMEL_HLCDC_LAYER_YUV (2 << 0) +#define ATMEL_HLCDC_RGB_MODE(m) (((m) & 0xf) << 4) +#define ATMEL_HLCDC_CLUT_MODE(m) (((m) & 0x3) << 8) +#define ATMEL_HLCDC_YUV_MODE(m) (((m) & 0xf) << 12) +#define ATMEL_HLCDC_YUV422ROT (1 << 16) +#define ATMEL_HLCDC_YUV422SWP (1 << 17) +#define ATMEL_HLCDC_DSCALEOPT (1 << 20)
+#define ATMEL_HLCDC_XRGB4444_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(0)) +#define ATMEL_HLCDC_ARGB4444_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(1)) +#define ATMEL_HLCDC_RGBA4444_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(2)) +#define ATMEL_HLCDC_RGB565_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(3)) +#define ATMEL_HLCDC_ARGB1555_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(4)) +#define ATMEL_HLCDC_XRGB8888_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(9)) +#define ATMEL_HLCDC_RGB888_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(10)) +#define ATMEL_HLCDC_ARGB8888_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(12)) +#define ATMEL_HLCDC_RGBA8888_MODE (ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(13))
+#define ATMEL_HLCDC_AYUV_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(0)) +#define ATMEL_HLCDC_YUYV_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(1)) +#define ATMEL_HLCDC_UYVY_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(2)) +#define ATMEL_HLCDC_YVYU_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(3)) +#define ATMEL_HLCDC_VYUY_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(4)) +#define ATMEL_HLCDC_NV61_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(5)) +#define ATMEL_HLCDC_YUV422_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(6)) +#define ATMEL_HLCDC_NV21_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(7)) +#define ATMEL_HLCDC_YUV420_MODE (ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(8))
+#define ATMEL_HLCDC_LAYER_POS_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.pos) +#define ATMEL_HLCDC_LAYER_SIZE_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.size) +#define ATMEL_HLCDC_LAYER_MEMSIZE_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.memsize) +#define ATMEL_HLCDC_LAYER_XSTRIDE_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.xstride) +#define ATMEL_HLCDC_LAYER_PSTRIDE_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.pstride) +#define ATMEL_HLCDC_LAYER_DFLTCOLOR_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.default_color) +#define ATMEL_HLCDC_LAYER_CRKEY_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.chroma_key) +#define ATMEL_HLCDC_LAYER_CRKEY_MASK_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.chroma_key_mask)
+#define ATMEL_HLCDC_LAYER_GENERAL_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.general_config) +#define ATMEL_HLCDC_LAYER_CRKEY BIT(0) +#define ATMEL_HLCDC_LAYER_INV BIT(1) +#define ATMEL_HLCDC_LAYER_ITER2BL BIT(2) +#define ATMEL_HLCDC_LAYER_ITER BIT(3) +#define ATMEL_HLCDC_LAYER_REVALPHA BIT(4) +#define ATMEL_HLCDC_LAYER_GAEN BIT(5) +#define ATMEL_HLCDC_LAYER_LAEN BIT(6) +#define ATMEL_HLCDC_LAYER_OVR BIT(7) +#define ATMEL_HLCDC_LAYER_DMA BIT(8) +#define ATMEL_HLCDC_LAYER_REP BIT(9) +#define ATMEL_HLCDC_LAYER_DSTKEY BIT(10) +#define ATMEL_HLCDC_LAYER_GA_MASK GENMASK(23, 16) +#define ATMEL_HLCDC_LAYER_GA_SHIFT 16
+#define ATMEL_HLCDC_LAYER_DISC_POS_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.disc_pos)
+#define ATMEL_HLCDC_LAYER_DISC_SIZE_CFG(p) ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.disc_size)
+#define ATMEL_HLCDC_MAX_PLANES 3
+/**
- Atmel HLCDC Layer registers layout structure
- Each HLCDC layer has its own register organization and a given register
- by be placed differently on 2 different layers depending on its
- capabilities.
- This structure stores common registers layout for a given layer and is
- used by HLCDC layer code to chose the appropriate register to write to
- or to read from.
- For all fields, a value of zero means "unsupported".
- See Atmel's datasheet for a detailled description of these registers.
- @xstride: xstride registers
- @pstride: pstride registers
- @pos: position register
- @size: displayed size register
- @memsize: memory size register
- @default_color: default color register
- @chroma_key: chroma key register
- @chroma_key_mask: chroma key mask register
- @general_config: general layer config register
- @disc_pos: discard area position register
- @disc_size: discard area size register
- @color_space_conv: color spave conversion register
- */
+struct atmel_hlcdc_layer_cfg_layout {
- int xstride[ATMEL_HLCDC_MAX_PLANES];
- int pstride[ATMEL_HLCDC_MAX_PLANES];
- int pos;
- int size;
- int memsize;
- int default_color;
- int chroma_key;
- int chroma_key_mask;
- int general_config;
- int disc_pos;
- int disc_size;
- int color_space_conv;
+};
+/**
- Atmel HLCDC GEM flip structure
- This structure is allocated when someone asked for a layer update (most
- likely a DRM plane update, either primary, overlay or cursor plane) and
- released when the layer do not need the reference GEM objects anymore
- (i.e. the layer was disabled or updated).
- @node: list element structure. Used to attach the GEM flip structure to
the garbage collector when the layer no longer need the referenced
GEM objects.
- @gems: the referenced GEM objects. The number of GEM object referenced
depends on the chosen format.
- @remaining_gems: the number of GEM object still referenced by the layer.
When no more objects are referenced the GEM flip structure
is added to the garbage collector.
- */
+struct atmel_hlcdc_layer_gem_flip {
- struct list_head node;
- struct drm_gem_object *gems[ATMEL_HLCDC_MAX_PLANES];
- int remaining_gems;
+};
+/**
- Atmel HLCDC DMA descriptor structure
- This structure is used by the HLCDC DMA engine to schedule a DMA transfer.
- The structure fields must remain in this specific order, because they're
- used by the HLCDC DMA engine, which expect them in this order.
- @addr: buffer DMA address
- @ctrl: DMA transfer options
- @next: next DMA descriptor to fetch
- @gem_flip: the attached gem_flip operation
- */
+struct atmel_hlcdc_dma_channel_dscr {
- dma_addr_t addr;
- u32 ctrl;
- dma_addr_t next;
- struct atmel_hlcdc_layer_gem_flip *gem_flip;
+} __aligned(sizeof(u64));
+/**
- Atmel HLCDC layer types
- */
+enum atmel_hlcdc_layer_type {
- ATMEL_HLCDC_BASE_LAYER,
- ATMEL_HLCDC_OVERLAY_LAYER,
- ATMEL_HLCDC_CURSOR_LAYER,
- ATMEL_HLCDC_PP_LAYER,
+};
+/**
- Atmel HLCDC Supported formats structure
- This structure list all the formats supported by a given layer.
- @nformats: number of supported formats
- @formats: supported formats
- */
+struct atmel_hlcdc_formats {
- int nformats;
- uint32_t *formats;
+};
+/**
- Atmel HLCDC Layer description structure
- This structure describe the capabilities provided by a given layer.
- @name: layer name
- @type: layer type
- @id: layer id
- @regs_offset: offset of the layer registers from the HLCDC registers base
- @nconfigs: number of config registers provided by this layer
- @layout: config registers layout
- @max_width: maximum width supported by this layer (0 means unlimited)
- @max_height: maximum height supported by this layer (0 means unlimited)
- */
+struct atmel_hlcdc_layer_desc {
- const char *name;
- enum atmel_hlcdc_layer_type type;
- int id;
- int regs_offset;
- int nconfigs;
- struct atmel_hlcdc_formats *formats;
- struct atmel_hlcdc_layer_cfg_layout layout;
- int max_width;
- int max_height;
+};
+/**
- Atmel HLCDC Layer Update Slot structure
- This structure stores layer update requests to be applied on next frame.
- This is the base structure behind the atomic layer update infrastructure.
- Atomic layer update provides a way to update all layer's parameters
- simultaneously. This is needed to avoid incompatible sequential updates
- like this one:
- update layer format from RGB888 (1 plane/buffer) to YUV422
- (2 planes/buffers)
- the format update is applied but the DMA channel for the second
- plane/buffer is not enabled
- enable the DMA channel for the second plane
- *@dscrs: DMA channel descriptors
- *@gem_flip: gem_flip object
- *@updated_configs: bitmask used to record modified configs
- *@configs: new config values
- */
+struct atmel_hlcdc_layer_update_slot {
- struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_MAX_PLANES];
- struct atmel_hlcdc_layer_gem_flip *gem_flip;
- unsigned long *updated_configs;
- u32 *configs;
+};
+/**
- Atmel HLCDC Layer Update structure
- This structure provides a way to queue layer update requests.
- At a given time there is at most:
- one pending update request, which means the update request has been
- commited (or validated) and is waiting for the DMA channel(s) to be
- available
- one request being prepared, which means someone started a layer update
- but has not commited it yet. There cannot be more than one started
- request, because the update lock is taken when starting a layer update
- and release when commiting or rolling back the request.
- *@slots: update slots. One is used for pending request and the other one
for started update request
- *@pending: the pending slot index or -1 if no request is pending
- *@next: the started update slot index or -1 no update has been started
- *@pending_lock: lock to access the pending field
- *@lock: layer update lock
- */
+struct atmel_hlcdc_layer_update {
- struct atmel_hlcdc_layer_update_slot slots[2];
- int pending;
- int next;
- spinlock_t pending_lock;
- struct mutex lock;
+};
+/**
- Atmel HLCDC Layer GEM flip garbage collector structure
- This structure is used to schedule GEM object release when we are in
- interrupt context (within atmel_hlcdc_layer_irq function).
- *@list: GEM flip objects to release
- *@list_lock: lock to access the GEM flip list
- *@work: work queue scheduled when there are GEM flip to collect
- *@finished: action to execute the GEM flip and all attached objects have been
released
- *@finished_data: data passed to the finished callback
- *@finished_lock: lock to access finished related fields
- */
+struct atmel_hlcdc_layer_gem_flip_gc {
- struct list_head list;
- spinlock_t list_lock;
- struct work_struct work;
- void (*finished)(void *data);
- void *finished_data;
- struct mutex finished_lock;
+};
+/**
- Atmel HLCDC Layer DMA channel structure
- This structure stores informations on the DMA channel associated to a
- given layer.
- *@enabled: DMA channel status
- *@lock: lock to access the DMA channel fields
- *@cur: current DMA transfers (one for each plane/buffer)
- *@queue: next DMA transfers, to be launch on next frame update
- *@dscrs: allocated DMA descriptors
- */
+struct atmel_hlcdc_layer_dma_channel {
- bool enabled;
- spinlock_t lock;
- struct atmel_hlcdc_dma_channel_dscr *cur[ATMEL_HLCDC_MAX_PLANES];
- struct atmel_hlcdc_dma_channel_dscr *queue[ATMEL_HLCDC_MAX_PLANES];
- struct atmel_hlcdc_dma_channel_dscr *dscrs;
+};
+/**
- Atmel HLCDC Layer structure
- This structure stores information on the layer instance.
- *@desc: layer description
- *@max_planes: maximum planes/buffers that can be associated with this layer.
This depends on the supported formats.
- *@hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
- *@dma: dma channel
- *@gc: GEM flip garbage collector
- *@update: update handler
- */
+struct atmel_hlcdc_layer {
- const struct atmel_hlcdc_layer_desc *desc;
- int max_planes;
- struct atmel_hlcdc *hlcdc;
- struct atmel_hlcdc_layer_dma_channel dma;
- struct atmel_hlcdc_layer_gem_flip_gc gc;
- struct atmel_hlcdc_layer_update update;
+};
+void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer);
+int atmel_hlcdc_layer_init(struct drm_device *dev,
struct atmel_hlcdc_layer *layer,
const struct atmel_hlcdc_layer_desc *desc);
+void atmel_hlcdc_layer_cleanup(struct drm_device *dev,
struct atmel_hlcdc_layer *layer);
+int atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer);
+void atmel_hlcdc_layer_set_finished(struct atmel_hlcdc_layer *layer,
void (*finished)(void *data),
void *data);
+int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer);
+void atmel_hlcdc_layer_update_cfg(struct atmel_hlcdc_layer *layer, int cfg,
u32 mask, u32 val);
+int atmel_hlcdc_layer_update_set_gem(struct atmel_hlcdc_layer *layer,
int plane_id,
struct drm_gem_cma_object *gem,
unsigned int offset);
+void atmel_hlcdc_layer_update_rollback(struct atmel_hlcdc_layer *layer);
+void atmel_hlcdc_layer_update_commit(struct atmel_hlcdc_layer *layer);
+static inline bool +atmel_hlcdc_layer_dma_channel_busy(struct atmel_hlcdc_layer *layer) +{
- struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
- int i;
- for (i = 0; i < layer->max_planes; i++) {
if (dma->queue[i])
return true;
- }
- return false;
+}
+#endif /* DRM_ATMEL_HLCDC_LAYER_H */ diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_panel.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_panel.c new file mode 100644 index 0000000..3295021 --- /dev/null +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_panel.c @@ -0,0 +1,351 @@ +/*
- Copyright (C) 2014 Traphandler
- Copyright (C) 2014 Free Electrons
- Copyright (C) 2014 Atmel
- Author: Jean-Jacques Hiblot jjhiblot@traphandler.com
- Author: Boris BREZILLON boris.brezillon@free-electrons.com
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License version 2 as published by
- the Free Software Foundation.
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
- You should have received a copy of the GNU General Public License along with
- this program. If not, see http://www.gnu.org/licenses/.
- */
+#include <drm/drmP.h> +#include <drm/drm_panel.h>
+#include "atmel_hlcdc_dc.h"
+/**
- Atmel HLCDC RGB output mode
- */
+enum atmel_hlcdc_connector_rgb_mode {
- ATMEL_HLCDC_CONNECTOR_RGB444,
- ATMEL_HLCDC_CONNECTOR_RGB565,
- ATMEL_HLCDC_CONNECTOR_RGB666,
- ATMEL_HLCDC_CONNECTOR_RGB888,
+};
+/**
- Atmel HLCDC Panel structure
- This structure stores informations about an DRM panel connected through
- the RGB connector.
- @mode: RGB output mode
- @connector: DRM connector
- @encoder: DRM encoder
- @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
- @panel: pointer to the attached DRM panel
- @pinctrl: pinctrl state used by the current RGB output mode
- @np: DRM panel DT node
- @dpms: current DPMS mode
- */
+struct atmel_hlcdc_panel {
- enum atmel_hlcdc_connector_rgb_mode mode;
- struct drm_connector connector;
- struct drm_encoder encoder;
- struct atmel_hlcdc *hlcdc;
- struct drm_panel *panel;
- struct pinctrl *pinctrl;
- struct device_node *np;
- int dpms;
+};
+static const char * const pin_state_names[] = {
- [ATMEL_HLCDC_CONNECTOR_RGB444] = "rgb-444",
- [ATMEL_HLCDC_CONNECTOR_RGB565] = "rgb-565",
- [ATMEL_HLCDC_CONNECTOR_RGB666] = "rgb-666",
- [ATMEL_HLCDC_CONNECTOR_RGB888] = "rgb-888",
+};
+static inline struct atmel_hlcdc_panel * +drm_connector_to_atmel_hlcdc_panel(struct drm_connector *connector) +{
- return container_of(connector, struct atmel_hlcdc_panel, connector);
+}
+static inline struct atmel_hlcdc_panel * +drm_encoder_to_atmel_hlcdc_panel(struct drm_encoder *encoder) +{
- return container_of(encoder, struct atmel_hlcdc_panel, encoder);
+}
+static void atmel_hlcdc_panel_encoder_dpms(struct drm_encoder *encoder,
int mode)
+{
- struct atmel_hlcdc_panel *panel =
drm_encoder_to_atmel_hlcdc_panel(encoder);
- struct regmap *regmap = panel->hlcdc->regmap;
- unsigned int status;
- if (mode != DRM_MODE_DPMS_ON)
mode = DRM_MODE_DPMS_OFF;
- if (mode == panel->dpms)
return;
- if (mode != DRM_MODE_DPMS_ON) {
regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP);
while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
(status & ATMEL_HLCDC_DISP))
cpu_relax();
regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC);
while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
(status & ATMEL_HLCDC_SYNC))
cpu_relax();
regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PIXEL_CLK);
while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
(status & ATMEL_HLCDC_PIXEL_CLK))
cpu_relax();
clk_disable_unprepare(panel->hlcdc->sys_clk);
if (panel->panel)
drm_panel_disable(panel->panel);
- } else {
if (panel->panel)
drm_panel_enable(panel->panel);
clk_prepare_enable(panel->hlcdc->sys_clk);
regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PIXEL_CLK);
while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
!(status & ATMEL_HLCDC_PIXEL_CLK))
cpu_relax();
regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC);
while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
!(status & ATMEL_HLCDC_SYNC))
cpu_relax();
regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP);
while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
!(status & ATMEL_HLCDC_DISP))
cpu_relax();
- }
- panel->dpms = mode;
+}
+static bool +atmel_hlcdc_panel_encoder_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted)
+{
- return true;
+}
+static void atmel_hlcdc_panel_encoder_prepare(struct drm_encoder *encoder) +{
- atmel_hlcdc_panel_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+static void atmel_hlcdc_panel_encoder_commit(struct drm_encoder *encoder) +{
- atmel_hlcdc_panel_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+}
+static void +atmel_hlcdc_panel_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted)
+{
- struct atmel_hlcdc_panel *panel =
drm_encoder_to_atmel_hlcdc_panel(encoder);
- unsigned long prate = clk_get_rate(panel->hlcdc->sys_clk);
- unsigned long mode_rate = mode->clock * 1000;
- int div;
- u32 cfg0 = 0;
- if ((prate / 2) < mode_rate) {
prate *= 2;
cfg0 |= ATMEL_HLCDC_CLKSEL;
- }
- div = DIV_ROUND_UP(prate, mode_rate);
- if (div < 2)
div = 2;
- cfg0 |= ATMEL_HLCDC_CLKDIV(div);
- regmap_update_bits(panel->hlcdc->regmap, ATMEL_HLCDC_CFG(0),
ATMEL_HLCDC_CLKSEL | ATMEL_HLCDC_CLKDIV_MASK, cfg0);
+}
+static struct drm_encoder_helper_funcs encoder_helper_funcs = {
- .dpms = atmel_hlcdc_panel_encoder_dpms,
- .mode_fixup = atmel_hlcdc_panel_encoder_mode_fixup,
- .prepare = atmel_hlcdc_panel_encoder_prepare,
- .commit = atmel_hlcdc_panel_encoder_commit,
- .mode_set = atmel_hlcdc_panel_encoder_mode_set,
+};
+static void atmel_hlcdc_panel_encoder_destroy(struct drm_encoder *encoder) +{
- drm_encoder_cleanup(encoder);
- memset(encoder, 0, sizeof(*encoder));
+}
+static const struct drm_encoder_funcs encoder_funcs = {
- .destroy = atmel_hlcdc_panel_encoder_destroy,
+};
+static int atmel_hlcdc_panel_get_modes(struct drm_connector *connector) +{
- struct atmel_hlcdc_panel *panel =
drm_connector_to_atmel_hlcdc_panel(connector);
- int ret;
- if (!panel->panel)
return -ENOENT;
- ret = panel->panel->funcs->get_modes(panel->panel);
- return ret;
+}
+static int atmel_hlcdc_panel_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
+{
- return MODE_OK;
+}
+static struct drm_encoder * +atmel_hlcdc_panel_best_encoder(struct drm_connector *connector) +{
- struct atmel_hlcdc_panel *panel =
drm_connector_to_atmel_hlcdc_panel(connector);
- return &panel->encoder;
+}
+static struct drm_connector_helper_funcs connector_helper_funcs = {
- .get_modes = atmel_hlcdc_panel_get_modes,
- .mode_valid = atmel_hlcdc_panel_mode_valid,
- .best_encoder = atmel_hlcdc_panel_best_encoder,
+};
+static enum drm_connector_status +atmel_hlcdc_panel_connector_detect(struct drm_connector *connector, bool force) +{
- struct atmel_hlcdc_panel *panel =
drm_connector_to_atmel_hlcdc_panel(connector);
- if (!panel->panel) {
panel->panel = of_drm_find_panel(panel->np);
if (panel->panel)
drm_panel_attach(panel->panel, &panel->connector);
- }
- if (panel->panel)
return connector_status_connected;
- return connector_status_disconnected;
+}
+static void +atmel_hlcdc_panel_connector_destroy(struct drm_connector *connector) +{
- struct atmel_hlcdc_panel *panel =
drm_connector_to_atmel_hlcdc_panel(connector);
- if (panel->panel)
drm_panel_detach(panel->panel);
- drm_sysfs_connector_remove(connector);
- drm_connector_cleanup(connector);
- pinctrl_put(panel->pinctrl);
+}
+static const struct drm_connector_funcs connector_funcs = {
- .dpms = drm_helper_connector_dpms,
- .detect = atmel_hlcdc_panel_connector_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = atmel_hlcdc_panel_connector_destroy,
+};
+int atmel_hlcdc_panel_create(struct drm_device *dev) +{
- struct atmel_hlcdc_dc *dc = dev->dev_private;
- struct atmel_hlcdc_panel *panel;
- struct of_phandle_args out_args;
- u32 cfg;
- int ret;
- panel = devm_kzalloc(dev->dev, sizeof(*panel), GFP_KERNEL);
- if (!panel)
return -ENOMEM;
- ret = of_parse_phandle_with_fixed_args(dev->dev->of_node,
"atmel,panel", 2, 0,
&out_args);
- if (ret) {
dev_err(dev->dev, "failed to retrieve panel info from DT\n");
return ret;
- }
- switch (out_args.args[0]) {
- case ATMEL_HLCDC_CONNECTOR_RGB444:
- case ATMEL_HLCDC_CONNECTOR_RGB565:
- case ATMEL_HLCDC_CONNECTOR_RGB666:
- case ATMEL_HLCDC_CONNECTOR_RGB888:
break;
- default:
dev_err(dev->dev, "unknown RGB mode\n");
return -EINVAL;
- }
- panel->np = out_args.np;
- panel->mode = out_args.args[0];
- panel->pinctrl = pinctrl_get_select(dev->dev,
pin_state_names[panel->mode]);
- if (IS_ERR(panel->pinctrl)) {
dev_err(dev->dev, "could not request pinctrl state\n");
return PTR_ERR(panel->pinctrl);
- }
- cfg = out_args.args[1] & (ATMEL_HLCDC_HSPOL |
ATMEL_HLCDC_VSPOL |
ATMEL_HLCDC_VSPDLYS |
ATMEL_HLCDC_VSPDLYE |
ATMEL_HLCDC_DISPPOL |
ATMEL_HLCDC_DISPDLY |
ATMEL_HLCDC_VSPSU |
ATMEL_HLCDC_VSPHO);
- cfg |= panel->mode << 8;
- regmap_write(dc->hlcdc->regmap,
ATMEL_HLCDC_CFG(5),
cfg);
- panel->dpms = DRM_MODE_DPMS_OFF;
- panel->hlcdc = dc->hlcdc;
- drm_connector_init(dev, &panel->connector, &connector_funcs,
DRM_MODE_CONNECTOR_LVDS);
- drm_connector_helper_add(&panel->connector, &connector_helper_funcs);
- panel->connector.dpms = DRM_MODE_DPMS_OFF;
- panel->connector.polled = DRM_CONNECTOR_POLL_CONNECT;
- drm_encoder_init(dev, &panel->encoder, &encoder_funcs,
DRM_MODE_ENCODER_LVDS);
- drm_encoder_helper_add(&panel->encoder, &encoder_helper_funcs);
- drm_mode_connector_attach_encoder(&panel->connector, &panel->encoder);
- drm_sysfs_connector_add(&panel->connector);
- panel->encoder.possible_crtcs = 0x1;
- return 0;
+} diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c new file mode 100644 index 0000000..f428b47 --- /dev/null +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c @@ -0,0 +1,658 @@ +/*
- Copyright (C) 2014 Free Electrons
- Copyright (C) 2014 Atmel
- Author: Boris BREZILLON boris.brezillon@free-electrons.com
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License version 2 as published by
- the Free Software Foundation.
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
- You should have received a copy of the GNU General Public License along with
- this program. If not, see http://www.gnu.org/licenses/.
- */
+#include "atmel_hlcdc_dc.h"
+#define SUBPIXEL_MASK 0xffff
+static uint32_t rgb_formats[] = {
- DRM_FORMAT_XRGB4444,
- DRM_FORMAT_ARGB4444,
- DRM_FORMAT_RGBA4444,
- DRM_FORMAT_ARGB1555,
- DRM_FORMAT_RGB888,
- DRM_FORMAT_ARGB1555,
- DRM_FORMAT_XRGB8888,
- DRM_FORMAT_ARGB8888,
- DRM_FORMAT_RGBA8888,
+};
+struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = {
- .formats = rgb_formats,
- .nformats = ARRAY_SIZE(rgb_formats),
+};
+static uint32_t rgb_and_yuv_formats[] = {
- DRM_FORMAT_XRGB4444,
- DRM_FORMAT_ARGB4444,
- DRM_FORMAT_RGBA4444,
- DRM_FORMAT_ARGB1555,
- DRM_FORMAT_RGB888,
- DRM_FORMAT_ARGB1555,
- DRM_FORMAT_XRGB8888,
- DRM_FORMAT_ARGB8888,
- DRM_FORMAT_RGBA8888,
- DRM_FORMAT_AYUV,
- DRM_FORMAT_YUYV,
- DRM_FORMAT_UYVY,
- DRM_FORMAT_YVYU,
- DRM_FORMAT_VYUY,
- DRM_FORMAT_NV61,
- DRM_FORMAT_YUV422,
- DRM_FORMAT_NV21,
+};
+struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = {
- .formats = rgb_and_yuv_formats,
- .nformats = ARRAY_SIZE(rgb_and_yuv_formats),
+};
+static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode) +{
- switch (format) {
- case DRM_FORMAT_XRGB4444:
*mode = ATMEL_HLCDC_XRGB4444_MODE;
break;
- case DRM_FORMAT_ARGB4444:
*mode = ATMEL_HLCDC_ARGB4444_MODE;
break;
- case DRM_FORMAT_RGBA4444:
*mode = ATMEL_HLCDC_RGBA4444_MODE;
break;
- case DRM_FORMAT_RGB565:
*mode = ATMEL_HLCDC_RGB565_MODE;
break;
- case DRM_FORMAT_RGB888:
*mode = ATMEL_HLCDC_RGB888_MODE;
break;
- case DRM_FORMAT_ARGB1555:
*mode = ATMEL_HLCDC_ARGB1555_MODE;
break;
- case DRM_FORMAT_XRGB8888:
*mode = ATMEL_HLCDC_XRGB8888_MODE;
break;
- case DRM_FORMAT_ARGB8888:
*mode = ATMEL_HLCDC_ARGB8888_MODE;
break;
- case DRM_FORMAT_RGBA8888:
*mode = ATMEL_HLCDC_RGBA8888_MODE;
break;
- case DRM_FORMAT_AYUV:
*mode = ATMEL_HLCDC_AYUV_MODE;
break;
- case DRM_FORMAT_YUYV:
*mode = ATMEL_HLCDC_YUYV_MODE;
break;
- case DRM_FORMAT_UYVY:
*mode = ATMEL_HLCDC_UYVY_MODE;
break;
- case DRM_FORMAT_YVYU:
*mode = ATMEL_HLCDC_YVYU_MODE;
break;
- case DRM_FORMAT_VYUY:
*mode = ATMEL_HLCDC_VYUY_MODE;
break;
- case DRM_FORMAT_NV61:
*mode = ATMEL_HLCDC_NV61_MODE;
break;
- case DRM_FORMAT_YUV422:
*mode = ATMEL_HLCDC_YUV422_MODE;
break;
- case DRM_FORMAT_NV21:
*mode = ATMEL_HLCDC_NV21_MODE;
break;
- case DRM_FORMAT_YUV420:
*mode = ATMEL_HLCDC_YUV420_MODE;
break;
- default:
return -ENOTSUPP;
- }
- return 0;
+}
+static bool atmel_hlcdc_format_embedds_alpha(u32 format) +{
- int i;
- for (i = 0; i < sizeof(format); i++) {
char tmp = (format >> (8 * i)) & 0xff;
if (tmp == 'A')
return true;
- }
- return false;
+}
+static u32 heo_downscaling_xcoef[] = {
- 0x11343311,
- 0x000000f7,
- 0x1635300c,
- 0x000000f9,
- 0x1b362c08,
- 0x000000fb,
- 0x1f372804,
- 0x000000fe,
- 0x24382400,
- 0x00000000,
- 0x28371ffe,
- 0x00000004,
- 0x2c361bfb,
- 0x00000008,
- 0x303516f9,
- 0x0000000c,
+};
+static u32 heo_downscaling_ycoef[] = {
- 0x00123737,
- 0x00173732,
- 0x001b382d,
- 0x001f3928,
- 0x00243824,
- 0x0028391f,
- 0x002d381b,
- 0x00323717,
+};
+static u32 heo_upscaling_xcoef[] = {
- 0xf74949f7,
- 0x00000000,
- 0xf55f33fb,
- 0x000000fe,
- 0xf5701efe,
- 0x000000ff,
- 0xf87c0dff,
- 0x00000000,
- 0x00800000,
- 0x00000000,
- 0x0d7cf800,
- 0x000000ff,
- 0x1e70f5ff,
- 0x000000fe,
- 0x335ff5fe,
- 0x000000fb,
+};
+static u32 heo_upscaling_ycoef[] = {
- 0x00004040,
- 0x00075920,
- 0x00056f0c,
- 0x00027b03,
- 0x00008000,
- 0x00037b02,
- 0x000c6f05,
- 0x00205907,
+};
+int atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
struct drm_crtc *crtc,
int crtc_x, int crtc_y,
unsigned int crtc_w,
unsigned int crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h)
+{
- const struct atmel_hlcdc_layer_cfg_layout *layout =
&plane->layer.desc->layout;
- if (!crtc_h || !crtc_w)
return -EINVAL;
- if (!layout->pos && (crtc_x || crtc_y))
return -EINVAL;
- if ((crtc_x + crtc_w) > crtc->mode.crtc_hdisplay ||
(crtc_y + crtc_h) > crtc->mode.crtc_vdisplay)
return -EINVAL;
- if (!layout->size &&
(crtc->mode.crtc_hdisplay != crtc_w ||
crtc->mode.crtc_vdisplay != crtc_h))
return -EINVAL;
- if (plane->layer.desc->max_height &&
crtc_h > plane->layer.desc->max_height)
return -EINVAL;
- if (plane->layer.desc->max_width &&
crtc_w > plane->layer.desc->max_width)
return -EINVAL;
- if (!layout->memsize && (crtc_h != src_h || crtc_w != src_w))
return -EINVAL;
- if (layout->size)
atmel_hlcdc_layer_update_cfg(&plane->layer,
layout->size,
0xffffffff,
(crtc_w - 1) |
((crtc_h - 1) << 16));
- if (layout->memsize)
atmel_hlcdc_layer_update_cfg(&plane->layer,
layout->memsize,
0xffffffff,
(src_w - 1) |
((src_h - 1) << 16));
- if (layout->pos)
atmel_hlcdc_layer_update_cfg(&plane->layer,
layout->pos,
0xffffffff,
crtc_x | (crtc_y << 16));
- /* TODO: rework the rescaling part */
- if (crtc_w != src_w || crtc_h != src_h) {
u32 factor_reg = 0;
if (crtc_w != src_w) {
int i;
u32 factor;
u32 *coeff_tab = heo_upscaling_xcoef;
u32 max_memsize;
if (crtc_w < src_w)
coeff_tab = heo_downscaling_xcoef;
for (i = 0; i < ARRAY_SIZE(heo_upscaling_xcoef); i++)
atmel_hlcdc_layer_update_cfg(&plane->layer,
17 + i,
0xffffffff,
coeff_tab[i]);
factor = ((8 * 256 * src_w) - (256 * 4)) / crtc_w;
factor++;
max_memsize = ((factor * crtc_w) + (256 * 4)) / 2048;
if (max_memsize > src_w)
factor--;
factor_reg |= factor | 0x80000000;
}
if (crtc_h != src_h) {
int i;
u32 factor;
u32 *coeff_tab = heo_upscaling_ycoef;
u32 max_memsize;
if (crtc_w < src_w)
coeff_tab = heo_downscaling_ycoef;
for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++)
atmel_hlcdc_layer_update_cfg(&plane->layer,
33 + i,
0xffffffff,
coeff_tab[i]);
factor = ((8 * 256 * src_w) - (256 * 4)) / crtc_w;
factor++;
max_memsize = ((factor * crtc_w) + (256 * 4)) / 2048;
if (max_memsize > src_w)
factor--;
factor_reg |= (factor << 16) | 0x80000000;
}
atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff,
factor_reg);
- }
- return 0;
+}
+void atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
u32 format)
+{
- const struct atmel_hlcdc_layer_cfg_layout *layout =
&plane->layer.desc->layout;
- unsigned int cfg = ATMEL_HLCDC_LAYER_DMA;
- if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |
ATMEL_HLCDC_LAYER_ITER;
if (atmel_hlcdc_format_embedds_alpha(format))
cfg |= ATMEL_HLCDC_LAYER_LAEN;
else
cfg |= (plane->alpha << ATMEL_HLCDC_LAYER_GA_SHIFT) |
ATMEL_HLCDC_LAYER_GAEN;
- }
- atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config,
ATMEL_HLCDC_LAYER_ITER2BL |
ATMEL_HLCDC_LAYER_ITER |
ATMEL_HLCDC_LAYER_GAEN |
ATMEL_HLCDC_LAYER_LAEN |
ATMEL_HLCDC_LAYER_OVR |
ATMEL_HLCDC_LAYER_DMA |
ATMEL_HLCDC_LAYER_GA_MASK, cfg);
+}
+int atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
u32 format)
+{
- u32 mode;
- int ret;
- ret = atmel_hlcdc_format_to_plane_mode(format, &mode);
- if (ret)
return ret;
- atmel_hlcdc_layer_update_cfg(&plane->layer,
ATMEL_HLCDC_LAYER_FORMAT_CFG_ID,
0xffffffff,
mode);
- return 0;
+}
+int atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,
u32 pixel_format,
struct drm_gem_cma_object **gems,
unsigned int *pitches,
unsigned int *offsets,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h)
+{
- struct atmel_hlcdc_layer *layer = &plane->layer;
- const struct atmel_hlcdc_layer_cfg_layout *layout =
&layer->desc->layout;
- int bpp[ATMEL_HLCDC_MAX_PLANES];
- int nplanes;
- int ret = 0;
- int i;
- nplanes = drm_format_num_planes(pixel_format);
- for (i = 0; i < nplanes; i++) {
bpp[i] = drm_format_plane_cpp(pixel_format, i);
if (!bpp[i])
return -EINVAL;
if (!gems[i])
return -EINVAL;
- }
- for (i = 0; i < nplanes; i++) {
int offset;
offset = offsets[i] + (src_y * pitches[i]) +
(src_x * bpp[i]);
ret = atmel_hlcdc_layer_update_set_gem(&plane->layer, i,
gems[i], offset);
if (ret)
return ret;
if (layout->xstride[i])
atmel_hlcdc_layer_update_cfg(&plane->layer,
layout->xstride[i],
0xffffffff,
pitches[i] - (bpp[i] * src_w));
- }
- return 0;
+}
+static int atmel_hlcdc_plane_update(struct drm_plane *p,
struct drm_crtc *crtc,
struct drm_framebuffer *fb,
int crtc_x, int crtc_y,
unsigned int crtc_w, unsigned int crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h)
+{
- struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
- struct drm_gem_cma_object *gems[ATMEL_HLCDC_MAX_PLANES];
- int nplanes;
- int ret = 0;
- int i;
- /* Subpixel positioning is not supported */
- if ((src_x | src_y | src_w | src_h) & SUBPIXEL_MASK) {
DRM_DEBUG_KMS("Primary base does not support subpixel positioning\n");
return -EINVAL;
- }
- src_h >>= 16;
- src_w >>= 16;
- src_x >>= 16;
- src_y >>= 16;
- nplanes = drm_format_num_planes(fb->pixel_format);
- if (nplanes > ATMEL_HLCDC_MAX_PLANES)
return -EINVAL;
- for (i = 0; i < nplanes; i++)
gems[i] = drm_fb_cma_get_gem_obj(fb, i);
- atmel_hlcdc_layer_update_start(&plane->layer);
- ret = atmel_hlcdc_plane_update_pos_and_size(plane, crtc,
crtc_x, crtc_y,
crtc_w, crtc_h,
src_x, src_y,
src_w, src_h);
- if (ret)
goto out;
- atmel_hlcdc_plane_update_general_settings(plane, fb->pixel_format);
- ret = atmel_hlcdc_plane_update_format(plane, fb->pixel_format);
- if (ret)
goto out;
- ret = atmel_hlcdc_plane_update_buffers(plane, fb->pixel_format,
gems,
fb->pitches, fb->offsets,
src_x, src_y,
src_w, src_h);
- if (ret)
goto out;
- atmel_hlcdc_layer_update_commit(&plane->layer);
- drm_framebuffer_reference(fb);
- if (plane->base.fb)
drm_framebuffer_unreference(plane->base.fb);
- plane->base.fb = fb;
- return 0;
+out:
- atmel_hlcdc_layer_update_rollback(&plane->layer);
- return ret;
+}
+static int atmel_hlcdc_plane_disable(struct drm_plane *p) +{
- struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
- return atmel_hlcdc_layer_disable(&plane->layer);
+}
+static void atmel_hlcdc_plane_destroy(struct drm_plane *p) +{
- struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
- if (plane->base.fb)
drm_framebuffer_unreference(plane->base.fb);
- atmel_hlcdc_layer_cleanup(p->dev, &plane->layer);
- drm_plane_cleanup(p);
- devm_kfree(p->dev->dev, plane);
+}
+static int atmel_hlcdc_plane_set_alpha(struct atmel_hlcdc_plane *plane,
u8 alpha)
+{
- atmel_hlcdc_layer_update_start(&plane->layer);
- plane->alpha = alpha;
- atmel_hlcdc_plane_update_general_settings(plane,
plane->base.fb->pixel_format);
- atmel_hlcdc_layer_update_commit(&plane->layer);
- return 0;
+}
+static int atmel_hlcdc_plane_set_property(struct drm_plane *p,
struct drm_property *property,
uint64_t value)
+{
- struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
- struct atmel_hlcdc_plane_properties *props = plane->properties;
- if (property == props->alpha)
atmel_hlcdc_plane_set_alpha(plane, value);
- else
return -EINVAL;
- return 0;
+}
+static struct drm_plane_funcs layer_plane_funcs = {
- .update_plane = atmel_hlcdc_plane_update,
- .disable_plane = atmel_hlcdc_plane_disable,
- .set_property = atmel_hlcdc_plane_set_property,
- .destroy = atmel_hlcdc_plane_destroy,
+};
+static struct atmel_hlcdc_plane * +atmel_hlcdc_plane_create(struct drm_device *dev,
const struct atmel_hlcdc_layer_desc *desc)
+{
- struct atmel_hlcdc_plane *plane;
- enum drm_plane_type type;
- int ret;
- plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
- if (!plane)
return ERR_PTR(-ENOMEM);
- ret = atmel_hlcdc_layer_init(dev, &plane->layer, desc);
- if (ret)
return ERR_PTR(ret);
- /* Set default property values*/
- plane->alpha = 0xff;
- if (desc->type == ATMEL_HLCDC_BASE_LAYER)
type = DRM_PLANE_TYPE_PRIMARY;
- else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER)
type = DRM_PLANE_TYPE_CURSOR;
- else
type = DRM_PLANE_TYPE_OVERLAY;
- ret = drm_universal_plane_init(dev, &plane->base, 0,
&layer_plane_funcs,
desc->formats->formats,
desc->formats->nformats, type);
- if (ret)
return ERR_PTR(ret);
- return plane;
+}
+static struct atmel_hlcdc_plane_properties * +atmel_hlcdc_plane_create_properties(struct drm_device *dev) +{
- struct atmel_hlcdc_plane_properties *props;
- props = devm_kzalloc(dev->dev, sizeof(*props), GFP_KERNEL);
- if (!props)
return ERR_PTR(-ENOMEM);
- props->alpha = drm_property_create_range(dev, 0, "alpha", 0, 255);
- if (!props->alpha)
return ERR_PTR(-ENOMEM);
- return props;
+}
+struct atmel_hlcdc_planes * +atmel_hlcdc_create_planes(struct drm_device *dev) +{
- struct atmel_hlcdc_dc *dc = dev->dev_private;
- struct atmel_hlcdc_plane_properties *props;
- struct atmel_hlcdc_planes *planes;
- const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;
- int nlayers = dc->desc->nlayers;
- int i;
- planes = devm_kzalloc(dev->dev, sizeof(*planes), GFP_KERNEL);
- if (!planes)
return ERR_PTR(-ENOMEM);
- for (i = 0; i < nlayers; i++) {
if (descs[i].type == ATMEL_HLCDC_OVERLAY_LAYER)
planes->noverlays++;
- }
- if (planes->noverlays) {
planes->overlays = devm_kzalloc(dev->dev,
planes->noverlays *
sizeof(*planes->overlays),
GFP_KERNEL);
if (!planes->overlays)
return ERR_PTR(-ENOMEM);
- }
- props = atmel_hlcdc_plane_create_properties(dev);
- if (IS_ERR(props))
return ERR_CAST(props);
- planes->noverlays = 0;
- for (i = 0; i < nlayers; i++) {
struct atmel_hlcdc_plane *plane;
if (descs[i].type == ATMEL_HLCDC_PP_LAYER)
continue;
plane = atmel_hlcdc_plane_create(dev, &descs[i]);
if (IS_ERR(plane))
return ERR_CAST(plane);
plane->properties = props;
switch (descs[i].type) {
case ATMEL_HLCDC_BASE_LAYER:
if (planes->primary)
return ERR_PTR(-EINVAL);
planes->primary = plane;
break;
case ATMEL_HLCDC_OVERLAY_LAYER:
planes->overlays[planes->noverlays++] = plane;
drm_object_attach_property(&plane->base.base,
props->alpha, 255);
break;
case ATMEL_HLCDC_CURSOR_LAYER:
if (planes->cursor)
return ERR_PTR(-EINVAL);
planes->cursor = plane;
drm_object_attach_property(&plane->base.base,
props->alpha, 255);
break;
default:
break;
}
- }
- return planes;
+} diff --git a/drivers/gpu/drm/atmel_hlcdc/Kconfig b/drivers/gpu/drm/atmel_hlcdc/Kconfig new file mode 100644 index 0000000..59c8eeb --- /dev/null +++ b/drivers/gpu/drm/atmel_hlcdc/Kconfig @@ -0,0 +1,11 @@ +config DRM_ATMEL_HLCDC
- tristate "DRM Support for ATMEL HLCDC Display Controller"
- depends on DRM && OF && ARM && COMMON_CLK
- select DRM_GEM_CMA_HELPER
- select DRM_KMS_HELPER
- select DRM_KMS_FB_HELPER
- select DRM_KMS_CMA_HELPER
- select DRM_PANEL
- help
Choose this option if you have an ATMEL SoC with an HLCDC display
controller (i.e. at91sam9n12, at91sam9x5 family or sama5d3 family).
diff --git a/drivers/gpu/drm/atmel_hlcdc/Makefile b/drivers/gpu/drm/atmel_hlcdc/Makefile new file mode 100644 index 0000000..08de8d7 --- /dev/null +++ b/drivers/gpu/drm/atmel_hlcdc/Makefile @@ -0,0 +1,8 @@ +atmel_hlcdc-y := atmel_hlcdc_crtc.o \
atmel_hlcdc_drv.o \
atmel_hlcdc_layer.o \
atmel_hlcdc_panel.o \
atmel_hlcdc_plane.o \
atmel_hlcdc_pwm.o
+obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel_hlcdc.o