| /* |
| * shmob_drm_plane.c -- SH Mobile DRM Planes |
| * |
| * Copyright (C) 2012 Renesas Corporation |
| * |
| * Laurent Pinchart (laurent.pinchart@ideasonboard.com) |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| */ |
| |
| #include <drm/drmP.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 <video/sh_mobile_meram.h> |
| |
| #include "shmob_drm_drv.h" |
| #include "shmob_drm_kms.h" |
| #include "shmob_drm_plane.h" |
| #include "shmob_drm_regs.h" |
| |
| struct shmob_drm_plane { |
| struct drm_plane plane; |
| unsigned int index; |
| unsigned int alpha; |
| |
| const struct shmob_drm_format_info *format; |
| unsigned long dma[2]; |
| |
| unsigned int src_x; |
| unsigned int src_y; |
| unsigned int crtc_x; |
| unsigned int crtc_y; |
| unsigned int crtc_w; |
| unsigned int crtc_h; |
| }; |
| |
| #define to_shmob_plane(p) container_of(p, struct shmob_drm_plane, plane) |
| |
| static void shmob_drm_plane_compute_base(struct shmob_drm_plane *splane, |
| struct drm_framebuffer *fb, |
| int x, int y) |
| { |
| struct drm_gem_cma_object *gem; |
| unsigned int bpp; |
| |
| bpp = splane->format->yuv ? 8 : splane->format->bpp; |
| gem = drm_fb_cma_get_gem_obj(fb, 0); |
| splane->dma[0] = gem->paddr + fb->offsets[0] |
| + y * fb->pitches[0] + x * bpp / 8; |
| |
| if (splane->format->yuv) { |
| bpp = splane->format->bpp - 8; |
| gem = drm_fb_cma_get_gem_obj(fb, 1); |
| splane->dma[1] = gem->paddr + fb->offsets[1] |
| + y / (bpp == 4 ? 2 : 1) * fb->pitches[1] |
| + x * (bpp == 16 ? 2 : 1); |
| } |
| } |
| |
| static void __shmob_drm_plane_setup(struct shmob_drm_plane *splane, |
| struct drm_framebuffer *fb) |
| { |
| struct shmob_drm_device *sdev = splane->plane.dev->dev_private; |
| u32 format; |
| |
| /* TODO: Support ROP3 mode */ |
| format = LDBBSIFR_EN | (splane->alpha << LDBBSIFR_LAY_SHIFT); |
| |
| switch (splane->format->fourcc) { |
| case DRM_FORMAT_RGB565: |
| case DRM_FORMAT_NV21: |
| case DRM_FORMAT_NV61: |
| case DRM_FORMAT_NV42: |
| format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW; |
| break; |
| case DRM_FORMAT_RGB888: |
| case DRM_FORMAT_NV12: |
| case DRM_FORMAT_NV16: |
| case DRM_FORMAT_NV24: |
| format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW | LDBBSIFR_SWPB; |
| break; |
| case DRM_FORMAT_ARGB8888: |
| default: |
| format |= LDBBSIFR_SWPL; |
| break; |
| } |
| |
| switch (splane->format->fourcc) { |
| case DRM_FORMAT_RGB565: |
| format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16; |
| break; |
| case DRM_FORMAT_RGB888: |
| format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24; |
| break; |
| case DRM_FORMAT_ARGB8888: |
| format |= LDBBSIFR_AL_PK | LDBBSIFR_RY | LDDFR_PKF_ARGB32; |
| break; |
| case DRM_FORMAT_NV12: |
| case DRM_FORMAT_NV21: |
| format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_420; |
| break; |
| case DRM_FORMAT_NV16: |
| case DRM_FORMAT_NV61: |
| format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_422; |
| break; |
| case DRM_FORMAT_NV24: |
| case DRM_FORMAT_NV42: |
| format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_444; |
| break; |
| } |
| |
| #define plane_reg_dump(sdev, splane, reg) \ |
| dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x 0x%08x\n", __func__, \ |
| splane->index, #reg, \ |
| lcdc_read(sdev, reg(splane->index)), \ |
| lcdc_read(sdev, reg(splane->index) + LCDC_SIDE_B_OFFSET)) |
| |
| plane_reg_dump(sdev, splane, LDBnBSIFR); |
| plane_reg_dump(sdev, splane, LDBnBSSZR); |
| plane_reg_dump(sdev, splane, LDBnBLOCR); |
| plane_reg_dump(sdev, splane, LDBnBSMWR); |
| plane_reg_dump(sdev, splane, LDBnBSAYR); |
| plane_reg_dump(sdev, splane, LDBnBSACR); |
| |
| lcdc_write(sdev, LDBCR, LDBCR_UPC(splane->index)); |
| dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x\n", __func__, splane->index, |
| "LDBCR", lcdc_read(sdev, LDBCR)); |
| |
| lcdc_write(sdev, LDBnBSIFR(splane->index), format); |
| |
| lcdc_write(sdev, LDBnBSSZR(splane->index), |
| (splane->crtc_h << LDBBSSZR_BVSS_SHIFT) | |
| (splane->crtc_w << LDBBSSZR_BHSS_SHIFT)); |
| lcdc_write(sdev, LDBnBLOCR(splane->index), |
| (splane->crtc_y << LDBBLOCR_CVLC_SHIFT) | |
| (splane->crtc_x << LDBBLOCR_CHLC_SHIFT)); |
| lcdc_write(sdev, LDBnBSMWR(splane->index), |
| fb->pitches[0] << LDBBSMWR_BSMW_SHIFT); |
| |
| shmob_drm_plane_compute_base(splane, fb, splane->src_x, splane->src_y); |
| |
| lcdc_write(sdev, LDBnBSAYR(splane->index), splane->dma[0]); |
| if (splane->format->yuv) |
| lcdc_write(sdev, LDBnBSACR(splane->index), splane->dma[1]); |
| |
| lcdc_write(sdev, LDBCR, |
| LDBCR_UPF(splane->index) | LDBCR_UPD(splane->index)); |
| dev_dbg(sdev->ddev->dev, "%s(%u): %s 0x%08x\n", __func__, splane->index, |
| "LDBCR", lcdc_read(sdev, LDBCR)); |
| |
| plane_reg_dump(sdev, splane, LDBnBSIFR); |
| plane_reg_dump(sdev, splane, LDBnBSSZR); |
| plane_reg_dump(sdev, splane, LDBnBLOCR); |
| plane_reg_dump(sdev, splane, LDBnBSMWR); |
| plane_reg_dump(sdev, splane, LDBnBSAYR); |
| plane_reg_dump(sdev, splane, LDBnBSACR); |
| } |
| |
| void shmob_drm_plane_setup(struct drm_plane *plane) |
| { |
| struct shmob_drm_plane *splane = to_shmob_plane(plane); |
| |
| if (plane->fb == NULL) |
| return; |
| |
| __shmob_drm_plane_setup(splane, plane->fb); |
| } |
| |
| static int |
| shmob_drm_plane_update(struct drm_plane *plane, 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 shmob_drm_plane *splane = to_shmob_plane(plane); |
| struct shmob_drm_device *sdev = plane->dev->dev_private; |
| const struct shmob_drm_format_info *format; |
| |
| format = shmob_drm_format_info(fb->pixel_format); |
| if (format == NULL) { |
| dev_dbg(sdev->dev, "update_plane: unsupported format %08x\n", |
| fb->pixel_format); |
| return -EINVAL; |
| } |
| |
| if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) { |
| dev_dbg(sdev->dev, "%s: scaling not supported\n", __func__); |
| return -EINVAL; |
| } |
| |
| splane->format = format; |
| |
| splane->src_x = src_x >> 16; |
| splane->src_y = src_y >> 16; |
| splane->crtc_x = crtc_x; |
| splane->crtc_y = crtc_y; |
| splane->crtc_w = crtc_w; |
| splane->crtc_h = crtc_h; |
| |
| __shmob_drm_plane_setup(splane, fb); |
| return 0; |
| } |
| |
| static int shmob_drm_plane_disable(struct drm_plane *plane) |
| { |
| struct shmob_drm_plane *splane = to_shmob_plane(plane); |
| struct shmob_drm_device *sdev = plane->dev->dev_private; |
| |
| splane->format = NULL; |
| |
| lcdc_write(sdev, LDBnBSIFR(splane->index), 0); |
| return 0; |
| } |
| |
| static void shmob_drm_plane_destroy(struct drm_plane *plane) |
| { |
| shmob_drm_plane_disable(plane); |
| drm_plane_cleanup(plane); |
| } |
| |
| static const struct drm_plane_funcs shmob_drm_plane_funcs = { |
| .update_plane = shmob_drm_plane_update, |
| .disable_plane = shmob_drm_plane_disable, |
| .destroy = shmob_drm_plane_destroy, |
| }; |
| |
| static const uint32_t formats[] = { |
| DRM_FORMAT_RGB565, |
| DRM_FORMAT_RGB888, |
| DRM_FORMAT_ARGB8888, |
| DRM_FORMAT_NV12, |
| DRM_FORMAT_NV21, |
| DRM_FORMAT_NV16, |
| DRM_FORMAT_NV61, |
| DRM_FORMAT_NV24, |
| DRM_FORMAT_NV42, |
| }; |
| |
| int shmob_drm_plane_create(struct shmob_drm_device *sdev, unsigned int index) |
| { |
| struct shmob_drm_plane *splane; |
| int ret; |
| |
| splane = devm_kzalloc(sdev->dev, sizeof(*splane), GFP_KERNEL); |
| if (splane == NULL) |
| return -ENOMEM; |
| |
| splane->index = index; |
| splane->alpha = 255; |
| |
| ret = drm_plane_init(sdev->ddev, &splane->plane, 1, |
| &shmob_drm_plane_funcs, formats, |
| ARRAY_SIZE(formats), false); |
| |
| return ret; |
| } |