M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
config DRM_EXYNOS
tristate "DRM Support for Samsung SoC EXYNOS Series"
depends on DRM && PLAT_SAMSUNG
select DRM_KMS_HELPER
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
help
Choose this option if you have a Samsung SoC EXYNOS chipset.
If M is selected the module will be called exynosdrm.
config DRM_EXYNOS_FIMD
bool "Exynos DRM FIMD"
depends on DRM_EXYNOS && !FB_S3C
help
Choose this option if you want to use Exynos FIMD for DRM.
config DRM_EXYNOS_HDMI
bool "Exynos DRM HDMI"
depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_TV
help
Choose this option if you want to use Exynos HDMI for DRM.
config DRM_EXYNOS_VIDI
bool "Exynos DRM Virtual Display"
depends on DRM_EXYNOS
help
Choose this option if you want to use Exynos VIDI for DRM.
+17
View File
@@ -0,0 +1,17 @@
#
# Makefile for the drm device driver. This driver provides support for the
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/exynos
exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \
exynos_drm_crtc.o exynos_drm_fbdev.o exynos_drm_fb.o \
exynos_drm_buf.o exynos_drm_gem.o exynos_drm_core.o \
exynos_drm_plane.o
exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o
exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o \
exynos_ddc.o exynos_hdmiphy.o \
exynos_drm_hdmi.o
exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o
obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2011 Samsung Electronics Co.Ltd
* Authors:
* Seung-Woo Kim <sw0312.kim@samsung.com>
* Inki Dae <inki.dae@samsung.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 "drmP.h"
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include "exynos_drm_drv.h"
#include "exynos_hdmi.h"
static int s5p_ddc_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
hdmi_attach_ddc_client(client);
dev_info(&client->adapter->dev, "attached s5p_ddc "
"into i2c adapter successfully\n");
return 0;
}
static int s5p_ddc_remove(struct i2c_client *client)
{
dev_info(&client->adapter->dev, "detached s5p_ddc "
"from i2c adapter successfully\n");
return 0;
}
static struct i2c_device_id ddc_idtable[] = {
{"s5p_ddc", 0},
{ },
};
struct i2c_driver ddc_driver = {
.driver = {
.name = "s5p_ddc",
.owner = THIS_MODULE,
},
.id_table = ddc_idtable,
.probe = s5p_ddc_probe,
.remove = __devexit_p(s5p_ddc_remove),
.command = NULL,
};
@@ -0,0 +1,213 @@
/* exynos_drm_buf.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Author: Inki Dae <inki.dae@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "drmP.h"
#include "drm.h"
#include "exynos_drm.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_gem.h"
#include "exynos_drm_buf.h"
static int lowlevel_buffer_allocate(struct drm_device *dev,
unsigned int flags, struct exynos_drm_gem_buf *buf)
{
dma_addr_t start_addr;
unsigned int npages, page_size, i = 0;
struct scatterlist *sgl;
int ret = 0;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (IS_NONCONTIG_BUFFER(flags)) {
DRM_DEBUG_KMS("not support allocation type.\n");
return -EINVAL;
}
if (buf->dma_addr) {
DRM_DEBUG_KMS("already allocated.\n");
return 0;
}
if (buf->size >= SZ_1M) {
npages = buf->size >> SECTION_SHIFT;
page_size = SECTION_SIZE;
} else if (buf->size >= SZ_64K) {
npages = buf->size >> 16;
page_size = SZ_64K;
} else {
npages = buf->size >> PAGE_SHIFT;
page_size = PAGE_SIZE;
}
buf->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
if (!buf->sgt) {
DRM_ERROR("failed to allocate sg table.\n");
return -ENOMEM;
}
ret = sg_alloc_table(buf->sgt, npages, GFP_KERNEL);
if (ret < 0) {
DRM_ERROR("failed to initialize sg table.\n");
kfree(buf->sgt);
buf->sgt = NULL;
return -ENOMEM;
}
buf->kvaddr = dma_alloc_writecombine(dev->dev, buf->size,
&buf->dma_addr, GFP_KERNEL);
if (!buf->kvaddr) {
DRM_ERROR("failed to allocate buffer.\n");
ret = -ENOMEM;
goto err1;
}
buf->pages = kzalloc(sizeof(struct page) * npages, GFP_KERNEL);
if (!buf->pages) {
DRM_ERROR("failed to allocate pages.\n");
ret = -ENOMEM;
goto err2;
}
sgl = buf->sgt->sgl;
start_addr = buf->dma_addr;
while (i < npages) {
buf->pages[i] = phys_to_page(start_addr);
sg_set_page(sgl, buf->pages[i], page_size, 0);
sg_dma_address(sgl) = start_addr;
start_addr += page_size;
sgl = sg_next(sgl);
i++;
}
DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n",
(unsigned long)buf->kvaddr,
(unsigned long)buf->dma_addr,
buf->size);
return ret;
err2:
dma_free_writecombine(dev->dev, buf->size, buf->kvaddr,
(dma_addr_t)buf->dma_addr);
buf->dma_addr = (dma_addr_t)NULL;
err1:
sg_free_table(buf->sgt);
kfree(buf->sgt);
buf->sgt = NULL;
return ret;
}
static void lowlevel_buffer_deallocate(struct drm_device *dev,
unsigned int flags, struct exynos_drm_gem_buf *buf)
{
DRM_DEBUG_KMS("%s.\n", __FILE__);
/*
* release only physically continuous memory and
* non-continuous memory would be released by exynos
* gem framework.
*/
if (IS_NONCONTIG_BUFFER(flags)) {
DRM_DEBUG_KMS("not support allocation type.\n");
return;
}
if (!buf->dma_addr) {
DRM_DEBUG_KMS("dma_addr is invalid.\n");
return;
}
DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n",
(unsigned long)buf->kvaddr,
(unsigned long)buf->dma_addr,
buf->size);
sg_free_table(buf->sgt);
kfree(buf->sgt);
buf->sgt = NULL;
kfree(buf->pages);
buf->pages = NULL;
dma_free_writecombine(dev->dev, buf->size, buf->kvaddr,
(dma_addr_t)buf->dma_addr);
buf->dma_addr = (dma_addr_t)NULL;
}
struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev,
unsigned int size)
{
struct exynos_drm_gem_buf *buffer;
DRM_DEBUG_KMS("%s.\n", __FILE__);
DRM_DEBUG_KMS("desired size = 0x%x\n", size);
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
if (!buffer) {
DRM_ERROR("failed to allocate exynos_drm_gem_buf.\n");
return NULL;
}
buffer->size = size;
return buffer;
}
void exynos_drm_fini_buf(struct drm_device *dev,
struct exynos_drm_gem_buf *buffer)
{
DRM_DEBUG_KMS("%s.\n", __FILE__);
if (!buffer) {
DRM_DEBUG_KMS("buffer is null.\n");
return;
}
kfree(buffer);
buffer = NULL;
}
int exynos_drm_alloc_buf(struct drm_device *dev,
struct exynos_drm_gem_buf *buf, unsigned int flags)
{
/*
* allocate memory region and set the memory information
* to vaddr and dma_addr of a buffer object.
*/
if (lowlevel_buffer_allocate(dev, flags, buf) < 0)
return -ENOMEM;
return 0;
}
void exynos_drm_free_buf(struct drm_device *dev,
unsigned int flags, struct exynos_drm_gem_buf *buffer)
{
lowlevel_buffer_deallocate(dev, flags, buffer);
}
@@ -0,0 +1,47 @@
/* exynos_drm_buf.h
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Author: Inki Dae <inki.dae@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _EXYNOS_DRM_BUF_H_
#define _EXYNOS_DRM_BUF_H_
/* create and initialize buffer object. */
struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev,
unsigned int size);
/* destroy buffer object. */
void exynos_drm_fini_buf(struct drm_device *dev,
struct exynos_drm_gem_buf *buffer);
/* allocate physical memory region and setup sgt and pages. */
int exynos_drm_alloc_buf(struct drm_device *dev,
struct exynos_drm_gem_buf *buf,
unsigned int flags);
/* release physical memory region, sgt and pages. */
void exynos_drm_free_buf(struct drm_device *dev,
unsigned int flags,
struct exynos_drm_gem_buf *buffer);
#endif
@@ -0,0 +1,354 @@
/*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@samsung.com>
* Seung-Woo Kim <sw0312.kim@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "drmP.h"
#include "drm_crtc_helper.h"
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_encoder.h"
#define MAX_EDID 256
#define to_exynos_connector(x) container_of(x, struct exynos_drm_connector,\
drm_connector)
struct exynos_drm_connector {
struct drm_connector drm_connector;
uint32_t encoder_id;
struct exynos_drm_manager *manager;
};
/* convert exynos_video_timings to drm_display_mode */
static inline void
convert_to_display_mode(struct drm_display_mode *mode,
struct exynos_drm_panel_info *panel)
{
struct fb_videomode *timing = &panel->timing;
DRM_DEBUG_KMS("%s\n", __FILE__);
mode->clock = timing->pixclock / 1000;
mode->vrefresh = timing->refresh;
mode->hdisplay = timing->xres;
mode->hsync_start = mode->hdisplay + timing->right_margin;
mode->hsync_end = mode->hsync_start + timing->hsync_len;
mode->htotal = mode->hsync_end + timing->left_margin;
mode->vdisplay = timing->yres;
mode->vsync_start = mode->vdisplay + timing->lower_margin;
mode->vsync_end = mode->vsync_start + timing->vsync_len;
mode->vtotal = mode->vsync_end + timing->upper_margin;
mode->width_mm = panel->width_mm;
mode->height_mm = panel->height_mm;
if (timing->vmode & FB_VMODE_INTERLACED)
mode->flags |= DRM_MODE_FLAG_INTERLACE;
if (timing->vmode & FB_VMODE_DOUBLE)
mode->flags |= DRM_MODE_FLAG_DBLSCAN;
}
/* convert drm_display_mode to exynos_video_timings */
static inline void
convert_to_video_timing(struct fb_videomode *timing,
struct drm_display_mode *mode)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
memset(timing, 0, sizeof(*timing));
timing->pixclock = mode->clock * 1000;
timing->refresh = drm_mode_vrefresh(mode);
timing->xres = mode->hdisplay;
timing->right_margin = mode->hsync_start - mode->hdisplay;
timing->hsync_len = mode->hsync_end - mode->hsync_start;
timing->left_margin = mode->htotal - mode->hsync_end;
timing->yres = mode->vdisplay;
timing->lower_margin = mode->vsync_start - mode->vdisplay;
timing->vsync_len = mode->vsync_end - mode->vsync_start;
timing->upper_margin = mode->vtotal - mode->vsync_end;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
timing->vmode = FB_VMODE_INTERLACED;
else
timing->vmode = FB_VMODE_NONINTERLACED;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
timing->vmode |= FB_VMODE_DOUBLE;
}
static int exynos_drm_connector_get_modes(struct drm_connector *connector)
{
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
struct exynos_drm_manager *manager = exynos_connector->manager;
struct exynos_drm_display_ops *display_ops = manager->display_ops;
unsigned int count;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!display_ops) {
DRM_DEBUG_KMS("display_ops is null.\n");
return 0;
}
/*
* if get_edid() exists then get_edid() callback of hdmi side
* is called to get edid data through i2c interface else
* get timing from the FIMD driver(display controller).
*
* P.S. in case of lcd panel, count is always 1 if success
* because lcd panel has only one mode.
*/
if (display_ops->get_edid) {
int ret;
void *edid;
edid = kzalloc(MAX_EDID, GFP_KERNEL);
if (!edid) {
DRM_ERROR("failed to allocate edid\n");
return 0;
}
ret = display_ops->get_edid(manager->dev, connector,
edid, MAX_EDID);
if (ret < 0) {
DRM_ERROR("failed to get edid data.\n");
kfree(edid);
edid = NULL;
return 0;
}
drm_mode_connector_update_edid_property(connector, edid);
count = drm_add_edid_modes(connector, edid);
kfree(connector->display_info.raw_edid);
connector->display_info.raw_edid = edid;
} else {
struct drm_display_mode *mode = drm_mode_create(connector->dev);
struct exynos_drm_panel_info *panel;
if (display_ops->get_panel)
panel = display_ops->get_panel(manager->dev);
else {
drm_mode_destroy(connector->dev, mode);
return 0;
}
convert_to_display_mode(mode, panel);
connector->display_info.width_mm = mode->width_mm;
connector->display_info.height_mm = mode->height_mm;
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_set_name(mode);
drm_mode_probed_add(connector, mode);
count = 1;
}
return count;
}
static int exynos_drm_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
struct exynos_drm_manager *manager = exynos_connector->manager;
struct exynos_drm_display_ops *display_ops = manager->display_ops;
struct fb_videomode timing;
int ret = MODE_BAD;
DRM_DEBUG_KMS("%s\n", __FILE__);
convert_to_video_timing(&timing, mode);
if (display_ops && display_ops->check_timing)
if (!display_ops->check_timing(manager->dev, (void *)&timing))
ret = MODE_OK;
return ret;
}
struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
struct drm_mode_object *obj;
struct drm_encoder *encoder;
DRM_DEBUG_KMS("%s\n", __FILE__);
obj = drm_mode_object_find(dev, exynos_connector->encoder_id,
DRM_MODE_OBJECT_ENCODER);
if (!obj) {
DRM_DEBUG_KMS("Unknown ENCODER ID %d\n",
exynos_connector->encoder_id);
return NULL;
}
encoder = obj_to_encoder(obj);
return encoder;
}
static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
.get_modes = exynos_drm_connector_get_modes,
.mode_valid = exynos_drm_connector_mode_valid,
.best_encoder = exynos_drm_best_encoder,
};
static int exynos_drm_connector_fill_modes(struct drm_connector *connector,
unsigned int max_width, unsigned int max_height)
{
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
struct exynos_drm_manager *manager = exynos_connector->manager;
struct exynos_drm_manager_ops *ops = manager->ops;
unsigned int width, height;
width = max_width;
height = max_height;
/*
* if specific driver want to find desired_mode using maxmum
* resolution then get max width and height from that driver.
*/
if (ops && ops->get_max_resol)
ops->get_max_resol(manager->dev, &width, &height);
return drm_helper_probe_single_connector_modes(connector, width,
height);
}
/* get detection status of display device. */
static enum drm_connector_status
exynos_drm_connector_detect(struct drm_connector *connector, bool force)
{
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
struct exynos_drm_manager *manager = exynos_connector->manager;
struct exynos_drm_display_ops *display_ops =
manager->display_ops;
enum drm_connector_status status = connector_status_disconnected;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (display_ops && display_ops->is_connected) {
if (display_ops->is_connected(manager->dev))
status = connector_status_connected;
else
status = connector_status_disconnected;
}
return status;
}
static void exynos_drm_connector_destroy(struct drm_connector *connector)
{
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
DRM_DEBUG_KMS("%s\n", __FILE__);
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
kfree(exynos_connector);
}
static struct drm_connector_funcs exynos_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.fill_modes = exynos_drm_connector_fill_modes,
.detect = exynos_drm_connector_detect,
.destroy = exynos_drm_connector_destroy,
};
struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
struct drm_encoder *encoder)
{
struct exynos_drm_connector *exynos_connector;
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
struct drm_connector *connector;
int type;
int err;
DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_connector = kzalloc(sizeof(*exynos_connector), GFP_KERNEL);
if (!exynos_connector) {
DRM_ERROR("failed to allocate connector\n");
return NULL;
}
connector = &exynos_connector->drm_connector;
switch (manager->display_ops->type) {
case EXYNOS_DISPLAY_TYPE_HDMI:
type = DRM_MODE_CONNECTOR_HDMIA;
connector->interlace_allowed = true;
connector->polled = DRM_CONNECTOR_POLL_HPD;
break;
case EXYNOS_DISPLAY_TYPE_VIDI:
type = DRM_MODE_CONNECTOR_VIRTUAL;
connector->polled = DRM_CONNECTOR_POLL_HPD;
break;
default:
type = DRM_MODE_CONNECTOR_Unknown;
break;
}
drm_connector_init(dev, connector, &exynos_connector_funcs, type);
drm_connector_helper_add(connector, &exynos_connector_helper_funcs);
err = drm_sysfs_connector_add(connector);
if (err)
goto err_connector;
exynos_connector->encoder_id = encoder->base.id;
exynos_connector->manager = manager;
connector->encoder = encoder;
err = drm_mode_connector_attach_encoder(connector, encoder);
if (err) {
DRM_ERROR("failed to attach a connector to a encoder\n");
goto err_sysfs;
}
DRM_DEBUG_KMS("connector has been created\n");
return connector;
err_sysfs:
drm_sysfs_connector_remove(connector);
err_connector:
drm_connector_cleanup(connector);
kfree(exynos_connector);
return NULL;
}
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@samsung.com>
* Seung-Woo Kim <sw0312.kim@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _EXYNOS_DRM_CONNECTOR_H_
#define _EXYNOS_DRM_CONNECTOR_H_
struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
struct drm_encoder *encoder);
#endif
@@ -0,0 +1,217 @@
/* exynos_drm_core.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Author:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@samsung.com>
* Seung-Woo Kim <sw0312.kim@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "drmP.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_encoder.h"
#include "exynos_drm_connector.h"
#include "exynos_drm_fbdev.h"
static LIST_HEAD(exynos_drm_subdrv_list);
static struct drm_device *drm_dev;
static int exynos_drm_subdrv_probe(struct drm_device *dev,
struct exynos_drm_subdrv *subdrv)
{
struct drm_encoder *encoder;
struct drm_connector *connector;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (subdrv->probe) {
int ret;
/*
* this probe callback would be called by sub driver
* after setting of all resources to this sub driver,
* such as clock, irq and register map are done or by load()
* of exynos drm driver.
*
* P.S. note that this driver is considered for modularization.
*/
ret = subdrv->probe(dev, subdrv->dev);
if (ret)
return ret;
}
if (!subdrv->manager)
return 0;
subdrv->manager->dev = subdrv->dev;
/* create and initialize a encoder for this sub driver. */
encoder = exynos_drm_encoder_create(dev, subdrv->manager,
(1 << MAX_CRTC) - 1);
if (!encoder) {
DRM_ERROR("failed to create encoder\n");
return -EFAULT;
}
/*
* create and initialize a connector for this sub driver and
* attach the encoder created above to the connector.
*/
connector = exynos_drm_connector_create(dev, encoder);
if (!connector) {
DRM_ERROR("failed to create connector\n");
encoder->funcs->destroy(encoder);
return -EFAULT;
}
subdrv->encoder = encoder;
subdrv->connector = connector;
return 0;
}
static void exynos_drm_subdrv_remove(struct drm_device *dev,
struct exynos_drm_subdrv *subdrv)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (subdrv->remove)
subdrv->remove(dev);
if (subdrv->encoder) {
struct drm_encoder *encoder = subdrv->encoder;
encoder->funcs->destroy(encoder);
subdrv->encoder = NULL;
}
if (subdrv->connector) {
struct drm_connector *connector = subdrv->connector;
connector->funcs->destroy(connector);
subdrv->connector = NULL;
}
}
int exynos_drm_device_register(struct drm_device *dev)
{
struct exynos_drm_subdrv *subdrv, *n;
int err;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (!dev)
return -EINVAL;
drm_dev = dev;
list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) {
subdrv->drm_dev = dev;
err = exynos_drm_subdrv_probe(dev, subdrv);
if (err) {
DRM_DEBUG("exynos drm subdrv probe failed.\n");
list_del(&subdrv->list);
}
}
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_device_register);
int exynos_drm_device_unregister(struct drm_device *dev)
{
struct exynos_drm_subdrv *subdrv;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (!dev) {
WARN(1, "Unexpected drm device unregister!\n");
return -EINVAL;
}
list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list)
exynos_drm_subdrv_remove(dev, subdrv);
drm_dev = NULL;
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_device_unregister);
int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (!subdrv)
return -EINVAL;
list_add_tail(&subdrv->list, &exynos_drm_subdrv_list);
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register);
int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (!subdrv)
return -EINVAL;
list_del(&subdrv->list);
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister);
int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file)
{
struct exynos_drm_subdrv *subdrv;
int ret;
list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
if (subdrv->open) {
ret = subdrv->open(dev, subdrv->dev, file);
if (ret)
goto err;
}
}
return 0;
err:
list_for_each_entry_reverse(subdrv, &subdrv->list, list) {
if (subdrv->close)
subdrv->close(dev, subdrv->dev, file);
}
return ret;
}
EXPORT_SYMBOL_GPL(exynos_drm_subdrv_open);
void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file)
{
struct exynos_drm_subdrv *subdrv;
list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
if (subdrv->close)
subdrv->close(dev, subdrv->dev, file);
}
}
EXPORT_SYMBOL_GPL(exynos_drm_subdrv_close);
@@ -0,0 +1,432 @@
/* exynos_drm_crtc.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@samsung.com>
* Seung-Woo Kim <sw0312.kim@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "drmP.h"
#include "drm_crtc_helper.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_encoder.h"
#include "exynos_drm_gem.h"
#define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc,\
drm_crtc)
/*
* Exynos specific crtc structure.
*
* @drm_crtc: crtc object.
* @overlay: contain information common to display controller and hdmi and
* contents of this overlay object would be copied to sub driver size.
* @pipe: a crtc index created at load() with a new crtc object creation
* and the crtc object would be set to private->crtc array
* to get a crtc object corresponding to this pipe from private->crtc
* array when irq interrupt occured. the reason of using this pipe is that
* drm framework doesn't support multiple irq yet.
* we can refer to the crtc to current hardware interrupt occured through
* this pipe value.
* @dpms: store the crtc dpms value
*/
struct exynos_drm_crtc {
struct drm_crtc drm_crtc;
struct exynos_drm_overlay overlay;
unsigned int pipe;
unsigned int dpms;
};
static void exynos_drm_crtc_apply(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct exynos_drm_overlay *overlay = &exynos_crtc->overlay;
exynos_drm_fn_encoder(crtc, overlay,
exynos_drm_encoder_crtc_mode_set);
exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe,
exynos_drm_encoder_crtc_commit);
}
int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay,
struct drm_framebuffer *fb,
struct drm_display_mode *mode,
struct exynos_drm_crtc_pos *pos)
{
struct exynos_drm_gem_buf *buffer;
unsigned int actual_w;
unsigned int actual_h;
int nr = exynos_drm_format_num_buffers(fb->pixel_format);
int i;
for (i = 0; i < nr; i++) {
buffer = exynos_drm_fb_buffer(fb, i);
if (!buffer) {
DRM_LOG_KMS("buffer is null\n");
return -EFAULT;
}
overlay->dma_addr[i] = buffer->dma_addr;
overlay->vaddr[i] = buffer->kvaddr;
DRM_DEBUG_KMS("buffer: %d, vaddr = 0x%lx, dma_addr = 0x%lx\n",
i, (unsigned long)overlay->vaddr[i],
(unsigned long)overlay->dma_addr[i]);
}
actual_w = min((mode->hdisplay - pos->crtc_x), pos->crtc_w);
actual_h = min((mode->vdisplay - pos->crtc_y), pos->crtc_h);
/* set drm framebuffer data. */
overlay->fb_x = pos->fb_x;
overlay->fb_y = pos->fb_y;
overlay->fb_width = fb->width;
overlay->fb_height = fb->height;
overlay->bpp = fb->bits_per_pixel;
overlay->pitch = fb->pitches[0];
overlay->pixel_format = fb->pixel_format;
/* set overlay range to be displayed. */
overlay->crtc_x = pos->crtc_x;
overlay->crtc_y = pos->crtc_y;
overlay->crtc_width = actual_w;
overlay->crtc_height = actual_h;
/* set drm mode data. */
overlay->mode_width = mode->hdisplay;
overlay->mode_height = mode->vdisplay;
overlay->refresh = mode->vrefresh;
overlay->scan_flag = mode->flags;
DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)",
overlay->crtc_x, overlay->crtc_y,
overlay->crtc_width, overlay->crtc_height);
return 0;
}
static int exynos_drm_crtc_update(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc;
struct exynos_drm_overlay *overlay;
struct exynos_drm_crtc_pos pos;
struct drm_display_mode *mode = &crtc->mode;
struct drm_framebuffer *fb = crtc->fb;
if (!mode || !fb)
return -EINVAL;
exynos_crtc = to_exynos_crtc(crtc);
overlay = &exynos_crtc->overlay;
memset(&pos, 0, sizeof(struct exynos_drm_crtc_pos));
/* it means the offset of framebuffer to be displayed. */
pos.fb_x = crtc->x;
pos.fb_y = crtc->y;
/* OSD position to be displayed. */
pos.crtc_x = 0;
pos.crtc_y = 0;
pos.crtc_w = fb->width - crtc->x;
pos.crtc_h = fb->height - crtc->y;
return exynos_drm_overlay_update(overlay, crtc->fb, mode, &pos);
}
static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct drm_device *dev = crtc->dev;
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
if (exynos_crtc->dpms == mode) {
DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
return;
}
mutex_lock(&dev->struct_mutex);
switch (mode) {
case DRM_MODE_DPMS_ON:
exynos_drm_fn_encoder(crtc, &mode,
exynos_drm_encoder_crtc_dpms);
exynos_crtc->dpms = mode;
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
exynos_drm_fn_encoder(crtc, &mode,
exynos_drm_encoder_crtc_dpms);
exynos_crtc->dpms = mode;
break;
default:
DRM_ERROR("unspecified mode %d\n", mode);
break;
}
mutex_unlock(&dev->struct_mutex);
}
static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* drm framework doesn't check NULL. */
}
static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* when set_crtc is requested from user or at booting time,
* crtc->commit would be called without dpms call so if dpms is
* no power on then crtc->dpms should be called
* with DRM_MODE_DPMS_ON for the hardware power to be on.
*/
if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) {
int mode = DRM_MODE_DPMS_ON;
/*
* enable hardware(power on) to all encoders hdmi connected
* to current crtc.
*/
exynos_drm_crtc_dpms(crtc, mode);
/*
* enable dma to all encoders connected to current crtc and
* lcd panel.
*/
exynos_drm_fn_encoder(crtc, &mode,
exynos_drm_encoder_dpms_from_crtc);
}
exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe,
exynos_drm_encoder_crtc_commit);
}
static bool
exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* drm framework doesn't check NULL */
return true;
}
static int
exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode, int x, int y,
struct drm_framebuffer *old_fb)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* copy the mode data adjusted by mode_fixup() into crtc->mode
* so that hardware can be seet to proper mode.
*/
memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
return exynos_drm_crtc_update(crtc);
}
static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb)
{
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
ret = exynos_drm_crtc_update(crtc);
if (ret)
return ret;
exynos_drm_crtc_apply(crtc);
return ret;
}
static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* drm framework doesn't check NULL */
}
static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
.dpms = exynos_drm_crtc_dpms,
.prepare = exynos_drm_crtc_prepare,
.commit = exynos_drm_crtc_commit,
.mode_fixup = exynos_drm_crtc_mode_fixup,
.mode_set = exynos_drm_crtc_mode_set,
.mode_set_base = exynos_drm_crtc_mode_set_base,
.load_lut = exynos_drm_crtc_load_lut,
};
static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event)
{
struct drm_device *dev = crtc->dev;
struct exynos_drm_private *dev_priv = dev->dev_private;
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct drm_framebuffer *old_fb = crtc->fb;
int ret = -EINVAL;
DRM_DEBUG_KMS("%s\n", __FILE__);
mutex_lock(&dev->struct_mutex);
if (event) {
/*
* the pipe from user always is 0 so we can set pipe number
* of current owner to event.
*/
event->pipe = exynos_crtc->pipe;
ret = drm_vblank_get(dev, exynos_crtc->pipe);
if (ret) {
DRM_DEBUG("failed to acquire vblank counter\n");
list_del(&event->base.link);
goto out;
}
list_add_tail(&event->base.link,
&dev_priv->pageflip_event_list);
crtc->fb = fb;
ret = exynos_drm_crtc_update(crtc);
if (ret) {
crtc->fb = old_fb;
drm_vblank_put(dev, exynos_crtc->pipe);
list_del(&event->base.link);
goto out;
}
/*
* the values related to a buffer of the drm framebuffer
* to be applied should be set at here. because these values
* first, are set to shadow registers and then to
* real registers at vsync front porch period.
*/
exynos_drm_crtc_apply(crtc);
}
out:
mutex_unlock(&dev->struct_mutex);
return ret;
}
static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct exynos_drm_private *private = crtc->dev->dev_private;
DRM_DEBUG_KMS("%s\n", __FILE__);
private->crtc[exynos_crtc->pipe] = NULL;
drm_crtc_cleanup(crtc);
kfree(exynos_crtc);
}
static struct drm_crtc_funcs exynos_crtc_funcs = {
.set_config = drm_crtc_helper_set_config,
.page_flip = exynos_drm_crtc_page_flip,
.destroy = exynos_drm_crtc_destroy,
};
struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev,
struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
return &exynos_crtc->overlay;
}
int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
{
struct exynos_drm_crtc *exynos_crtc;
struct exynos_drm_private *private = dev->dev_private;
struct drm_crtc *crtc;
DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL);
if (!exynos_crtc) {
DRM_ERROR("failed to allocate exynos crtc\n");
return -ENOMEM;
}
exynos_crtc->pipe = nr;
exynos_crtc->dpms = DRM_MODE_DPMS_OFF;
exynos_crtc->overlay.zpos = DEFAULT_ZPOS;
crtc = &exynos_crtc->drm_crtc;
private->crtc[nr] = crtc;
drm_crtc_init(dev, crtc, &exynos_crtc_funcs);
drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);
return 0;
}
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc)
{
struct exynos_drm_private *private = dev->dev_private;
struct exynos_drm_crtc *exynos_crtc =
to_exynos_crtc(private->crtc[crtc]);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
return -EPERM;
exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
exynos_drm_enable_vblank);
return 0;
}
void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
{
struct exynos_drm_private *private = dev->dev_private;
struct exynos_drm_crtc *exynos_crtc =
to_exynos_crtc(private->crtc[crtc]);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
return;
exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
exynos_drm_disable_vblank);
}
@@ -0,0 +1,63 @@
/* exynos_drm_crtc.h
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@samsung.com>
* Seung-Woo Kim <sw0312.kim@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _EXYNOS_DRM_CRTC_H_
#define _EXYNOS_DRM_CRTC_H_
struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev,
struct drm_crtc *crtc);
int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr);
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc);
void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc);
/*
* Exynos specific crtc postion structure.
*
* @fb_x: offset x on a framebuffer to be displyed
* - the unit is screen coordinates.
* @fb_y: offset y on a framebuffer to be displayed
* - the unit is screen coordinates.
* @crtc_x: offset x on hardware screen.
* @crtc_y: offset y on hardware screen.
* @crtc_w: width of hardware screen.
* @crtc_h: height of hardware screen.
*/
struct exynos_drm_crtc_pos {
unsigned int fb_x;
unsigned int fb_y;
unsigned int crtc_x;
unsigned int crtc_y;
unsigned int crtc_w;
unsigned int crtc_h;
};
int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay,
struct drm_framebuffer *fb,
struct drm_display_mode *mode,
struct exynos_drm_crtc_pos *pos);
#endif
@@ -0,0 +1,366 @@
/*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@samsung.com>
* Seung-Woo Kim <sw0312.kim@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "drmP.h"
#include "drm.h"
#include "drm_crtc_helper.h"
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_encoder.h"
#include "exynos_drm_fbdev.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_gem.h"
#include "exynos_drm_plane.h"
#include "exynos_drm_vidi.h"
#define DRIVER_NAME "exynos"
#define DRIVER_DESC "Samsung SoC DRM"
#define DRIVER_DATE "20110530"
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
#define VBLANK_OFF_DELAY 50000
static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
{
struct exynos_drm_private *private;
int ret;
int nr;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
private = kzalloc(sizeof(struct exynos_drm_private), GFP_KERNEL);
if (!private) {
DRM_ERROR("failed to allocate private\n");
return -ENOMEM;
}
INIT_LIST_HEAD(&private->pageflip_event_list);
dev->dev_private = (void *)private;
drm_mode_config_init(dev);
/* init kms poll for handling hpd */
drm_kms_helper_poll_init(dev);
exynos_drm_mode_config_init(dev);
/*
* EXYNOS4 is enough to have two CRTCs and each crtc would be used
* without dependency of hardware.
*/
for (nr = 0; nr < MAX_CRTC; nr++) {
ret = exynos_drm_crtc_create(dev, nr);
if (ret)
goto err_crtc;
}
for (nr = 0; nr < MAX_PLANE; nr++) {
ret = exynos_plane_init(dev, nr);
if (ret)
goto err_crtc;
}
ret = drm_vblank_init(dev, MAX_CRTC);
if (ret)
goto err_crtc;
/*
* probe sub drivers such as display controller and hdmi driver,
* that were registered at probe() of platform driver
* to the sub driver and create encoder and connector for them.
*/
ret = exynos_drm_device_register(dev);
if (ret)
goto err_vblank;
/* setup possible_clones. */
exynos_drm_encoder_setup(dev);
/*
* create and configure fb helper and also exynos specific
* fbdev object.
*/
ret = exynos_drm_fbdev_init(dev);
if (ret) {
DRM_ERROR("failed to initialize drm fbdev\n");
goto err_drm_device;
}
drm_vblank_offdelay = VBLANK_OFF_DELAY;
return 0;
err_drm_device:
exynos_drm_device_unregister(dev);
err_vblank:
drm_vblank_cleanup(dev);
err_crtc:
drm_mode_config_cleanup(dev);
kfree(private);
return ret;
}
static int exynos_drm_unload(struct drm_device *dev)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
exynos_drm_fbdev_fini(dev);
exynos_drm_device_unregister(dev);
drm_vblank_cleanup(dev);
drm_kms_helper_poll_fini(dev);
drm_mode_config_cleanup(dev);
kfree(dev->dev_private);
dev->dev_private = NULL;
return 0;
}
static int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
return exynos_drm_subdrv_open(dev, file);
}
static void exynos_drm_preclose(struct drm_device *dev,
struct drm_file *file)
{
struct exynos_drm_private *private = dev->dev_private;
struct drm_pending_vblank_event *e, *t;
unsigned long flags;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
/* release events of current file */
spin_lock_irqsave(&dev->event_lock, flags);
list_for_each_entry_safe(e, t, &private->pageflip_event_list,
base.link) {
if (e->base.file_priv == file) {
list_del(&e->base.link);
e->base.destroy(&e->base);
}
}
spin_unlock_irqrestore(&dev->event_lock, flags);
exynos_drm_subdrv_close(dev, file);
}
static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (!file->driver_priv)
return;
kfree(file->driver_priv);
file->driver_priv = NULL;
}
static void exynos_drm_lastclose(struct drm_device *dev)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
exynos_drm_fbdev_restore_mode(dev);
}
static struct vm_operations_struct exynos_drm_gem_vm_ops = {
.fault = exynos_drm_gem_fault,
.open = drm_gem_vm_open,
.close = drm_gem_vm_close,
};
static struct drm_ioctl_desc exynos_ioctls[] = {
DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CREATE, exynos_drm_gem_create_ioctl,
DRM_UNLOCKED | DRM_AUTH),
DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MAP_OFFSET,
exynos_drm_gem_map_offset_ioctl, DRM_UNLOCKED |
DRM_AUTH),
DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MMAP,
exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH),
DRM_IOCTL_DEF_DRV(EXYNOS_PLANE_SET_ZPOS, exynos_plane_set_zpos_ioctl,
DRM_UNLOCKED | DRM_AUTH),
DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION,
vidi_connection_ioctl, DRM_UNLOCKED | DRM_AUTH),
};
static const struct file_operations exynos_drm_driver_fops = {
.owner = THIS_MODULE,
.open = drm_open,
.mmap = exynos_drm_gem_mmap,
.poll = drm_poll,
.read = drm_read,
.unlocked_ioctl = drm_ioctl,
.release = drm_release,
};
static struct drm_driver exynos_drm_driver = {
.driver_features = DRIVER_HAVE_IRQ | DRIVER_BUS_PLATFORM |
DRIVER_MODESET | DRIVER_GEM,
.load = exynos_drm_load,
.unload = exynos_drm_unload,
.open = exynos_drm_open,
.preclose = exynos_drm_preclose,
.lastclose = exynos_drm_lastclose,
.postclose = exynos_drm_postclose,
.get_vblank_counter = drm_vblank_count,
.enable_vblank = exynos_drm_crtc_enable_vblank,
.disable_vblank = exynos_drm_crtc_disable_vblank,
.gem_init_object = exynos_drm_gem_init_object,
.gem_free_object = exynos_drm_gem_free_object,
.gem_vm_ops = &exynos_drm_gem_vm_ops,
.dumb_create = exynos_drm_gem_dumb_create,
.dumb_map_offset = exynos_drm_gem_dumb_map_offset,
.dumb_destroy = exynos_drm_gem_dumb_destroy,
.ioctls = exynos_ioctls,
.fops = &exynos_drm_driver_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
};
static int exynos_drm_platform_probe(struct platform_device *pdev)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
exynos_drm_driver.num_ioctls = DRM_ARRAY_SIZE(exynos_ioctls);
return drm_platform_init(&exynos_drm_driver, pdev);
}
static int exynos_drm_platform_remove(struct platform_device *pdev)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
drm_platform_exit(&exynos_drm_driver, pdev);
return 0;
}
static struct platform_driver exynos_drm_platform_driver = {
.probe = exynos_drm_platform_probe,
.remove = __devexit_p(exynos_drm_platform_remove),
.driver = {
.owner = THIS_MODULE,
.name = "exynos-drm",
},
};
static int __init exynos_drm_init(void)
{
int ret;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
#ifdef CONFIG_DRM_EXYNOS_FIMD
ret = platform_driver_register(&fimd_driver);
if (ret < 0)
goto out_fimd;
#endif
#ifdef CONFIG_DRM_EXYNOS_HDMI
ret = platform_driver_register(&hdmi_driver);
if (ret < 0)
goto out_hdmi;
ret = platform_driver_register(&mixer_driver);
if (ret < 0)
goto out_mixer;
ret = platform_driver_register(&exynos_drm_common_hdmi_driver);
if (ret < 0)
goto out_common_hdmi;
#endif
#ifdef CONFIG_DRM_EXYNOS_VIDI
ret = platform_driver_register(&vidi_driver);
if (ret < 0)
goto out_vidi;
#endif
ret = platform_driver_register(&exynos_drm_platform_driver);
if (ret < 0)
goto out;
return 0;
out:
#ifdef CONFIG_DRM_EXYNOS_VIDI
out_vidi:
platform_driver_unregister(&vidi_driver);
#endif
#ifdef CONFIG_DRM_EXYNOS_HDMI
platform_driver_unregister(&exynos_drm_common_hdmi_driver);
out_common_hdmi:
platform_driver_unregister(&mixer_driver);
out_mixer:
platform_driver_unregister(&hdmi_driver);
out_hdmi:
#endif
#ifdef CONFIG_DRM_EXYNOS_FIMD
platform_driver_unregister(&fimd_driver);
out_fimd:
#endif
return ret;
}
static void __exit exynos_drm_exit(void)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
platform_driver_unregister(&exynos_drm_platform_driver);
#ifdef CONFIG_DRM_EXYNOS_HDMI
platform_driver_unregister(&exynos_drm_common_hdmi_driver);
platform_driver_unregister(&mixer_driver);
platform_driver_unregister(&hdmi_driver);
#endif
#ifdef CONFIG_DRM_EXYNOS_VIDI
platform_driver_unregister(&vidi_driver);
#endif
#ifdef CONFIG_DRM_EXYNOS_FIMD
platform_driver_unregister(&fimd_driver);
#endif
}
module_init(exynos_drm_init);
module_exit(exynos_drm_exit);
MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
MODULE_DESCRIPTION("Samsung SoC DRM Driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,290 @@
/* exynos_drm_drv.h
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@samsung.com>
* Seung-Woo Kim <sw0312.kim@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _EXYNOS_DRM_DRV_H_
#define _EXYNOS_DRM_DRV_H_
#include <linux/module.h>
#include "drm.h"
#define MAX_CRTC 3
#define MAX_PLANE 5
#define MAX_FB_BUFFER 4
#define DEFAULT_ZPOS -1
struct drm_device;
struct exynos_drm_overlay;
struct drm_connector;
extern unsigned int drm_vblank_offdelay;
/* this enumerates display type. */
enum exynos_drm_output_type {
EXYNOS_DISPLAY_TYPE_NONE,
/* RGB or CPU Interface. */
EXYNOS_DISPLAY_TYPE_LCD,
/* HDMI Interface. */
EXYNOS_DISPLAY_TYPE_HDMI,
/* Virtual Display Interface. */
EXYNOS_DISPLAY_TYPE_VIDI,
};
/*
* Exynos drm overlay ops structure.
*
* @mode_set: copy drm overlay info to hw specific overlay info.
* @commit: apply hardware specific overlay data to registers.
* @disable: disable hardware specific overlay.
*/
struct exynos_drm_overlay_ops {
void (*mode_set)(struct device *subdrv_dev,
struct exynos_drm_overlay *overlay);
void (*commit)(struct device *subdrv_dev, int zpos);
void (*disable)(struct device *subdrv_dev, int zpos);
};
/*
* Exynos drm common overlay structure.
*
* @fb_x: offset x on a framebuffer to be displayed.
* - the unit is screen coordinates.
* @fb_y: offset y on a framebuffer to be displayed.
* - the unit is screen coordinates.
* @fb_width: width of a framebuffer.
* @fb_height: height of a framebuffer.
* @crtc_x: offset x on hardware screen.
* @crtc_y: offset y on hardware screen.
* @crtc_width: window width to be displayed (hardware screen).
* @crtc_height: window height to be displayed (hardware screen).
* @mode_width: width of screen mode.
* @mode_height: height of screen mode.
* @refresh: refresh rate.
* @scan_flag: interlace or progressive way.
* (it could be DRM_MODE_FLAG_*)
* @bpp: pixel size.(in bit)
* @pixel_format: fourcc pixel format of this overlay
* @dma_addr: array of bus(accessed by dma) address to the memory region
* allocated for a overlay.
* @vaddr: array of virtual memory addresss to this overlay.
* @zpos: order of overlay layer(z position).
* @default_win: a window to be enabled.
* @color_key: color key on or off.
* @index_color: if using color key feature then this value would be used
* as index color.
* @local_path: in case of lcd type, local path mode on or off.
* @transparency: transparency on or off.
* @activated: activated or not.
*
* this structure is common to exynos SoC and its contents would be copied
* to hardware specific overlay info.
*/
struct exynos_drm_overlay {
unsigned int fb_x;
unsigned int fb_y;
unsigned int fb_width;
unsigned int fb_height;
unsigned int crtc_x;
unsigned int crtc_y;
unsigned int crtc_width;
unsigned int crtc_height;
unsigned int mode_width;
unsigned int mode_height;
unsigned int refresh;
unsigned int scan_flag;
unsigned int bpp;
unsigned int pitch;
uint32_t pixel_format;
dma_addr_t dma_addr[MAX_FB_BUFFER];
void __iomem *vaddr[MAX_FB_BUFFER];
int zpos;
bool default_win;
bool color_key;
unsigned int index_color;
bool local_path;
bool transparency;
bool activated;
};
/*
* Exynos DRM Display Structure.
* - this structure is common to analog tv, digital tv and lcd panel.
*
* @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
* @is_connected: check for that display is connected or not.
* @get_edid: get edid modes from display driver.
* @get_panel: get panel object from display driver.
* @check_timing: check if timing is valid or not.
* @power_on: display device on or off.
*/
struct exynos_drm_display_ops {
enum exynos_drm_output_type type;
bool (*is_connected)(struct device *dev);
int (*get_edid)(struct device *dev, struct drm_connector *connector,
u8 *edid, int len);
void *(*get_panel)(struct device *dev);
int (*check_timing)(struct device *dev, void *timing);
int (*power_on)(struct device *dev, int mode);
};
/*
* Exynos drm manager ops
*
* @dpms: control device power.
* @apply: set timing, vblank and overlay data to registers.
* @mode_fixup: fix mode data comparing to hw specific display mode.
* @mode_set: convert drm_display_mode to hw specific display mode and
* would be called by encoder->mode_set().
* @get_max_resol: get maximum resolution to specific hardware.
* @commit: set current hw specific display mode to hw.
* @enable_vblank: specific driver callback for enabling vblank interrupt.
* @disable_vblank: specific driver callback for disabling vblank interrupt.
*/
struct exynos_drm_manager_ops {
void (*dpms)(struct device *subdrv_dev, int mode);
void (*apply)(struct device *subdrv_dev);
void (*mode_fixup)(struct device *subdrv_dev,
struct drm_connector *connector,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
void (*mode_set)(struct device *subdrv_dev, void *mode);
void (*get_max_resol)(struct device *subdrv_dev, unsigned int *width,
unsigned int *height);
void (*commit)(struct device *subdrv_dev);
int (*enable_vblank)(struct device *subdrv_dev);
void (*disable_vblank)(struct device *subdrv_dev);
};
/*
* Exynos drm common manager structure.
*
* @dev: pointer to device object for subdrv device driver.
* sub drivers such as display controller or hdmi driver,
* have their own device object.
* @ops: pointer to callbacks for exynos drm specific framebuffer.
* these callbacks should be set by specific drivers such fimd
* or hdmi driver and are used to control hardware global registers.
* @overlay_ops: pointer to callbacks for exynos drm specific framebuffer.
* these callbacks should be set by specific drivers such fimd
* or hdmi driver and are used to control hardware overlay reigsters.
* @display: pointer to callbacks for exynos drm specific framebuffer.
* these callbacks should be set by specific drivers such fimd
* or hdmi driver and are used to control display devices such as
* analog tv, digital tv and lcd panel and also get timing data for them.
*/
struct exynos_drm_manager {
struct device *dev;
int pipe;
struct exynos_drm_manager_ops *ops;
struct exynos_drm_overlay_ops *overlay_ops;
struct exynos_drm_display_ops *display_ops;
};
/*
* Exynos drm private structure.
*/
struct exynos_drm_private {
struct drm_fb_helper *fb_helper;
/* list head for new event to be added. */
struct list_head pageflip_event_list;
/*
* created crtc object would be contained at this array and
* this array is used to be aware of which crtc did it request vblank.
*/
struct drm_crtc *crtc[MAX_CRTC];
};
/*
* Exynos drm sub driver structure.
*
* @list: sub driver has its own list object to register to exynos drm driver.
* @dev: pointer to device object for subdrv device driver.
* @drm_dev: pointer to drm_device and this pointer would be set
* when sub driver calls exynos_drm_subdrv_register().
* @manager: subdrv has its own manager to control a hardware appropriately
* and we can access a hardware drawing on this manager.
* @probe: this callback would be called by exynos drm driver after
* subdrv is registered to it.
* @remove: this callback is used to release resources created
* by probe callback.
* @open: this would be called with drm device file open.
* @close: this would be called with drm device file close.
* @encoder: encoder object owned by this sub driver.
* @connector: connector object owned by this sub driver.
*/
struct exynos_drm_subdrv {
struct list_head list;
struct device *dev;
struct drm_device *drm_dev;
struct exynos_drm_manager *manager;
int (*probe)(struct drm_device *drm_dev, struct device *dev);
void (*remove)(struct drm_device *dev);
int (*open)(struct drm_device *drm_dev, struct device *dev,
struct drm_file *file);
void (*close)(struct drm_device *drm_dev, struct device *dev,
struct drm_file *file);
struct drm_encoder *encoder;
struct drm_connector *connector;
};
/*
* this function calls a probe callback registered to sub driver list and
* create its own encoder and connector and then set drm_device object
* to global one.
*/
int exynos_drm_device_register(struct drm_device *dev);
/*
* this function calls a remove callback registered to sub driver list and
* destroy its own encoder and connetor.
*/
int exynos_drm_device_unregister(struct drm_device *dev);
/*
* this function would be called by sub drivers such as display controller
* or hdmi driver to register this sub driver object to exynos drm driver
* and when a sub driver is registered to exynos drm driver a probe callback
* of the sub driver is called and creates its own encoder and connector.
*/
int exynos_drm_subdrv_register(struct exynos_drm_subdrv *drm_subdrv);
/* this function removes subdrv list from exynos drm driver */
int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *drm_subdrv);
int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file);
void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
extern struct platform_driver fimd_driver;
extern struct platform_driver hdmi_driver;
extern struct platform_driver mixer_driver;
extern struct platform_driver exynos_drm_common_hdmi_driver;
extern struct platform_driver vidi_driver;
#endif
@@ -0,0 +1,445 @@
/* exynos_drm_encoder.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@samsung.com>
* Seung-Woo Kim <sw0312.kim@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "drmP.h"
#include "drm_crtc_helper.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_encoder.h"
#define to_exynos_encoder(x) container_of(x, struct exynos_drm_encoder,\
drm_encoder)
/*
* exynos specific encoder structure.
*
* @drm_encoder: encoder object.
* @manager: specific encoder has its own manager to control a hardware
* appropriately and we can access a hardware drawing on this manager.
* @dpms: store the encoder dpms value.
*/
struct exynos_drm_encoder {
struct drm_encoder drm_encoder;
struct exynos_drm_manager *manager;
int dpms;
};
static void exynos_drm_display_power(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct drm_connector *connector;
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->encoder == encoder) {
struct exynos_drm_display_ops *display_ops =
manager->display_ops;
DRM_DEBUG_KMS("connector[%d] dpms[%d]\n",
connector->base.id, mode);
if (display_ops && display_ops->power_on)
display_ops->power_on(manager->dev, mode);
}
}
}
static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
struct exynos_drm_manager_ops *manager_ops = manager->ops;
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
DRM_DEBUG_KMS("%s, encoder dpms: %d\n", __FILE__, mode);
if (exynos_encoder->dpms == mode) {
DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
return;
}
mutex_lock(&dev->struct_mutex);
switch (mode) {
case DRM_MODE_DPMS_ON:
if (manager_ops && manager_ops->apply)
manager_ops->apply(manager->dev);
exynos_drm_display_power(encoder, mode);
exynos_encoder->dpms = mode;
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
exynos_drm_display_power(encoder, mode);
exynos_encoder->dpms = mode;
break;
default:
DRM_ERROR("unspecified mode %d\n", mode);
break;
}
mutex_unlock(&dev->struct_mutex);
}
static bool
exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
struct drm_connector *connector;
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
struct exynos_drm_manager_ops *manager_ops = manager->ops;
DRM_DEBUG_KMS("%s\n", __FILE__);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->encoder == encoder)
if (manager_ops && manager_ops->mode_fixup)
manager_ops->mode_fixup(manager->dev, connector,
mode, adjusted_mode);
}
return true;
}
static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
struct drm_connector *connector;
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
struct exynos_drm_manager_ops *manager_ops = manager->ops;
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
struct exynos_drm_overlay *overlay = get_exynos_drm_overlay(dev,
encoder->crtc);
DRM_DEBUG_KMS("%s\n", __FILE__);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->encoder == encoder) {
if (manager_ops && manager_ops->mode_set)
manager_ops->mode_set(manager->dev,
adjusted_mode);
if (overlay_ops && overlay_ops->mode_set)
overlay_ops->mode_set(manager->dev, overlay);
}
}
}
static void exynos_drm_encoder_prepare(struct drm_encoder *encoder)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* drm framework doesn't check NULL. */
}
static void exynos_drm_encoder_commit(struct drm_encoder *encoder)
{
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
struct exynos_drm_manager_ops *manager_ops = manager->ops;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (manager_ops && manager_ops->commit)
manager_ops->commit(manager->dev);
}
static struct drm_crtc *
exynos_drm_encoder_get_crtc(struct drm_encoder *encoder)
{
return encoder->crtc;
}
static struct drm_encoder_helper_funcs exynos_encoder_helper_funcs = {
.dpms = exynos_drm_encoder_dpms,
.mode_fixup = exynos_drm_encoder_mode_fixup,
.mode_set = exynos_drm_encoder_mode_set,
.prepare = exynos_drm_encoder_prepare,
.commit = exynos_drm_encoder_commit,
.get_crtc = exynos_drm_encoder_get_crtc,
};
static void exynos_drm_encoder_destroy(struct drm_encoder *encoder)
{
struct exynos_drm_encoder *exynos_encoder =
to_exynos_encoder(encoder);
DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_encoder->manager->pipe = -1;
drm_encoder_cleanup(encoder);
kfree(exynos_encoder);
}
static struct drm_encoder_funcs exynos_encoder_funcs = {
.destroy = exynos_drm_encoder_destroy,
};
static unsigned int exynos_drm_encoder_clones(struct drm_encoder *encoder)
{
struct drm_encoder *clone;
struct drm_device *dev = encoder->dev;
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
struct exynos_drm_display_ops *display_ops =
exynos_encoder->manager->display_ops;
unsigned int clone_mask = 0;
int cnt = 0;
list_for_each_entry(clone, &dev->mode_config.encoder_list, head) {
switch (display_ops->type) {
case EXYNOS_DISPLAY_TYPE_LCD:
case EXYNOS_DISPLAY_TYPE_HDMI:
case EXYNOS_DISPLAY_TYPE_VIDI:
clone_mask |= (1 << (cnt++));
break;
default:
continue;
}
}
return clone_mask;
}
void exynos_drm_encoder_setup(struct drm_device *dev)
{
struct drm_encoder *encoder;
DRM_DEBUG_KMS("%s\n", __FILE__);
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
encoder->possible_clones = exynos_drm_encoder_clones(encoder);
}
struct drm_encoder *
exynos_drm_encoder_create(struct drm_device *dev,
struct exynos_drm_manager *manager,
unsigned int possible_crtcs)
{
struct drm_encoder *encoder;
struct exynos_drm_encoder *exynos_encoder;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!manager || !possible_crtcs)
return NULL;
if (!manager->dev)
return NULL;
exynos_encoder = kzalloc(sizeof(*exynos_encoder), GFP_KERNEL);
if (!exynos_encoder) {
DRM_ERROR("failed to allocate encoder\n");
return NULL;
}
exynos_encoder->dpms = DRM_MODE_DPMS_OFF;
exynos_encoder->manager = manager;
encoder = &exynos_encoder->drm_encoder;
encoder->possible_crtcs = possible_crtcs;
DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
drm_encoder_init(dev, encoder, &exynos_encoder_funcs,
DRM_MODE_ENCODER_TMDS);
drm_encoder_helper_add(encoder, &exynos_encoder_helper_funcs);
DRM_DEBUG_KMS("encoder has been created\n");
return encoder;
}
struct exynos_drm_manager *exynos_drm_get_manager(struct drm_encoder *encoder)
{
return to_exynos_encoder(encoder)->manager;
}
void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data,
void (*fn)(struct drm_encoder *, void *))
{
struct drm_device *dev = crtc->dev;
struct drm_encoder *encoder;
struct exynos_drm_private *private = dev->dev_private;
struct exynos_drm_manager *manager;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
/*
* if crtc is detached from encoder, check pipe,
* otherwise check crtc attached to encoder
*/
if (!encoder->crtc) {
manager = to_exynos_encoder(encoder)->manager;
if (manager->pipe < 0 ||
private->crtc[manager->pipe] != crtc)
continue;
} else {
if (encoder->crtc != crtc)
continue;
}
fn(encoder, data);
}
}
void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data)
{
struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager;
struct exynos_drm_manager_ops *manager_ops = manager->ops;
int crtc = *(int *)data;
if (manager->pipe == -1)
manager->pipe = crtc;
if (manager_ops->enable_vblank)
manager_ops->enable_vblank(manager->dev);
}
void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data)
{
struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager;
struct exynos_drm_manager_ops *manager_ops = manager->ops;
int crtc = *(int *)data;
if (manager->pipe == -1)
manager->pipe = crtc;
if (manager_ops->disable_vblank)
manager_ops->disable_vblank(manager->dev);
}
void exynos_drm_encoder_crtc_plane_commit(struct drm_encoder *encoder,
void *data)
{
struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager;
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
int zpos = DEFAULT_ZPOS;
if (data)
zpos = *(int *)data;
if (overlay_ops && overlay_ops->commit)
overlay_ops->commit(manager->dev, zpos);
}
void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data)
{
struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager;
int crtc = *(int *)data;
int zpos = DEFAULT_ZPOS;
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* when crtc is detached from encoder, this pipe is used
* to select manager operation
*/
manager->pipe = crtc;
exynos_drm_encoder_crtc_plane_commit(encoder, &zpos);
}
void exynos_drm_encoder_dpms_from_crtc(struct drm_encoder *encoder, void *data)
{
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
int mode = *(int *)data;
DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_drm_encoder_dpms(encoder, mode);
exynos_encoder->dpms = mode;
}
void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data)
{
struct drm_device *dev = encoder->dev;
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
struct exynos_drm_manager *manager = exynos_encoder->manager;
struct exynos_drm_manager_ops *manager_ops = manager->ops;
struct drm_connector *connector;
int mode = *(int *)data;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (manager_ops && manager_ops->dpms)
manager_ops->dpms(manager->dev, mode);
/*
* set current dpms mode to the connector connected to
* current encoder. connector->dpms would be checked
* at drm_helper_connector_dpms()
*/
list_for_each_entry(connector, &dev->mode_config.connector_list, head)
if (connector->encoder == encoder)
connector->dpms = mode;
/*
* if this condition is ok then it means that the crtc is already
* detached from encoder and last function for detaching is properly
* done, so clear pipe from manager to prevent repeated call.
*/
if (mode > DRM_MODE_DPMS_ON) {
if (!encoder->crtc)
manager->pipe = -1;
}
}
void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data)
{
struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager;
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
struct exynos_drm_overlay *overlay = data;
if (overlay_ops && overlay_ops->mode_set)
overlay_ops->mode_set(manager->dev, overlay);
}
void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data)
{
struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager;
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
int zpos = DEFAULT_ZPOS;
DRM_DEBUG_KMS("\n");
if (data)
zpos = *(int *)data;
if (overlay_ops && overlay_ops->disable)
overlay_ops->disable(manager->dev, zpos);
}
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@samsung.com>
* Seung-Woo Kim <sw0312.kim@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _EXYNOS_DRM_ENCODER_H_
#define _EXYNOS_DRM_ENCODER_H_
struct exynos_drm_manager;
void exynos_drm_encoder_setup(struct drm_device *dev);
struct drm_encoder *exynos_drm_encoder_create(struct drm_device *dev,
struct exynos_drm_manager *mgr,
unsigned int possible_crtcs);
struct exynos_drm_manager *
exynos_drm_get_manager(struct drm_encoder *encoder);
void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data,
void (*fn)(struct drm_encoder *, void *));
void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data);
void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_crtc_plane_commit(struct drm_encoder *encoder,
void *data);
void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_dpms_from_crtc(struct drm_encoder *encoder,
void *data);
void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data);
#endif
@@ -0,0 +1,213 @@
/* exynos_drm_fb.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@samsung.com>
* Seung-Woo Kim <sw0312.kim@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "drmP.h"
#include "drm_crtc.h"
#include "drm_crtc_helper.h"
#include "drm_fb_helper.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_gem.h"
#define to_exynos_fb(x) container_of(x, struct exynos_drm_fb, fb)
/*
* exynos specific framebuffer structure.
*
* @fb: drm framebuffer obejct.
* @exynos_gem_obj: array of exynos specific gem object containing a gem object.
*/
struct exynos_drm_fb {
struct drm_framebuffer fb;
struct exynos_drm_gem_obj *exynos_gem_obj[MAX_FB_BUFFER];
};
static void exynos_drm_fb_destroy(struct drm_framebuffer *fb)
{
struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
DRM_DEBUG_KMS("%s\n", __FILE__);
drm_framebuffer_cleanup(fb);
kfree(exynos_fb);
exynos_fb = NULL;
}
static int exynos_drm_fb_create_handle(struct drm_framebuffer *fb,
struct drm_file *file_priv,
unsigned int *handle)
{
struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
DRM_DEBUG_KMS("%s\n", __FILE__);
return drm_gem_handle_create(file_priv,
&exynos_fb->exynos_gem_obj[0]->base, handle);
}
static int exynos_drm_fb_dirty(struct drm_framebuffer *fb,
struct drm_file *file_priv, unsigned flags,
unsigned color, struct drm_clip_rect *clips,
unsigned num_clips)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* TODO */
return 0;
}
static struct drm_framebuffer_funcs exynos_drm_fb_funcs = {
.destroy = exynos_drm_fb_destroy,
.create_handle = exynos_drm_fb_create_handle,
.dirty = exynos_drm_fb_dirty,
};
struct drm_framebuffer *
exynos_drm_framebuffer_init(struct drm_device *dev,
struct drm_mode_fb_cmd2 *mode_cmd,
struct drm_gem_object *obj)
{
struct exynos_drm_fb *exynos_fb;
int ret;
exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL);
if (!exynos_fb) {
DRM_ERROR("failed to allocate exynos drm framebuffer\n");
return ERR_PTR(-ENOMEM);
}
ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs);
if (ret) {
DRM_ERROR("failed to initialize framebuffer\n");
return ERR_PTR(ret);
}
drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd);
exynos_fb->exynos_gem_obj[0] = to_exynos_gem_obj(obj);
return &exynos_fb->fb;
}
static struct drm_framebuffer *
exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
struct drm_mode_fb_cmd2 *mode_cmd)
{
struct drm_gem_object *obj;
struct drm_framebuffer *fb;
struct exynos_drm_fb *exynos_fb;
int nr;
int i;
DRM_DEBUG_KMS("%s\n", __FILE__);
obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
if (!obj) {
DRM_ERROR("failed to lookup gem object\n");
return ERR_PTR(-ENOENT);
}
drm_gem_object_unreference_unlocked(obj);
fb = exynos_drm_framebuffer_init(dev, mode_cmd, obj);
if (IS_ERR(fb))
return fb;
exynos_fb = to_exynos_fb(fb);
nr = exynos_drm_format_num_buffers(fb->pixel_format);
for (i = 1; i < nr; i++) {
obj = drm_gem_object_lookup(dev, file_priv,
mode_cmd->handles[i]);
if (!obj) {
DRM_ERROR("failed to lookup gem object\n");
exynos_drm_fb_destroy(fb);
return ERR_PTR(-ENOENT);
}
drm_gem_object_unreference_unlocked(obj);
exynos_fb->exynos_gem_obj[i] = to_exynos_gem_obj(obj);
}
return fb;
}
struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb,
int index)
{
struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
struct exynos_drm_gem_buf *buffer;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (index >= MAX_FB_BUFFER)
return NULL;
buffer = exynos_fb->exynos_gem_obj[index]->buffer;
if (!buffer)
return NULL;
DRM_DEBUG_KMS("vaddr = 0x%lx, dma_addr = 0x%lx\n",
(unsigned long)buffer->kvaddr,
(unsigned long)buffer->dma_addr);
return buffer;
}
static void exynos_drm_output_poll_changed(struct drm_device *dev)
{
struct exynos_drm_private *private = dev->dev_private;
struct drm_fb_helper *fb_helper = private->fb_helper;
if (fb_helper)
drm_fb_helper_hotplug_event(fb_helper);
}
static struct drm_mode_config_funcs exynos_drm_mode_config_funcs = {
.fb_create = exynos_user_fb_create,
.output_poll_changed = exynos_drm_output_poll_changed,
};
void exynos_drm_mode_config_init(struct drm_device *dev)
{
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
/*
* set max width and height as default value(4096x4096).
* this value would be used to check framebuffer size limitation
* at drm_mode_addfb().
*/
dev->mode_config.max_width = 4096;
dev->mode_config.max_height = 4096;
dev->mode_config.funcs = &exynos_drm_mode_config_funcs;
}
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@samsung.com>
* Seung-Woo Kim <sw0312.kim@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _EXYNOS_DRM_FB_H_
#define _EXYNOS_DRM_FB_H
static inline int exynos_drm_format_num_buffers(uint32_t format)
{
switch (format) {
case DRM_FORMAT_NV12M:
case DRM_FORMAT_NV12MT:
return 2;
case DRM_FORMAT_YUV420M:
return 3;
default:
return 1;
}
}
struct drm_framebuffer *
exynos_drm_framebuffer_init(struct drm_device *dev,
struct drm_mode_fb_cmd2 *mode_cmd,
struct drm_gem_object *obj);
/* get memory information of a drm framebuffer */
struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb,
int index);
void exynos_drm_mode_config_init(struct drm_device *dev);
#endif
@@ -0,0 +1,318 @@
/* exynos_drm_fbdev.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@samsung.com>
* Seung-Woo Kim <sw0312.kim@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "drmP.h"
#include "drm_crtc.h"
#include "drm_fb_helper.h"
#include "drm_crtc_helper.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_gem.h"
#define MAX_CONNECTOR 4
#define PREFERRED_BPP 32
#define to_exynos_fbdev(x) container_of(x, struct exynos_drm_fbdev,\
drm_fb_helper)
struct exynos_drm_fbdev {
struct drm_fb_helper drm_fb_helper;
struct exynos_drm_gem_obj *exynos_gem_obj;
};
static struct fb_ops exynos_drm_fb_ops = {
.owner = THIS_MODULE,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_check_var = drm_fb_helper_check_var,
.fb_set_par = drm_fb_helper_set_par,
.fb_blank = drm_fb_helper_blank,
.fb_pan_display = drm_fb_helper_pan_display,
.fb_setcmap = drm_fb_helper_setcmap,
};
static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
struct drm_framebuffer *fb)
{
struct fb_info *fbi = helper->fbdev;
struct drm_device *dev = helper->dev;
struct exynos_drm_gem_buf *buffer;
unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3);
unsigned long offset;
DRM_DEBUG_KMS("%s\n", __FILE__);
drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
/* RGB formats use only one buffer */
buffer = exynos_drm_fb_buffer(fb, 0);
if (!buffer) {
DRM_LOG_KMS("buffer is null.\n");
return -EFAULT;
}
offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
offset += fbi->var.yoffset * fb->pitches[0];
dev->mode_config.fb_base = (resource_size_t)buffer->dma_addr;
fbi->screen_base = buffer->kvaddr + offset;
fbi->fix.smem_start = (unsigned long)(buffer->dma_addr + offset);
fbi->screen_size = size;
fbi->fix.smem_len = size;
return 0;
}
static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
struct exynos_drm_gem_obj *exynos_gem_obj;
struct drm_device *dev = helper->dev;
struct fb_info *fbi;
struct drm_mode_fb_cmd2 mode_cmd = { 0 };
struct platform_device *pdev = dev->platformdev;
unsigned long size;
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n",
sizes->surface_width, sizes->surface_height,
sizes->surface_bpp);
mode_cmd.width = sizes->surface_width;
mode_cmd.height = sizes->surface_height;
mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3);
mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
sizes->surface_depth);
mutex_lock(&dev->struct_mutex);
fbi = framebuffer_alloc(0, &pdev->dev);
if (!fbi) {
DRM_ERROR("failed to allocate fb info.\n");
ret = -ENOMEM;
goto out;
}
size = mode_cmd.pitches[0] * mode_cmd.height;
/* 0 means to allocate physically continuous memory */
exynos_gem_obj = exynos_drm_gem_create(dev, 0, size);
if (IS_ERR(exynos_gem_obj)) {
ret = PTR_ERR(exynos_gem_obj);
goto out;
}
exynos_fbdev->exynos_gem_obj = exynos_gem_obj;
helper->fb = exynos_drm_framebuffer_init(dev, &mode_cmd,
&exynos_gem_obj->base);
if (IS_ERR_OR_NULL(helper->fb)) {
DRM_ERROR("failed to create drm framebuffer.\n");
ret = PTR_ERR(helper->fb);
goto out;
}
helper->fbdev = fbi;
fbi->par = helper;
fbi->flags = FBINFO_FLAG_DEFAULT;
fbi->fbops = &exynos_drm_fb_ops;
ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
if (ret) {
DRM_ERROR("failed to allocate cmap.\n");
goto out;
}
ret = exynos_drm_fbdev_update(helper, helper->fb);
if (ret < 0) {
fb_dealloc_cmap(&fbi->cmap);
goto out;
}
/*
* if failed, all resources allocated above would be released by
* drm_mode_config_cleanup() when drm_load() had been called prior
* to any specific driver such as fimd or hdmi driver.
*/
out:
mutex_unlock(&dev->struct_mutex);
return ret;
}
static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
int ret = 0;
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* with !helper->fb, it means that this funcion is called first time
* and after that, the helper->fb would be used as clone mode.
*/
if (!helper->fb) {
ret = exynos_drm_fbdev_create(helper, sizes);
if (ret < 0) {
DRM_ERROR("failed to create fbdev.\n");
return ret;
}
/*
* fb_helper expects a value more than 1 if succeed
* because register_framebuffer() should be called.
*/
ret = 1;
}
return ret;
}
static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
.fb_probe = exynos_drm_fbdev_probe,
};
int exynos_drm_fbdev_init(struct drm_device *dev)
{
struct exynos_drm_fbdev *fbdev;
struct exynos_drm_private *private = dev->dev_private;
struct drm_fb_helper *helper;
unsigned int num_crtc;
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
return 0;
fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
if (!fbdev) {
DRM_ERROR("failed to allocate drm fbdev.\n");
return -ENOMEM;
}
private->fb_helper = helper = &fbdev->drm_fb_helper;
helper->funcs = &exynos_drm_fb_helper_funcs;
num_crtc = dev->mode_config.num_crtc;
ret = drm_fb_helper_init(dev, helper, num_crtc, MAX_CONNECTOR);
if (ret < 0) {
DRM_ERROR("failed to initialize drm fb helper.\n");
goto err_init;
}
ret = drm_fb_helper_single_add_all_connectors(helper);
if (ret < 0) {
DRM_ERROR("failed to register drm_fb_helper_connector.\n");
goto err_setup;
}
ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
if (ret < 0) {
DRM_ERROR("failed to set up hw configuration.\n");
goto err_setup;
}
return 0;
err_setup:
drm_fb_helper_fini(helper);
err_init:
private->fb_helper = NULL;
kfree(fbdev);
return ret;
}
static void exynos_drm_fbdev_destroy(struct drm_device *dev,
struct drm_fb_helper *fb_helper)
{
struct drm_framebuffer *fb;
/* release drm framebuffer and real buffer */
if (fb_helper->fb && fb_helper->fb->funcs) {
fb = fb_helper->fb;
if (fb && fb->funcs->destroy)
fb->funcs->destroy(fb);
}
/* release linux framebuffer */
if (fb_helper->fbdev) {
struct fb_info *info;
int ret;
info = fb_helper->fbdev;
ret = unregister_framebuffer(info);
if (ret < 0)
DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
if (info->cmap.len)
fb_dealloc_cmap(&info->cmap);
framebuffer_release(info);
}
drm_fb_helper_fini(fb_helper);
}
void exynos_drm_fbdev_fini(struct drm_device *dev)
{
struct exynos_drm_private *private = dev->dev_private;
struct exynos_drm_fbdev *fbdev;
if (!private || !private->fb_helper)
return;
fbdev = to_exynos_fbdev(private->fb_helper);
if (fbdev->exynos_gem_obj)
exynos_drm_gem_destroy(fbdev->exynos_gem_obj);
exynos_drm_fbdev_destroy(dev, private->fb_helper);
kfree(fbdev);
private->fb_helper = NULL;
}
void exynos_drm_fbdev_restore_mode(struct drm_device *dev)
{
struct exynos_drm_private *private = dev->dev_private;
if (!private || !private->fb_helper)
return;
drm_fb_helper_restore_fbdev_mode(private->fb_helper);
}
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
*
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@samsung.com>
* Seung-Woo Kim <sw0312.kim@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _EXYNOS_DRM_FBDEV_H_
#define _EXYNOS_DRM_FBDEV_H_
int exynos_drm_fbdev_init(struct drm_device *dev);
int exynos_drm_fbdev_reinit(struct drm_device *dev);
void exynos_drm_fbdev_fini(struct drm_device *dev);
void exynos_drm_fbdev_restore_mode(struct drm_device *dev);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,742 @@
/* exynos_drm_gem.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Author: Inki Dae <inki.dae@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "drmP.h"
#include "drm.h"
#include <linux/shmem_fs.h>
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_gem.h"
#include "exynos_drm_buf.h"
static unsigned int convert_to_vm_err_msg(int msg)
{
unsigned int out_msg;
switch (msg) {
case 0:
case -ERESTARTSYS:
case -EINTR:
out_msg = VM_FAULT_NOPAGE;
break;
case -ENOMEM:
out_msg = VM_FAULT_OOM;
break;
default:
out_msg = VM_FAULT_SIGBUS;
break;
}
return out_msg;
}
static int check_gem_flags(unsigned int flags)
{
if (flags & ~(EXYNOS_BO_MASK)) {
DRM_ERROR("invalid flags.\n");
return -EINVAL;
}
return 0;
}
static unsigned long roundup_gem_size(unsigned long size, unsigned int flags)
{
if (!IS_NONCONTIG_BUFFER(flags)) {
if (size >= SZ_1M)
return roundup(size, SECTION_SIZE);
else if (size >= SZ_64K)
return roundup(size, SZ_64K);
else
goto out;
}
out:
return roundup(size, PAGE_SIZE);
}
static struct page **exynos_gem_get_pages(struct drm_gem_object *obj,
gfp_t gfpmask)
{
struct inode *inode;
struct address_space *mapping;
struct page *p, **pages;
int i, npages;
/* This is the shared memory object that backs the GEM resource */
inode = obj->filp->f_path.dentry->d_inode;
mapping = inode->i_mapping;
npages = obj->size >> PAGE_SHIFT;
pages = drm_malloc_ab(npages, sizeof(struct page *));
if (pages == NULL)
return ERR_PTR(-ENOMEM);
gfpmask |= mapping_gfp_mask(mapping);
for (i = 0; i < npages; i++) {
p = shmem_read_mapping_page_gfp(mapping, i, gfpmask);
if (IS_ERR(p))
goto fail;
pages[i] = p;
}
return pages;
fail:
while (i--)
page_cache_release(pages[i]);
drm_free_large(pages);
return ERR_PTR(PTR_ERR(p));
}
static void exynos_gem_put_pages(struct drm_gem_object *obj,
struct page **pages,
bool dirty, bool accessed)
{
int i, npages;
npages = obj->size >> PAGE_SHIFT;
for (i = 0; i < npages; i++) {
if (dirty)
set_page_dirty(pages[i]);
if (accessed)
mark_page_accessed(pages[i]);
/* Undo the reference we took when populating the table */
page_cache_release(pages[i]);
}
drm_free_large(pages);
}
static int exynos_drm_gem_map_pages(struct drm_gem_object *obj,
struct vm_area_struct *vma,
unsigned long f_vaddr,
pgoff_t page_offset)
{
struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer;
unsigned long pfn;
if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
if (!buf->pages)
return -EINTR;
pfn = page_to_pfn(buf->pages[page_offset++]);
} else
pfn = (buf->dma_addr >> PAGE_SHIFT) + page_offset;
return vm_insert_mixed(vma, f_vaddr, pfn);
}
static int exynos_drm_gem_get_pages(struct drm_gem_object *obj)
{
struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer;
struct scatterlist *sgl;
struct page **pages;
unsigned int npages, i = 0;
int ret;
if (buf->pages) {
DRM_DEBUG_KMS("already allocated.\n");
return -EINVAL;
}
pages = exynos_gem_get_pages(obj, GFP_KERNEL);
if (IS_ERR(pages)) {
DRM_ERROR("failed to get pages.\n");
return PTR_ERR(pages);
}
npages = obj->size >> PAGE_SHIFT;
buf->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
if (!buf->sgt) {
DRM_ERROR("failed to allocate sg table.\n");
ret = -ENOMEM;
goto err;
}
ret = sg_alloc_table(buf->sgt, npages, GFP_KERNEL);
if (ret < 0) {
DRM_ERROR("failed to initialize sg table.\n");
ret = -EFAULT;
goto err1;
}
sgl = buf->sgt->sgl;
/* set all pages to sg list. */
while (i < npages) {
sg_set_page(sgl, pages[i], PAGE_SIZE, 0);
sg_dma_address(sgl) = page_to_phys(pages[i]);
i++;
sgl = sg_next(sgl);
}
/* add some codes for UNCACHED type here. TODO */
buf->pages = pages;
return ret;
err1:
kfree(buf->sgt);
buf->sgt = NULL;
err:
exynos_gem_put_pages(obj, pages, true, false);
return ret;
}
static void exynos_drm_gem_put_pages(struct drm_gem_object *obj)
{
struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer;
/*
* if buffer typs is EXYNOS_BO_NONCONTIG then release all pages
* allocated at gem fault handler.
*/
sg_free_table(buf->sgt);
kfree(buf->sgt);
buf->sgt = NULL;
exynos_gem_put_pages(obj, buf->pages, true, false);
buf->pages = NULL;
/* add some codes for UNCACHED type here. TODO */
}
static int exynos_drm_gem_handle_create(struct drm_gem_object *obj,
struct drm_file *file_priv,
unsigned int *handle)
{
int ret;
/*
* allocate a id of idr table where the obj is registered
* and handle has the id what user can see.
*/
ret = drm_gem_handle_create(file_priv, obj, handle);
if (ret)
return ret;
DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle);
/* drop reference from allocate - handle holds it now. */
drm_gem_object_unreference_unlocked(obj);
return 0;
}
void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)
{
struct drm_gem_object *obj;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!exynos_gem_obj)
return;
obj = &exynos_gem_obj->base;
DRM_DEBUG_KMS("handle count = %d\n", atomic_read(&obj->handle_count));
if ((exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) &&
exynos_gem_obj->buffer->pages)
exynos_drm_gem_put_pages(obj);
else
exynos_drm_free_buf(obj->dev, exynos_gem_obj->flags,
exynos_gem_obj->buffer);
exynos_drm_fini_buf(obj->dev, exynos_gem_obj->buffer);
exynos_gem_obj->buffer = NULL;
if (obj->map_list.map)
drm_gem_free_mmap_offset(obj);
/* release file pointer to gem object. */
drm_gem_object_release(obj);
kfree(exynos_gem_obj);
exynos_gem_obj = NULL;
}
static struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,
unsigned long size)
{
struct exynos_drm_gem_obj *exynos_gem_obj;
struct drm_gem_object *obj;
int ret;
exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL);
if (!exynos_gem_obj) {
DRM_ERROR("failed to allocate exynos gem object\n");
return NULL;
}
exynos_gem_obj->size = size;
obj = &exynos_gem_obj->base;
ret = drm_gem_object_init(dev, obj, size);
if (ret < 0) {
DRM_ERROR("failed to initialize gem object\n");
kfree(exynos_gem_obj);
return NULL;
}
DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp);
return exynos_gem_obj;
}
struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
unsigned int flags,
unsigned long size)
{
struct exynos_drm_gem_obj *exynos_gem_obj;
struct exynos_drm_gem_buf *buf;
int ret;
if (!size) {
DRM_ERROR("invalid size.\n");
return ERR_PTR(-EINVAL);
}
size = roundup_gem_size(size, flags);
DRM_DEBUG_KMS("%s\n", __FILE__);
ret = check_gem_flags(flags);
if (ret)
return ERR_PTR(ret);
buf = exynos_drm_init_buf(dev, size);
if (!buf)
return ERR_PTR(-ENOMEM);
exynos_gem_obj = exynos_drm_gem_init(dev, size);
if (!exynos_gem_obj) {
ret = -ENOMEM;
goto err_fini_buf;
}
exynos_gem_obj->buffer = buf;
/* set memory type and cache attribute from user side. */
exynos_gem_obj->flags = flags;
/*
* allocate all pages as desired size if user wants to allocate
* physically non-continuous memory.
*/
if (flags & EXYNOS_BO_NONCONTIG) {
ret = exynos_drm_gem_get_pages(&exynos_gem_obj->base);
if (ret < 0) {
drm_gem_object_release(&exynos_gem_obj->base);
goto err_fini_buf;
}
} else {
ret = exynos_drm_alloc_buf(dev, buf, flags);
if (ret < 0) {
drm_gem_object_release(&exynos_gem_obj->base);
goto err_fini_buf;
}
}
return exynos_gem_obj;
err_fini_buf:
exynos_drm_fini_buf(dev, buf);
return ERR_PTR(ret);
}
int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_exynos_gem_create *args = data;
struct exynos_drm_gem_obj *exynos_gem_obj;
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size);
if (IS_ERR(exynos_gem_obj))
return PTR_ERR(exynos_gem_obj);
ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
&args->handle);
if (ret) {
exynos_drm_gem_destroy(exynos_gem_obj);
return ret;
}
return 0;
}
void *exynos_drm_gem_get_dma_addr(struct drm_device *dev,
unsigned int gem_handle,
struct drm_file *file_priv)
{
struct exynos_drm_gem_obj *exynos_gem_obj;
struct drm_gem_object *obj;
obj = drm_gem_object_lookup(dev, file_priv, gem_handle);
if (!obj) {
DRM_ERROR("failed to lookup gem object.\n");
return ERR_PTR(-EINVAL);
}
exynos_gem_obj = to_exynos_gem_obj(obj);
if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
DRM_DEBUG_KMS("not support NONCONTIG type.\n");
drm_gem_object_unreference_unlocked(obj);
/* TODO */
return ERR_PTR(-EINVAL);
}
return &exynos_gem_obj->buffer->dma_addr;
}
void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
unsigned int gem_handle,
struct drm_file *file_priv)
{
struct exynos_drm_gem_obj *exynos_gem_obj;
struct drm_gem_object *obj;
obj = drm_gem_object_lookup(dev, file_priv, gem_handle);
if (!obj) {
DRM_ERROR("failed to lookup gem object.\n");
return;
}
exynos_gem_obj = to_exynos_gem_obj(obj);
if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
DRM_DEBUG_KMS("not support NONCONTIG type.\n");
drm_gem_object_unreference_unlocked(obj);
/* TODO */
return;
}
drm_gem_object_unreference_unlocked(obj);
/*
* decrease obj->refcount one more time because we has already
* increased it at exynos_drm_gem_get_dma_addr().
*/
drm_gem_object_unreference_unlocked(obj);
}
int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_exynos_gem_map_off *args = data;
DRM_DEBUG_KMS("%s\n", __FILE__);
DRM_DEBUG_KMS("handle = 0x%x, offset = 0x%lx\n",
args->handle, (unsigned long)args->offset);
if (!(dev->driver->driver_features & DRIVER_GEM)) {
DRM_ERROR("does not support GEM.\n");
return -ENODEV;
}
return exynos_drm_gem_dumb_map_offset(file_priv, dev, args->handle,
&args->offset);
}
static int exynos_drm_gem_mmap_buffer(struct file *filp,
struct vm_area_struct *vma)
{
struct drm_gem_object *obj = filp->private_data;
struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
struct exynos_drm_gem_buf *buffer;
unsigned long pfn, vm_size, usize, uaddr = vma->vm_start;
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
vma->vm_flags |= (VM_IO | VM_RESERVED);
/* in case of direct mapping, always having non-cachable attribute */
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
vm_size = usize = vma->vm_end - vma->vm_start;
/*
* a buffer contains information to physically continuous memory
* allocated by user request or at framebuffer creation.
*/
buffer = exynos_gem_obj->buffer;
/* check if user-requested size is valid. */
if (vm_size > buffer->size)
return -EINVAL;
if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
int i = 0;
if (!buffer->pages)
return -EINVAL;
vma->vm_flags |= VM_MIXEDMAP;
do {
ret = vm_insert_page(vma, uaddr, buffer->pages[i++]);
if (ret) {
DRM_ERROR("failed to remap user space.\n");
return ret;
}
uaddr += PAGE_SIZE;
usize -= PAGE_SIZE;
} while (usize > 0);
} else {
/*
* get page frame number to physical memory to be mapped
* to user space.
*/
pfn = ((unsigned long)exynos_gem_obj->buffer->dma_addr) >>
PAGE_SHIFT;
DRM_DEBUG_KMS("pfn = 0x%lx\n", pfn);
if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size,
vma->vm_page_prot)) {
DRM_ERROR("failed to remap pfn range.\n");
return -EAGAIN;
}
}
return 0;
}
static const struct file_operations exynos_drm_gem_fops = {
.mmap = exynos_drm_gem_mmap_buffer,
};
int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_exynos_gem_mmap *args = data;
struct drm_gem_object *obj;
unsigned int addr;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!(dev->driver->driver_features & DRIVER_GEM)) {
DRM_ERROR("does not support GEM.\n");
return -ENODEV;
}
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (!obj) {
DRM_ERROR("failed to lookup gem object.\n");
return -EINVAL;
}
obj->filp->f_op = &exynos_drm_gem_fops;
obj->filp->private_data = obj;
addr = vm_mmap(obj->filp, 0, args->size,
PROT_READ | PROT_WRITE, MAP_SHARED, 0);
drm_gem_object_unreference_unlocked(obj);
if (IS_ERR((void *)addr))
return PTR_ERR((void *)addr);
args->mapped = addr;
DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped);
return 0;
}
int exynos_drm_gem_init_object(struct drm_gem_object *obj)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
return 0;
}
void exynos_drm_gem_free_object(struct drm_gem_object *obj)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_drm_gem_destroy(to_exynos_gem_obj(obj));
}
int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
struct drm_device *dev,
struct drm_mode_create_dumb *args)
{
struct exynos_drm_gem_obj *exynos_gem_obj;
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* alocate memory to be used for framebuffer.
* - this callback would be called by user application
* with DRM_IOCTL_MODE_CREATE_DUMB command.
*/
args->pitch = args->width * args->bpp >> 3;
args->size = PAGE_ALIGN(args->pitch * args->height);
exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size);
if (IS_ERR(exynos_gem_obj))
return PTR_ERR(exynos_gem_obj);
ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
&args->handle);
if (ret) {
exynos_drm_gem_destroy(exynos_gem_obj);
return ret;
}
return 0;
}
int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
struct drm_device *dev, uint32_t handle,
uint64_t *offset)
{
struct exynos_drm_gem_obj *exynos_gem_obj;
struct drm_gem_object *obj;
int ret = 0;
DRM_DEBUG_KMS("%s\n", __FILE__);
mutex_lock(&dev->struct_mutex);
/*
* get offset of memory allocated for drm framebuffer.
* - this callback would be called by user application
* with DRM_IOCTL_MODE_MAP_DUMB command.
*/
obj = drm_gem_object_lookup(dev, file_priv, handle);
if (!obj) {
DRM_ERROR("failed to lookup gem object.\n");
ret = -EINVAL;
goto unlock;
}
exynos_gem_obj = to_exynos_gem_obj(obj);
if (!exynos_gem_obj->base.map_list.map) {
ret = drm_gem_create_mmap_offset(&exynos_gem_obj->base);
if (ret)
goto out;
}
*offset = (u64)exynos_gem_obj->base.map_list.hash.key << PAGE_SHIFT;
DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
out:
drm_gem_object_unreference(obj);
unlock:
mutex_unlock(&dev->struct_mutex);
return ret;
}
int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv,
struct drm_device *dev,
unsigned int handle)
{
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* obj->refcount and obj->handle_count are decreased and
* if both them are 0 then exynos_drm_gem_free_object()
* would be called by callback to release resources.
*/
ret = drm_gem_handle_delete(file_priv, handle);
if (ret < 0) {
DRM_ERROR("failed to delete drm_gem_handle.\n");
return ret;
}
return 0;
}
int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct drm_gem_object *obj = vma->vm_private_data;
struct drm_device *dev = obj->dev;
unsigned long f_vaddr;
pgoff_t page_offset;
int ret;
page_offset = ((unsigned long)vmf->virtual_address -
vma->vm_start) >> PAGE_SHIFT;
f_vaddr = (unsigned long)vmf->virtual_address;
mutex_lock(&dev->struct_mutex);
ret = exynos_drm_gem_map_pages(obj, vma, f_vaddr, page_offset);
if (ret < 0)
DRM_ERROR("failed to map pages.\n");
mutex_unlock(&dev->struct_mutex);
return convert_to_vm_err_msg(ret);
}
int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
/* set vm_area_struct. */
ret = drm_gem_mmap(filp, vma);
if (ret < 0) {
DRM_ERROR("failed to mmap.\n");
return ret;
}
vma->vm_flags &= ~VM_PFNMAP;
vma->vm_flags |= VM_MIXEDMAP;
return ret;
}
@@ -0,0 +1,153 @@
/* exynos_drm_gem.h
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authoer: Inki Dae <inki.dae@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _EXYNOS_DRM_GEM_H_
#define _EXYNOS_DRM_GEM_H_
#define to_exynos_gem_obj(x) container_of(x,\
struct exynos_drm_gem_obj, base)
#define IS_NONCONTIG_BUFFER(f) (f & EXYNOS_BO_NONCONTIG)
/*
* exynos drm gem buffer structure.
*
* @kvaddr: kernel virtual address to allocated memory region.
* @dma_addr: bus address(accessed by dma) to allocated memory region.
* - this address could be physical address without IOMMU and
* device address with IOMMU.
* @sgt: sg table to transfer page data.
* @pages: contain all pages to allocated memory region.
* @size: size of allocated memory region.
*/
struct exynos_drm_gem_buf {
void __iomem *kvaddr;
dma_addr_t dma_addr;
struct sg_table *sgt;
struct page **pages;
unsigned long size;
};
/*
* exynos drm buffer structure.
*
* @base: a gem object.
* - a new handle to this gem object would be created
* by drm_gem_handle_create().
* @buffer: a pointer to exynos_drm_gem_buffer object.
* - contain the information to memory region allocated
* by user request or at framebuffer creation.
* continuous memory region allocated by user request
* or at framebuffer creation.
* @size: total memory size to physically non-continuous memory region.
* @flags: indicate memory type to allocated buffer and cache attruibute.
*
* P.S. this object would be transfered to user as kms_bo.handle so
* user can access the buffer through kms_bo.handle.
*/
struct exynos_drm_gem_obj {
struct drm_gem_object base;
struct exynos_drm_gem_buf *buffer;
unsigned long size;
unsigned int flags;
};
/* destroy a buffer with gem object */
void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj);
/* create a new buffer with gem object */
struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
unsigned int flags,
unsigned long size);
/*
* request gem object creation and buffer allocation as the size
* that it is calculated with framebuffer information such as width,
* height and bpp.
*/
int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
/*
* get dma address from gem handle and this function could be used for
* other drivers such as 2d/3d acceleration drivers.
* with this function call, gem object reference count would be increased.
*/
void *exynos_drm_gem_get_dma_addr(struct drm_device *dev,
unsigned int gem_handle,
struct drm_file *file_priv);
/*
* put dma address from gem handle and this function could be used for
* other drivers such as 2d/3d acceleration drivers.
* with this function call, gem object reference count would be decreased.
*/
void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
unsigned int gem_handle,
struct drm_file *file_priv);
/* get buffer offset to map to user space. */
int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
/*
* mmap the physically continuous memory that a gem object contains
* to user space.
*/
int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
/* initialize gem object. */
int exynos_drm_gem_init_object(struct drm_gem_object *obj);
/* free gem object. */
void exynos_drm_gem_free_object(struct drm_gem_object *gem_obj);
/* create memory region for drm framebuffer. */
int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
struct drm_device *dev,
struct drm_mode_create_dumb *args);
/* map memory region for drm framebuffer to user space. */
int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
struct drm_device *dev, uint32_t handle,
uint64_t *offset);
/*
* destroy memory region allocated.
* - a gem handle and physical memory region pointed by a gem object
* would be released by drm_gem_handle_delete().
*/
int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv,
struct drm_device *dev,
unsigned int handle);
/* page fault handler and mmap fault address(virtual) to physical memory. */
int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
/* set vm_flags and we can change the vm attribute to other one at here. */
int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
#endif
@@ -0,0 +1,377 @@
/*
* Copyright (C) 2011 Samsung Electronics Co.Ltd
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Seung-Woo Kim <sw0312.kim@samsung.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 "drmP.h"
#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_hdmi.h"
#define to_context(dev) platform_get_drvdata(to_platform_device(dev))
#define to_subdrv(dev) to_context(dev)
#define get_ctx_from_subdrv(subdrv) container_of(subdrv,\
struct drm_hdmi_context, subdrv);
/* these callback points shoud be set by specific drivers. */
static struct exynos_hdmi_ops *hdmi_ops;
static struct exynos_mixer_ops *mixer_ops;
struct drm_hdmi_context {
struct exynos_drm_subdrv subdrv;
struct exynos_drm_hdmi_context *hdmi_ctx;
struct exynos_drm_hdmi_context *mixer_ctx;
};
void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
if (ops)
hdmi_ops = ops;
}
void exynos_mixer_ops_register(struct exynos_mixer_ops *ops)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
if (ops)
mixer_ops = ops;
}
static bool drm_hdmi_is_connected(struct device *dev)
{
struct drm_hdmi_context *ctx = to_context(dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (hdmi_ops && hdmi_ops->is_connected)
return hdmi_ops->is_connected(ctx->hdmi_ctx->ctx);
return false;
}
static int drm_hdmi_get_edid(struct device *dev,
struct drm_connector *connector, u8 *edid, int len)
{
struct drm_hdmi_context *ctx = to_context(dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (hdmi_ops && hdmi_ops->get_edid)
return hdmi_ops->get_edid(ctx->hdmi_ctx->ctx, connector, edid,
len);
return 0;
}
static int drm_hdmi_check_timing(struct device *dev, void *timing)
{
struct drm_hdmi_context *ctx = to_context(dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (hdmi_ops && hdmi_ops->check_timing)
return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing);
return 0;
}
static int drm_hdmi_power_on(struct device *dev, int mode)
{
struct drm_hdmi_context *ctx = to_context(dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (hdmi_ops && hdmi_ops->power_on)
return hdmi_ops->power_on(ctx->hdmi_ctx->ctx, mode);
return 0;
}
static struct exynos_drm_display_ops drm_hdmi_display_ops = {
.type = EXYNOS_DISPLAY_TYPE_HDMI,
.is_connected = drm_hdmi_is_connected,
.get_edid = drm_hdmi_get_edid,
.check_timing = drm_hdmi_check_timing,
.power_on = drm_hdmi_power_on,
};
static int drm_hdmi_enable_vblank(struct device *subdrv_dev)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
struct exynos_drm_manager *manager = subdrv->manager;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (mixer_ops && mixer_ops->enable_vblank)
return mixer_ops->enable_vblank(ctx->mixer_ctx->ctx,
manager->pipe);
return 0;
}
static void drm_hdmi_disable_vblank(struct device *subdrv_dev)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (mixer_ops && mixer_ops->disable_vblank)
return mixer_ops->disable_vblank(ctx->mixer_ctx->ctx);
}
static void drm_hdmi_mode_fixup(struct device *subdrv_dev,
struct drm_connector *connector,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (hdmi_ops && hdmi_ops->mode_fixup)
hdmi_ops->mode_fixup(ctx->hdmi_ctx->ctx, connector, mode,
adjusted_mode);
}
static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (hdmi_ops && hdmi_ops->mode_set)
hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
}
static void drm_hdmi_get_max_resol(struct device *subdrv_dev,
unsigned int *width, unsigned int *height)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (hdmi_ops && hdmi_ops->get_max_resol)
hdmi_ops->get_max_resol(ctx->hdmi_ctx->ctx, width, height);
}
static void drm_hdmi_commit(struct device *subdrv_dev)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (hdmi_ops && hdmi_ops->commit)
hdmi_ops->commit(ctx->hdmi_ctx->ctx);
}
static void drm_hdmi_dpms(struct device *subdrv_dev, int mode)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
switch (mode) {
case DRM_MODE_DPMS_ON:
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
if (hdmi_ops && hdmi_ops->disable)
hdmi_ops->disable(ctx->hdmi_ctx->ctx);
break;
default:
DRM_DEBUG_KMS("unkown dps mode: %d\n", mode);
break;
}
}
static struct exynos_drm_manager_ops drm_hdmi_manager_ops = {
.dpms = drm_hdmi_dpms,
.enable_vblank = drm_hdmi_enable_vblank,
.disable_vblank = drm_hdmi_disable_vblank,
.mode_fixup = drm_hdmi_mode_fixup,
.mode_set = drm_hdmi_mode_set,
.get_max_resol = drm_hdmi_get_max_resol,
.commit = drm_hdmi_commit,
};
static void drm_mixer_mode_set(struct device *subdrv_dev,
struct exynos_drm_overlay *overlay)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (mixer_ops && mixer_ops->win_mode_set)
mixer_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay);
}
static void drm_mixer_commit(struct device *subdrv_dev, int zpos)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (mixer_ops && mixer_ops->win_commit)
mixer_ops->win_commit(ctx->mixer_ctx->ctx, zpos);
}
static void drm_mixer_disable(struct device *subdrv_dev, int zpos)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (mixer_ops && mixer_ops->win_disable)
mixer_ops->win_disable(ctx->mixer_ctx->ctx, zpos);
}
static struct exynos_drm_overlay_ops drm_hdmi_overlay_ops = {
.mode_set = drm_mixer_mode_set,
.commit = drm_mixer_commit,
.disable = drm_mixer_disable,
};
static struct exynos_drm_manager hdmi_manager = {
.pipe = -1,
.ops = &drm_hdmi_manager_ops,
.overlay_ops = &drm_hdmi_overlay_ops,
.display_ops = &drm_hdmi_display_ops,
};
static int hdmi_subdrv_probe(struct drm_device *drm_dev,
struct device *dev)
{
struct exynos_drm_subdrv *subdrv = to_subdrv(dev);
struct drm_hdmi_context *ctx;
struct platform_device *pdev = to_platform_device(dev);
struct exynos_drm_common_hdmi_pd *pd;
DRM_DEBUG_KMS("%s\n", __FILE__);
pd = pdev->dev.platform_data;
if (!pd) {
DRM_DEBUG_KMS("platform data is null.\n");
return -EFAULT;
}
if (!pd->hdmi_dev) {
DRM_DEBUG_KMS("hdmi device is null.\n");
return -EFAULT;
}
if (!pd->mixer_dev) {
DRM_DEBUG_KMS("mixer device is null.\n");
return -EFAULT;
}
ctx = get_ctx_from_subdrv(subdrv);
ctx->hdmi_ctx = (struct exynos_drm_hdmi_context *)
to_context(pd->hdmi_dev);
if (!ctx->hdmi_ctx) {
DRM_DEBUG_KMS("hdmi context is null.\n");
return -EFAULT;
}
ctx->hdmi_ctx->drm_dev = drm_dev;
ctx->mixer_ctx = (struct exynos_drm_hdmi_context *)
to_context(pd->mixer_dev);
if (!ctx->mixer_ctx) {
DRM_DEBUG_KMS("mixer context is null.\n");
return -EFAULT;
}
ctx->mixer_ctx->drm_dev = drm_dev;
return 0;
}
static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct exynos_drm_subdrv *subdrv;
struct drm_hdmi_context *ctx;
DRM_DEBUG_KMS("%s\n", __FILE__);
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx) {
DRM_LOG_KMS("failed to alloc common hdmi context.\n");
return -ENOMEM;
}
subdrv = &ctx->subdrv;
subdrv->dev = dev;
subdrv->manager = &hdmi_manager;
subdrv->probe = hdmi_subdrv_probe;
platform_set_drvdata(pdev, subdrv);
exynos_drm_subdrv_register(subdrv);
return 0;
}
static int hdmi_runtime_suspend(struct device *dev)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
return 0;
}
static int hdmi_runtime_resume(struct device *dev)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
return 0;
}
static const struct dev_pm_ops hdmi_pm_ops = {
.runtime_suspend = hdmi_runtime_suspend,
.runtime_resume = hdmi_runtime_resume,
};
static int __devexit exynos_drm_hdmi_remove(struct platform_device *pdev)
{
struct drm_hdmi_context *ctx = platform_get_drvdata(pdev);
DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_drm_subdrv_unregister(&ctx->subdrv);
kfree(ctx);
return 0;
}
struct platform_driver exynos_drm_common_hdmi_driver = {
.probe = exynos_drm_hdmi_probe,
.remove = __devexit_p(exynos_drm_hdmi_remove),
.driver = {
.name = "exynos-drm-hdmi",
.owner = THIS_MODULE,
.pm = &hdmi_pm_ops,
},
};
@@ -0,0 +1,73 @@
/* exynos_drm_hdmi.h
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authoer: Inki Dae <inki.dae@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _EXYNOS_DRM_HDMI_H_
#define _EXYNOS_DRM_HDMI_H_
/*
* exynos hdmi common context structure.
*
* @drm_dev: pointer to drm_device.
* @ctx: pointer to the context of specific device driver.
* this context should be hdmi_context or mixer_context.
*/
struct exynos_drm_hdmi_context {
struct drm_device *drm_dev;
void *ctx;
};
struct exynos_hdmi_ops {
/* display */
bool (*is_connected)(void *ctx);
int (*get_edid)(void *ctx, struct drm_connector *connector,
u8 *edid, int len);
int (*check_timing)(void *ctx, void *timing);
int (*power_on)(void *ctx, int mode);
/* manager */
void (*mode_fixup)(void *ctx, struct drm_connector *connector,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
void (*mode_set)(void *ctx, void *mode);
void (*get_max_resol)(void *ctx, unsigned int *width,
unsigned int *height);
void (*commit)(void *ctx);
void (*disable)(void *ctx);
};
struct exynos_mixer_ops {
/* manager */
int (*enable_vblank)(void *ctx, int pipe);
void (*disable_vblank)(void *ctx);
/* overlay */
void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay);
void (*win_commit)(void *ctx, int zpos);
void (*win_disable)(void *ctx, int zpos);
};
void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops);
void exynos_mixer_ops_register(struct exynos_mixer_ops *ops);
#endif
@@ -0,0 +1,171 @@
/*
* Copyright (C) 2011 Samsung Electronics Co.Ltd
* Authors: Joonyoung Shim <jy0922.shim@samsung.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 "drmP.h"
#include "exynos_drm.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_encoder.h"
struct exynos_plane {
struct drm_plane base;
struct exynos_drm_overlay overlay;
bool enabled;
};
static const uint32_t formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_NV12,
DRM_FORMAT_NV12M,
DRM_FORMAT_NV12MT,
};
static int
exynos_update_plane(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 exynos_plane *exynos_plane =
container_of(plane, struct exynos_plane, base);
struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
struct exynos_drm_crtc_pos pos;
unsigned int x = src_x >> 16;
unsigned int y = src_y >> 16;
int ret;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
memset(&pos, 0, sizeof(struct exynos_drm_crtc_pos));
pos.crtc_x = crtc_x;
pos.crtc_y = crtc_y;
pos.crtc_w = crtc_w;
pos.crtc_h = crtc_h;
pos.fb_x = x;
pos.fb_y = y;
/* TODO: scale feature */
ret = exynos_drm_overlay_update(overlay, fb, &crtc->mode, &pos);
if (ret < 0)
return ret;
exynos_drm_fn_encoder(crtc, overlay,
exynos_drm_encoder_crtc_mode_set);
exynos_drm_fn_encoder(crtc, &overlay->zpos,
exynos_drm_encoder_crtc_plane_commit);
exynos_plane->enabled = true;
return 0;
}
static int exynos_disable_plane(struct drm_plane *plane)
{
struct exynos_plane *exynos_plane =
container_of(plane, struct exynos_plane, base);
struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
if (!exynos_plane->enabled)
return 0;
exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
exynos_drm_encoder_crtc_disable);
exynos_plane->enabled = false;
exynos_plane->overlay.zpos = DEFAULT_ZPOS;
return 0;
}
static void exynos_plane_destroy(struct drm_plane *plane)
{
struct exynos_plane *exynos_plane =
container_of(plane, struct exynos_plane, base);
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
exynos_disable_plane(plane);
drm_plane_cleanup(plane);
kfree(exynos_plane);
}
static struct drm_plane_funcs exynos_plane_funcs = {
.update_plane = exynos_update_plane,
.disable_plane = exynos_disable_plane,
.destroy = exynos_plane_destroy,
};
int exynos_plane_init(struct drm_device *dev, unsigned int nr)
{
struct exynos_plane *exynos_plane;
uint32_t possible_crtcs;
exynos_plane = kzalloc(sizeof(struct exynos_plane), GFP_KERNEL);
if (!exynos_plane)
return -ENOMEM;
/* all CRTCs are available */
possible_crtcs = (1 << MAX_CRTC) - 1;
exynos_plane->overlay.zpos = DEFAULT_ZPOS;
return drm_plane_init(dev, &exynos_plane->base, possible_crtcs,
&exynos_plane_funcs, formats, ARRAY_SIZE(formats),
false);
}
int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_exynos_plane_set_zpos *zpos_req = data;
struct drm_mode_object *obj;
struct drm_plane *plane;
struct exynos_plane *exynos_plane;
int ret = 0;
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -EINVAL;
if (zpos_req->zpos < 0 || zpos_req->zpos >= MAX_PLANE) {
if (zpos_req->zpos != DEFAULT_ZPOS) {
DRM_ERROR("zpos not within limits\n");
return -EINVAL;
}
}
mutex_lock(&dev->mode_config.mutex);
obj = drm_mode_object_find(dev, zpos_req->plane_id,
DRM_MODE_OBJECT_PLANE);
if (!obj) {
DRM_DEBUG_KMS("Unknown plane ID %d\n",
zpos_req->plane_id);
ret = -EINVAL;
goto out;
}
plane = obj_to_plane(obj);
exynos_plane = container_of(plane, struct exynos_plane, base);
exynos_plane->overlay.zpos = zpos_req->zpos;
out:
mutex_unlock(&dev->mode_config.mutex);
return ret;
}
@@ -0,0 +1,14 @@
/*
* Copyright (C) 2011 Samsung Electronics Co.Ltd
* Authors: Joonyoung Shim <jy0922.shim@samsung.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.
*
*/
int exynos_plane_init(struct drm_device *dev, unsigned int nr);
int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
@@ -0,0 +1,680 @@
/* exynos_drm_vidi.c
*
* Copyright (C) 2012 Samsung Electronics Co.Ltd
* Authors:
* Inki Dae <inki.dae@samsung.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 "drmP.h"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <drm/exynos_drm.h>
#include "drm_edid.h"
#include "drm_crtc_helper.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_encoder.h"
/* vidi has totally three virtual windows. */
#define WINDOWS_NR 3
#define get_vidi_context(dev) platform_get_drvdata(to_platform_device(dev))
struct vidi_win_data {
unsigned int offset_x;
unsigned int offset_y;
unsigned int ovl_width;
unsigned int ovl_height;
unsigned int fb_width;
unsigned int fb_height;
unsigned int bpp;
dma_addr_t dma_addr;
void __iomem *vaddr;
unsigned int buf_offsize;
unsigned int line_size; /* bytes */
bool enabled;
};
struct vidi_context {
struct exynos_drm_subdrv subdrv;
struct drm_crtc *crtc;
struct vidi_win_data win_data[WINDOWS_NR];
struct edid *raw_edid;
unsigned int clkdiv;
unsigned int default_win;
unsigned long irq_flags;
unsigned int connected;
bool vblank_on;
bool suspended;
struct work_struct work;
struct mutex lock;
};
static const char fake_edid_info[] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x4c, 0x2d, 0x05, 0x05,
0x00, 0x00, 0x00, 0x00, 0x30, 0x12, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78,
0x0a, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0xbd,
0xee, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x66, 0x21, 0x50, 0xb0, 0x51, 0x00,
0x1b, 0x30, 0x40, 0x70, 0x36, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e,
0x01, 0x1d, 0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00,
0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18,
0x4b, 0x1a, 0x44, 0x17, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x00, 0x00, 0x00, 0xfc, 0x00, 0x53, 0x41, 0x4d, 0x53, 0x55, 0x4e, 0x47,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xbc, 0x02, 0x03, 0x1e, 0xf1,
0x46, 0x84, 0x05, 0x03, 0x10, 0x20, 0x22, 0x23, 0x09, 0x07, 0x07, 0x83,
0x01, 0x00, 0x00, 0xe2, 0x00, 0x0f, 0x67, 0x03, 0x0c, 0x00, 0x10, 0x00,
0xb8, 0x2d, 0x01, 0x1d, 0x80, 0x18, 0x71, 0x1c, 0x16, 0x20, 0x58, 0x2c,
0x25, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x9e, 0x8c, 0x0a, 0xd0, 0x8a,
0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, 0xa0, 0x5a, 0x00, 0x00,
0x00, 0x18, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
0x45, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x06
};
static void vidi_fake_vblank_handler(struct work_struct *work);
static bool vidi_display_is_connected(struct device *dev)
{
struct vidi_context *ctx = get_vidi_context(dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* connection request would come from user side
* to do hotplug through specific ioctl.
*/
return ctx->connected ? true : false;
}
static int vidi_get_edid(struct device *dev, struct drm_connector *connector,
u8 *edid, int len)
{
struct vidi_context *ctx = get_vidi_context(dev);
struct edid *raw_edid;
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* the edid data comes from user side and it would be set
* to ctx->raw_edid through specific ioctl.
*/
if (!ctx->raw_edid) {
DRM_DEBUG_KMS("raw_edid is null.\n");
return -EFAULT;
}
raw_edid = kzalloc(len, GFP_KERNEL);
if (!raw_edid) {
DRM_DEBUG_KMS("failed to allocate raw_edid.\n");
return -ENOMEM;
}
memcpy(raw_edid, ctx->raw_edid, min((1 + ctx->raw_edid->extensions)
* EDID_LENGTH, len));
/* attach the edid data to connector. */
connector->display_info.raw_edid = (char *)raw_edid;
memcpy(edid, ctx->raw_edid, min((1 + ctx->raw_edid->extensions)
* EDID_LENGTH, len));
return 0;
}
static void *vidi_get_panel(struct device *dev)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* TODO. */
return NULL;
}
static int vidi_check_timing(struct device *dev, void *timing)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* TODO. */
return 0;
}
static int vidi_display_power_on(struct device *dev, int mode)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* TODO */
return 0;
}
static struct exynos_drm_display_ops vidi_display_ops = {
.type = EXYNOS_DISPLAY_TYPE_VIDI,
.is_connected = vidi_display_is_connected,
.get_edid = vidi_get_edid,
.get_panel = vidi_get_panel,
.check_timing = vidi_check_timing,
.power_on = vidi_display_power_on,
};
static void vidi_dpms(struct device *subdrv_dev, int mode)
{
struct vidi_context *ctx = get_vidi_context(subdrv_dev);
DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
mutex_lock(&ctx->lock);
switch (mode) {
case DRM_MODE_DPMS_ON:
/* TODO. */
break;
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
/* TODO. */
break;
default:
DRM_DEBUG_KMS("unspecified mode %d\n", mode);
break;
}
mutex_unlock(&ctx->lock);
}
static void vidi_apply(struct device *subdrv_dev)
{
struct vidi_context *ctx = get_vidi_context(subdrv_dev);
struct exynos_drm_manager *mgr = ctx->subdrv.manager;
struct exynos_drm_manager_ops *mgr_ops = mgr->ops;
struct exynos_drm_overlay_ops *ovl_ops = mgr->overlay_ops;
struct vidi_win_data *win_data;
int i;
DRM_DEBUG_KMS("%s\n", __FILE__);
for (i = 0; i < WINDOWS_NR; i++) {
win_data = &ctx->win_data[i];
if (win_data->enabled && (ovl_ops && ovl_ops->commit))
ovl_ops->commit(subdrv_dev, i);
}
if (mgr_ops && mgr_ops->commit)
mgr_ops->commit(subdrv_dev);
}
static void vidi_commit(struct device *dev)
{
struct vidi_context *ctx = get_vidi_context(dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (ctx->suspended)
return;
}
static int vidi_enable_vblank(struct device *dev)
{
struct vidi_context *ctx = get_vidi_context(dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (ctx->suspended)
return -EPERM;
if (!test_and_set_bit(0, &ctx->irq_flags))
ctx->vblank_on = true;
return 0;
}
static void vidi_disable_vblank(struct device *dev)
{
struct vidi_context *ctx = get_vidi_context(dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
if (ctx->suspended)
return;
if (test_and_clear_bit(0, &ctx->irq_flags))
ctx->vblank_on = false;
}
static struct exynos_drm_manager_ops vidi_manager_ops = {
.dpms = vidi_dpms,
.apply = vidi_apply,
.commit = vidi_commit,
.enable_vblank = vidi_enable_vblank,
.disable_vblank = vidi_disable_vblank,
};
static void vidi_win_mode_set(struct device *dev,
struct exynos_drm_overlay *overlay)
{
struct vidi_context *ctx = get_vidi_context(dev);
struct vidi_win_data *win_data;
int win;
unsigned long offset;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!overlay) {
dev_err(dev, "overlay is NULL\n");
return;
}
win = overlay->zpos;
if (win == DEFAULT_ZPOS)
win = ctx->default_win;
if (win < 0 || win > WINDOWS_NR)
return;
offset = overlay->fb_x * (overlay->bpp >> 3);
offset += overlay->fb_y * overlay->pitch;
DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch);
win_data = &ctx->win_data[win];
win_data->offset_x = overlay->crtc_x;
win_data->offset_y = overlay->crtc_y;
win_data->ovl_width = overlay->crtc_width;
win_data->ovl_height = overlay->crtc_height;
win_data->fb_width = overlay->fb_width;
win_data->fb_height = overlay->fb_height;
win_data->dma_addr = overlay->dma_addr[0] + offset;
win_data->vaddr = overlay->vaddr[0] + offset;
win_data->bpp = overlay->bpp;
win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) *
(overlay->bpp >> 3);
win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3);
/*
* some parts of win_data should be transferred to user side
* through specific ioctl.
*/
DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
win_data->offset_x, win_data->offset_y);
DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
win_data->ovl_width, win_data->ovl_height);
DRM_DEBUG_KMS("paddr = 0x%lx, vaddr = 0x%lx\n",
(unsigned long)win_data->dma_addr,
(unsigned long)win_data->vaddr);
DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
overlay->fb_width, overlay->crtc_width);
}
static void vidi_win_commit(struct device *dev, int zpos)
{
struct vidi_context *ctx = get_vidi_context(dev);
struct vidi_win_data *win_data;
int win = zpos;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (ctx->suspended)
return;
if (win == DEFAULT_ZPOS)
win = ctx->default_win;
if (win < 0 || win > WINDOWS_NR)
return;
win_data = &ctx->win_data[win];
win_data->enabled = true;
DRM_DEBUG_KMS("dma_addr = 0x%x\n", win_data->dma_addr);
if (ctx->vblank_on)
schedule_work(&ctx->work);
}
static void vidi_win_disable(struct device *dev, int zpos)
{
struct vidi_context *ctx = get_vidi_context(dev);
struct vidi_win_data *win_data;
int win = zpos;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (win == DEFAULT_ZPOS)
win = ctx->default_win;
if (win < 0 || win > WINDOWS_NR)
return;
win_data = &ctx->win_data[win];
win_data->enabled = false;
/* TODO. */
}
static struct exynos_drm_overlay_ops vidi_overlay_ops = {
.mode_set = vidi_win_mode_set,
.commit = vidi_win_commit,
.disable = vidi_win_disable,
};
static struct exynos_drm_manager vidi_manager = {
.pipe = -1,
.ops = &vidi_manager_ops,
.overlay_ops = &vidi_overlay_ops,
.display_ops = &vidi_display_ops,
};
static void vidi_finish_pageflip(struct drm_device *drm_dev, int crtc)
{
struct exynos_drm_private *dev_priv = drm_dev->dev_private;
struct drm_pending_vblank_event *e, *t;
struct timeval now;
unsigned long flags;
bool is_checked = false;
spin_lock_irqsave(&drm_dev->event_lock, flags);
list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list,
base.link) {
/* if event's pipe isn't same as crtc then ignore it. */
if (crtc != e->pipe)
continue;
is_checked = true;
do_gettimeofday(&now);
e->event.sequence = 0;
e->event.tv_sec = now.tv_sec;
e->event.tv_usec = now.tv_usec;
list_move_tail(&e->base.link, &e->base.file_priv->event_list);
wake_up_interruptible(&e->base.file_priv->event_wait);
}
if (is_checked) {
/*
* call drm_vblank_put only in case that drm_vblank_get was
* called.
*/
if (atomic_read(&drm_dev->vblank_refcount[crtc]) > 0)
drm_vblank_put(drm_dev, crtc);
/*
* don't off vblank if vblank_disable_allowed is 1,
* because vblank would be off by timer handler.
*/
if (!drm_dev->vblank_disable_allowed)
drm_vblank_off(drm_dev, crtc);
}
spin_unlock_irqrestore(&drm_dev->event_lock, flags);
}
static void vidi_fake_vblank_handler(struct work_struct *work)
{
struct vidi_context *ctx = container_of(work, struct vidi_context,
work);
struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
struct exynos_drm_manager *manager = subdrv->manager;
if (manager->pipe < 0)
return;
/* refresh rate is about 50Hz. */
usleep_range(16000, 20000);
drm_handle_vblank(subdrv->drm_dev, manager->pipe);
vidi_finish_pageflip(subdrv->drm_dev, manager->pipe);
}
static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* enable drm irq mode.
* - with irq_enabled = 1, we can use the vblank feature.
*
* P.S. note that we wouldn't use drm irq handler but
* just specific driver own one instead because
* drm framework supports only one irq handler.
*/
drm_dev->irq_enabled = 1;
/*
* with vblank_disable_allowed = 1, vblank interrupt will be disabled
* by drm timer once a current process gives up ownership of
* vblank event.(after drm_vblank_put function is called)
*/
drm_dev->vblank_disable_allowed = 1;
return 0;
}
static void vidi_subdrv_remove(struct drm_device *drm_dev)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* TODO. */
}
static int vidi_power_on(struct vidi_context *ctx, bool enable)
{
struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
struct device *dev = subdrv->dev;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (enable != false && enable != true)
return -EINVAL;
if (enable) {
ctx->suspended = false;
/* if vblank was enabled status, enable it again. */
if (test_and_clear_bit(0, &ctx->irq_flags))
vidi_enable_vblank(dev);
vidi_apply(dev);
} else {
ctx->suspended = true;
}
return 0;
}
static int vidi_show_connection(struct device *dev,
struct device_attribute *attr, char *buf)
{
int rc;
struct vidi_context *ctx = get_vidi_context(dev);
mutex_lock(&ctx->lock);
rc = sprintf(buf, "%d\n", ctx->connected);
mutex_unlock(&ctx->lock);
return rc;
}
static int vidi_store_connection(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct vidi_context *ctx = get_vidi_context(dev);
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
ret = kstrtoint(buf, 0, &ctx->connected);
if (ret)
return ret;
if (ctx->connected > 1)
return -EINVAL;
DRM_DEBUG_KMS("requested connection.\n");
drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
return len;
}
static DEVICE_ATTR(connection, 0644, vidi_show_connection,
vidi_store_connection);
int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
struct drm_file *file_priv)
{
struct vidi_context *ctx = NULL;
struct drm_encoder *encoder;
struct exynos_drm_manager *manager;
struct exynos_drm_display_ops *display_ops;
struct drm_exynos_vidi_connection *vidi = data;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!vidi) {
DRM_DEBUG_KMS("user data for vidi is null.\n");
return -EINVAL;
}
if (!vidi->edid) {
DRM_DEBUG_KMS("edid data is null.\n");
return -EINVAL;
}
if (vidi->connection > 1) {
DRM_DEBUG_KMS("connection should be 0 or 1.\n");
return -EINVAL;
}
list_for_each_entry(encoder, &drm_dev->mode_config.encoder_list,
head) {
manager = exynos_drm_get_manager(encoder);
display_ops = manager->display_ops;
if (display_ops->type == EXYNOS_DISPLAY_TYPE_VIDI) {
ctx = get_vidi_context(manager->dev);
break;
}
}
if (!ctx) {
DRM_DEBUG_KMS("not found virtual device type encoder.\n");
return -EINVAL;
}
if (ctx->connected == vidi->connection) {
DRM_DEBUG_KMS("same connection request.\n");
return -EINVAL;
}
if (vidi->connection)
ctx->raw_edid = (struct edid *)vidi->edid;
ctx->connected = vidi->connection;
drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
return 0;
}
static int __devinit vidi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct vidi_context *ctx;
struct exynos_drm_subdrv *subdrv;
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->default_win = 0;
INIT_WORK(&ctx->work, vidi_fake_vblank_handler);
/* for test */
ctx->raw_edid = (struct edid *)fake_edid_info;
subdrv = &ctx->subdrv;
subdrv->dev = dev;
subdrv->manager = &vidi_manager;
subdrv->probe = vidi_subdrv_probe;
subdrv->remove = vidi_subdrv_remove;
mutex_init(&ctx->lock);
platform_set_drvdata(pdev, ctx);
ret = device_create_file(&pdev->dev, &dev_attr_connection);
if (ret < 0)
DRM_INFO("failed to create connection sysfs.\n");
exynos_drm_subdrv_register(subdrv);
return 0;
}
static int __devexit vidi_remove(struct platform_device *pdev)
{
struct vidi_context *ctx = platform_get_drvdata(pdev);
DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_drm_subdrv_unregister(&ctx->subdrv);
kfree(ctx);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int vidi_suspend(struct device *dev)
{
struct vidi_context *ctx = get_vidi_context(dev);
return vidi_power_on(ctx, false);
}
static int vidi_resume(struct device *dev)
{
struct vidi_context *ctx = get_vidi_context(dev);
return vidi_power_on(ctx, true);
}
#endif
static const struct dev_pm_ops vidi_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(vidi_suspend, vidi_resume)
};
struct platform_driver vidi_driver = {
.probe = vidi_probe,
.remove = __devexit_p(vidi_remove),
.driver = {
.name = "exynos-drm-vidi",
.owner = THIS_MODULE,
.pm = &vidi_pm_ops,
},
};
@@ -0,0 +1,36 @@
/* exynos_drm_vidi.h
*
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
* Author: Inki Dae <inki.dae@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _EXYNOS_DRM_VIDI_H_
#define _EXYNOS_DRM_VIDI_H_
#ifdef CONFIG_DRM_EXYNOS_VIDI
int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
struct drm_file *file_priv);
#else
#define vidi_connection_ioctl NULL
#endif
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,37 @@
/*
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Seung-Woo Kim <sw0312.kim@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _EXYNOS_HDMI_H_
#define _EXYNOS_HDMI_H_
void hdmi_attach_ddc_client(struct i2c_client *ddc);
void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy);
extern struct i2c_driver hdmiphy_driver;
extern struct i2c_driver ddc_driver;
#endif
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2011 Samsung Electronics Co.Ltd
* Authors:
* Seung-Woo Kim <sw0312.kim@samsung.com>
* Inki Dae <inki.dae@samsung.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 "drmP.h"
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include "exynos_drm_drv.h"
#include "exynos_hdmi.h"
static int hdmiphy_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
hdmi_attach_hdmiphy_client(client);
dev_info(&client->adapter->dev, "attached s5p_hdmiphy "
"into i2c adapter successfully\n");
return 0;
}
static int hdmiphy_remove(struct i2c_client *client)
{
dev_info(&client->adapter->dev, "detached s5p_hdmiphy "
"from i2c adapter successfully\n");
return 0;
}
static const struct i2c_device_id hdmiphy_id[] = {
{ "s5p_hdmiphy", 0 },
{ },
};
struct i2c_driver hdmiphy_driver = {
.driver = {
.name = "s5p-hdmiphy",
.owner = THIS_MODULE,
},
.id_table = hdmiphy_id,
.probe = hdmiphy_probe,
.remove = __devexit_p(hdmiphy_remove),
.command = NULL,
};
EXPORT_SYMBOL(hdmiphy_driver);
File diff suppressed because it is too large Load Diff
+561
View File
@@ -0,0 +1,561 @@
/*
*
* Cloned from drivers/media/video/s5p-tv/regs-hdmi.h
*
* Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* HDMI register header file for Samsung TVOUT driver
*
* 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.
*/
#ifndef SAMSUNG_REGS_HDMI_H
#define SAMSUNG_REGS_HDMI_H
/*
* Register part
*/
/* HDMI Version 1.3 & Common */
#define HDMI_CTRL_BASE(x) ((x) + 0x00000000)
#define HDMI_CORE_BASE(x) ((x) + 0x00010000)
#define HDMI_I2S_BASE(x) ((x) + 0x00040000)
#define HDMI_TG_BASE(x) ((x) + 0x00050000)
/* Control registers */
#define HDMI_INTC_CON HDMI_CTRL_BASE(0x0000)
#define HDMI_INTC_FLAG HDMI_CTRL_BASE(0x0004)
#define HDMI_HPD_STATUS HDMI_CTRL_BASE(0x000C)
#define HDMI_V13_PHY_RSTOUT HDMI_CTRL_BASE(0x0014)
#define HDMI_V13_PHY_VPLL HDMI_CTRL_BASE(0x0018)
#define HDMI_V13_PHY_CMU HDMI_CTRL_BASE(0x001C)
#define HDMI_V13_CORE_RSTOUT HDMI_CTRL_BASE(0x0020)
/* Core registers */
#define HDMI_CON_0 HDMI_CORE_BASE(0x0000)
#define HDMI_CON_1 HDMI_CORE_BASE(0x0004)
#define HDMI_CON_2 HDMI_CORE_BASE(0x0008)
#define HDMI_SYS_STATUS HDMI_CORE_BASE(0x0010)
#define HDMI_V13_PHY_STATUS HDMI_CORE_BASE(0x0014)
#define HDMI_STATUS_EN HDMI_CORE_BASE(0x0020)
#define HDMI_HPD HDMI_CORE_BASE(0x0030)
#define HDMI_MODE_SEL HDMI_CORE_BASE(0x0040)
#define HDMI_ENC_EN HDMI_CORE_BASE(0x0044)
#define HDMI_V13_BLUE_SCREEN_0 HDMI_CORE_BASE(0x0050)
#define HDMI_V13_BLUE_SCREEN_1 HDMI_CORE_BASE(0x0054)
#define HDMI_V13_BLUE_SCREEN_2 HDMI_CORE_BASE(0x0058)
#define HDMI_H_BLANK_0 HDMI_CORE_BASE(0x00A0)
#define HDMI_H_BLANK_1 HDMI_CORE_BASE(0x00A4)
#define HDMI_V13_V_BLANK_0 HDMI_CORE_BASE(0x00B0)
#define HDMI_V13_V_BLANK_1 HDMI_CORE_BASE(0x00B4)
#define HDMI_V13_V_BLANK_2 HDMI_CORE_BASE(0x00B8)
#define HDMI_V13_H_V_LINE_0 HDMI_CORE_BASE(0x00C0)
#define HDMI_V13_H_V_LINE_1 HDMI_CORE_BASE(0x00C4)
#define HDMI_V13_H_V_LINE_2 HDMI_CORE_BASE(0x00C8)
#define HDMI_VSYNC_POL HDMI_CORE_BASE(0x00E4)
#define HDMI_INT_PRO_MODE HDMI_CORE_BASE(0x00E8)
#define HDMI_V13_V_BLANK_F_0 HDMI_CORE_BASE(0x0110)
#define HDMI_V13_V_BLANK_F_1 HDMI_CORE_BASE(0x0114)
#define HDMI_V13_V_BLANK_F_2 HDMI_CORE_BASE(0x0118)
#define HDMI_V13_H_SYNC_GEN_0 HDMI_CORE_BASE(0x0120)
#define HDMI_V13_H_SYNC_GEN_1 HDMI_CORE_BASE(0x0124)
#define HDMI_V13_H_SYNC_GEN_2 HDMI_CORE_BASE(0x0128)
#define HDMI_V13_V_SYNC_GEN_1_0 HDMI_CORE_BASE(0x0130)
#define HDMI_V13_V_SYNC_GEN_1_1 HDMI_CORE_BASE(0x0134)
#define HDMI_V13_V_SYNC_GEN_1_2 HDMI_CORE_BASE(0x0138)
#define HDMI_V13_V_SYNC_GEN_2_0 HDMI_CORE_BASE(0x0140)
#define HDMI_V13_V_SYNC_GEN_2_1 HDMI_CORE_BASE(0x0144)
#define HDMI_V13_V_SYNC_GEN_2_2 HDMI_CORE_BASE(0x0148)
#define HDMI_V13_V_SYNC_GEN_3_0 HDMI_CORE_BASE(0x0150)
#define HDMI_V13_V_SYNC_GEN_3_1 HDMI_CORE_BASE(0x0154)
#define HDMI_V13_V_SYNC_GEN_3_2 HDMI_CORE_BASE(0x0158)
#define HDMI_V13_ACR_CON HDMI_CORE_BASE(0x0180)
#define HDMI_V13_AVI_CON HDMI_CORE_BASE(0x0300)
#define HDMI_V13_AVI_BYTE(n) HDMI_CORE_BASE(0x0320 + 4 * (n))
#define HDMI_V13_DC_CONTROL HDMI_CORE_BASE(0x05C0)
#define HDMI_V13_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x05C4)
#define HDMI_V13_HPD_GEN HDMI_CORE_BASE(0x05C8)
#define HDMI_V13_AUI_CON HDMI_CORE_BASE(0x0360)
#define HDMI_V13_SPD_CON HDMI_CORE_BASE(0x0400)
/* Timing generator registers */
#define HDMI_TG_CMD HDMI_TG_BASE(0x0000)
#define HDMI_TG_H_FSZ_L HDMI_TG_BASE(0x0018)
#define HDMI_TG_H_FSZ_H HDMI_TG_BASE(0x001C)
#define HDMI_TG_HACT_ST_L HDMI_TG_BASE(0x0020)
#define HDMI_TG_HACT_ST_H HDMI_TG_BASE(0x0024)
#define HDMI_TG_HACT_SZ_L HDMI_TG_BASE(0x0028)
#define HDMI_TG_HACT_SZ_H HDMI_TG_BASE(0x002C)
#define HDMI_TG_V_FSZ_L HDMI_TG_BASE(0x0030)
#define HDMI_TG_V_FSZ_H HDMI_TG_BASE(0x0034)
#define HDMI_TG_VSYNC_L HDMI_TG_BASE(0x0038)
#define HDMI_TG_VSYNC_H HDMI_TG_BASE(0x003C)
#define HDMI_TG_VSYNC2_L HDMI_TG_BASE(0x0040)
#define HDMI_TG_VSYNC2_H HDMI_TG_BASE(0x0044)
#define HDMI_TG_VACT_ST_L HDMI_TG_BASE(0x0048)
#define HDMI_TG_VACT_ST_H HDMI_TG_BASE(0x004C)
#define HDMI_TG_VACT_SZ_L HDMI_TG_BASE(0x0050)
#define HDMI_TG_VACT_SZ_H HDMI_TG_BASE(0x0054)
#define HDMI_TG_FIELD_CHG_L HDMI_TG_BASE(0x0058)
#define HDMI_TG_FIELD_CHG_H HDMI_TG_BASE(0x005C)
#define HDMI_TG_VACT_ST2_L HDMI_TG_BASE(0x0060)
#define HDMI_TG_VACT_ST2_H HDMI_TG_BASE(0x0064)
#define HDMI_TG_VSYNC_TOP_HDMI_L HDMI_TG_BASE(0x0078)
#define HDMI_TG_VSYNC_TOP_HDMI_H HDMI_TG_BASE(0x007C)
#define HDMI_TG_VSYNC_BOT_HDMI_L HDMI_TG_BASE(0x0080)
#define HDMI_TG_VSYNC_BOT_HDMI_H HDMI_TG_BASE(0x0084)
#define HDMI_TG_FIELD_TOP_HDMI_L HDMI_TG_BASE(0x0088)
#define HDMI_TG_FIELD_TOP_HDMI_H HDMI_TG_BASE(0x008C)
#define HDMI_TG_FIELD_BOT_HDMI_L HDMI_TG_BASE(0x0090)
#define HDMI_TG_FIELD_BOT_HDMI_H HDMI_TG_BASE(0x0094)
/*
* Bit definition part
*/
/* HDMI_INTC_CON */
#define HDMI_INTC_EN_GLOBAL (1 << 6)
#define HDMI_INTC_EN_HPD_PLUG (1 << 3)
#define HDMI_INTC_EN_HPD_UNPLUG (1 << 2)
/* HDMI_INTC_FLAG */
#define HDMI_INTC_FLAG_HPD_PLUG (1 << 3)
#define HDMI_INTC_FLAG_HPD_UNPLUG (1 << 2)
/* HDMI_PHY_RSTOUT */
#define HDMI_PHY_SW_RSTOUT (1 << 0)
/* HDMI_CORE_RSTOUT */
#define HDMI_CORE_SW_RSTOUT (1 << 0)
/* HDMI_CON_0 */
#define HDMI_BLUE_SCR_EN (1 << 5)
#define HDMI_ASP_EN (1 << 2)
#define HDMI_ASP_DIS (0 << 2)
#define HDMI_ASP_MASK (1 << 2)
#define HDMI_EN (1 << 0)
/* HDMI_PHY_STATUS */
#define HDMI_PHY_STATUS_READY (1 << 0)
/* HDMI_MODE_SEL */
#define HDMI_MODE_HDMI_EN (1 << 1)
#define HDMI_MODE_DVI_EN (1 << 0)
#define HDMI_DVI_MODE_EN (1)
#define HDMI_DVI_MODE_DIS (0)
#define HDMI_MODE_MASK (3 << 0)
/* HDMI_TG_CMD */
#define HDMI_TG_EN (1 << 0)
#define HDMI_FIELD_EN (1 << 1)
/* HDMI Version 1.4 */
/* Control registers */
/* #define HDMI_INTC_CON HDMI_CTRL_BASE(0x0000) */
/* #define HDMI_INTC_FLAG HDMI_CTRL_BASE(0x0004) */
#define HDMI_HDCP_KEY_LOAD HDMI_CTRL_BASE(0x0008)
/* #define HDMI_HPD_STATUS HDMI_CTRL_BASE(0x000C) */
#define HDMI_INTC_CON_1 HDMI_CTRL_BASE(0x0010)
#define HDMI_INTC_FLAG_1 HDMI_CTRL_BASE(0x0014)
#define HDMI_PHY_STATUS_0 HDMI_CTRL_BASE(0x0020)
#define HDMI_PHY_STATUS_CMU HDMI_CTRL_BASE(0x0024)
#define HDMI_PHY_STATUS_PLL HDMI_CTRL_BASE(0x0028)
#define HDMI_PHY_CON_0 HDMI_CTRL_BASE(0x0030)
#define HDMI_HPD_CTRL HDMI_CTRL_BASE(0x0040)
#define HDMI_HPD_ST HDMI_CTRL_BASE(0x0044)
#define HDMI_HPD_TH_X HDMI_CTRL_BASE(0x0050)
#define HDMI_AUDIO_CLKSEL HDMI_CTRL_BASE(0x0070)
#define HDMI_PHY_RSTOUT HDMI_CTRL_BASE(0x0074)
#define HDMI_PHY_VPLL HDMI_CTRL_BASE(0x0078)
#define HDMI_PHY_CMU HDMI_CTRL_BASE(0x007C)
#define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0080)
/* Video related registers */
#define HDMI_YMAX HDMI_CORE_BASE(0x0060)
#define HDMI_YMIN HDMI_CORE_BASE(0x0064)
#define HDMI_CMAX HDMI_CORE_BASE(0x0068)
#define HDMI_CMIN HDMI_CORE_BASE(0x006C)
#define HDMI_V2_BLANK_0 HDMI_CORE_BASE(0x00B0)
#define HDMI_V2_BLANK_1 HDMI_CORE_BASE(0x00B4)
#define HDMI_V1_BLANK_0 HDMI_CORE_BASE(0x00B8)
#define HDMI_V1_BLANK_1 HDMI_CORE_BASE(0x00BC)
#define HDMI_V_LINE_0 HDMI_CORE_BASE(0x00C0)
#define HDMI_V_LINE_1 HDMI_CORE_BASE(0x00C4)
#define HDMI_H_LINE_0 HDMI_CORE_BASE(0x00C8)
#define HDMI_H_LINE_1 HDMI_CORE_BASE(0x00CC)
#define HDMI_HSYNC_POL HDMI_CORE_BASE(0x00E0)
#define HDMI_V_BLANK_F0_0 HDMI_CORE_BASE(0x0110)
#define HDMI_V_BLANK_F0_1 HDMI_CORE_BASE(0x0114)
#define HDMI_V_BLANK_F1_0 HDMI_CORE_BASE(0x0118)
#define HDMI_V_BLANK_F1_1 HDMI_CORE_BASE(0x011C)
#define HDMI_H_SYNC_START_0 HDMI_CORE_BASE(0x0120)
#define HDMI_H_SYNC_START_1 HDMI_CORE_BASE(0x0124)
#define HDMI_H_SYNC_END_0 HDMI_CORE_BASE(0x0128)
#define HDMI_H_SYNC_END_1 HDMI_CORE_BASE(0x012C)
#define HDMI_V_SYNC_LINE_BEF_2_0 HDMI_CORE_BASE(0x0130)
#define HDMI_V_SYNC_LINE_BEF_2_1 HDMI_CORE_BASE(0x0134)
#define HDMI_V_SYNC_LINE_BEF_1_0 HDMI_CORE_BASE(0x0138)
#define HDMI_V_SYNC_LINE_BEF_1_1 HDMI_CORE_BASE(0x013C)
#define HDMI_V_SYNC_LINE_AFT_2_0 HDMI_CORE_BASE(0x0140)
#define HDMI_V_SYNC_LINE_AFT_2_1 HDMI_CORE_BASE(0x0144)
#define HDMI_V_SYNC_LINE_AFT_1_0 HDMI_CORE_BASE(0x0148)
#define HDMI_V_SYNC_LINE_AFT_1_1 HDMI_CORE_BASE(0x014C)
#define HDMI_V_SYNC_LINE_AFT_PXL_2_0 HDMI_CORE_BASE(0x0150)
#define HDMI_V_SYNC_LINE_AFT_PXL_2_1 HDMI_CORE_BASE(0x0154)
#define HDMI_V_SYNC_LINE_AFT_PXL_1_0 HDMI_CORE_BASE(0x0158)
#define HDMI_V_SYNC_LINE_AFT_PXL_1_1 HDMI_CORE_BASE(0x015C)
#define HDMI_V_BLANK_F2_0 HDMI_CORE_BASE(0x0160)
#define HDMI_V_BLANK_F2_1 HDMI_CORE_BASE(0x0164)
#define HDMI_V_BLANK_F3_0 HDMI_CORE_BASE(0x0168)
#define HDMI_V_BLANK_F3_1 HDMI_CORE_BASE(0x016C)
#define HDMI_V_BLANK_F4_0 HDMI_CORE_BASE(0x0170)
#define HDMI_V_BLANK_F4_1 HDMI_CORE_BASE(0x0174)
#define HDMI_V_BLANK_F5_0 HDMI_CORE_BASE(0x0178)
#define HDMI_V_BLANK_F5_1 HDMI_CORE_BASE(0x017C)
#define HDMI_V_SYNC_LINE_AFT_3_0 HDMI_CORE_BASE(0x0180)
#define HDMI_V_SYNC_LINE_AFT_3_1 HDMI_CORE_BASE(0x0184)
#define HDMI_V_SYNC_LINE_AFT_4_0 HDMI_CORE_BASE(0x0188)
#define HDMI_V_SYNC_LINE_AFT_4_1 HDMI_CORE_BASE(0x018C)
#define HDMI_V_SYNC_LINE_AFT_5_0 HDMI_CORE_BASE(0x0190)
#define HDMI_V_SYNC_LINE_AFT_5_1 HDMI_CORE_BASE(0x0194)
#define HDMI_V_SYNC_LINE_AFT_6_0 HDMI_CORE_BASE(0x0198)
#define HDMI_V_SYNC_LINE_AFT_6_1 HDMI_CORE_BASE(0x019C)
#define HDMI_V_SYNC_LINE_AFT_PXL_3_0 HDMI_CORE_BASE(0x01A0)
#define HDMI_V_SYNC_LINE_AFT_PXL_3_1 HDMI_CORE_BASE(0x01A4)
#define HDMI_V_SYNC_LINE_AFT_PXL_4_0 HDMI_CORE_BASE(0x01A8)
#define HDMI_V_SYNC_LINE_AFT_PXL_4_1 HDMI_CORE_BASE(0x01AC)
#define HDMI_V_SYNC_LINE_AFT_PXL_5_0 HDMI_CORE_BASE(0x01B0)
#define HDMI_V_SYNC_LINE_AFT_PXL_5_1 HDMI_CORE_BASE(0x01B4)
#define HDMI_V_SYNC_LINE_AFT_PXL_6_0 HDMI_CORE_BASE(0x01B8)
#define HDMI_V_SYNC_LINE_AFT_PXL_6_1 HDMI_CORE_BASE(0x01BC)
#define HDMI_VACT_SPACE_1_0 HDMI_CORE_BASE(0x01C0)
#define HDMI_VACT_SPACE_1_1 HDMI_CORE_BASE(0x01C4)
#define HDMI_VACT_SPACE_2_0 HDMI_CORE_BASE(0x01C8)
#define HDMI_VACT_SPACE_2_1 HDMI_CORE_BASE(0x01CC)
#define HDMI_VACT_SPACE_3_0 HDMI_CORE_BASE(0x01D0)
#define HDMI_VACT_SPACE_3_1 HDMI_CORE_BASE(0x01D4)
#define HDMI_VACT_SPACE_4_0 HDMI_CORE_BASE(0x01D8)
#define HDMI_VACT_SPACE_4_1 HDMI_CORE_BASE(0x01DC)
#define HDMI_VACT_SPACE_5_0 HDMI_CORE_BASE(0x01E0)
#define HDMI_VACT_SPACE_5_1 HDMI_CORE_BASE(0x01E4)
#define HDMI_VACT_SPACE_6_0 HDMI_CORE_BASE(0x01E8)
#define HDMI_VACT_SPACE_6_1 HDMI_CORE_BASE(0x01EC)
#define HDMI_GCP_CON HDMI_CORE_BASE(0x0200)
#define HDMI_GCP_BYTE1 HDMI_CORE_BASE(0x0210)
#define HDMI_GCP_BYTE2 HDMI_CORE_BASE(0x0214)
#define HDMI_GCP_BYTE3 HDMI_CORE_BASE(0x0218)
/* Audio related registers */
#define HDMI_ASP_CON HDMI_CORE_BASE(0x0300)
#define HDMI_ASP_SP_FLAT HDMI_CORE_BASE(0x0304)
#define HDMI_ASP_CHCFG0 HDMI_CORE_BASE(0x0310)
#define HDMI_ASP_CHCFG1 HDMI_CORE_BASE(0x0314)
#define HDMI_ASP_CHCFG2 HDMI_CORE_BASE(0x0318)
#define HDMI_ASP_CHCFG3 HDMI_CORE_BASE(0x031C)
#define HDMI_ACR_CON HDMI_CORE_BASE(0x0400)
#define HDMI_ACR_MCTS0 HDMI_CORE_BASE(0x0410)
#define HDMI_ACR_MCTS1 HDMI_CORE_BASE(0x0414)
#define HDMI_ACR_MCTS2 HDMI_CORE_BASE(0x0418)
#define HDMI_ACR_CTS0 HDMI_CORE_BASE(0x0420)
#define HDMI_ACR_CTS1 HDMI_CORE_BASE(0x0424)
#define HDMI_ACR_CTS2 HDMI_CORE_BASE(0x0428)
#define HDMI_ACR_N0 HDMI_CORE_BASE(0x0430)
#define HDMI_ACR_N1 HDMI_CORE_BASE(0x0434)
#define HDMI_ACR_N2 HDMI_CORE_BASE(0x0438)
/* Packet related registers */
#define HDMI_ACP_CON HDMI_CORE_BASE(0x0500)
#define HDMI_ACP_TYPE HDMI_CORE_BASE(0x0514)
#define HDMI_ACP_DATA(n) HDMI_CORE_BASE(0x0520 + 4 * (n))
#define HDMI_ISRC_CON HDMI_CORE_BASE(0x0600)
#define HDMI_ISRC1_HEADER1 HDMI_CORE_BASE(0x0614)
#define HDMI_ISRC1_DATA(n) HDMI_CORE_BASE(0x0620 + 4 * (n))
#define HDMI_ISRC2_DATA(n) HDMI_CORE_BASE(0x06A0 + 4 * (n))
#define HDMI_AVI_CON HDMI_CORE_BASE(0x0700)
#define HDMI_AVI_HEADER0 HDMI_CORE_BASE(0x0710)
#define HDMI_AVI_HEADER1 HDMI_CORE_BASE(0x0714)
#define HDMI_AVI_HEADER2 HDMI_CORE_BASE(0x0718)
#define HDMI_AVI_CHECK_SUM HDMI_CORE_BASE(0x071C)
#define HDMI_AVI_BYTE(n) HDMI_CORE_BASE(0x0720 + 4 * (n))
#define HDMI_AUI_CON HDMI_CORE_BASE(0x0800)
#define HDMI_AUI_HEADER0 HDMI_CORE_BASE(0x0810)
#define HDMI_AUI_HEADER1 HDMI_CORE_BASE(0x0814)
#define HDMI_AUI_HEADER2 HDMI_CORE_BASE(0x0818)
#define HDMI_AUI_CHECK_SUM HDMI_CORE_BASE(0x081C)
#define HDMI_AUI_BYTE(n) HDMI_CORE_BASE(0x0820 + 4 * (n))
#define HDMI_MPG_CON HDMI_CORE_BASE(0x0900)
#define HDMI_MPG_CHECK_SUM HDMI_CORE_BASE(0x091C)
#define HDMI_MPG_DATA(n) HDMI_CORE_BASE(0x0920 + 4 * (n))
#define HDMI_SPD_CON HDMI_CORE_BASE(0x0A00)
#define HDMI_SPD_HEADER0 HDMI_CORE_BASE(0x0A10)
#define HDMI_SPD_HEADER1 HDMI_CORE_BASE(0x0A14)
#define HDMI_SPD_HEADER2 HDMI_CORE_BASE(0x0A18)
#define HDMI_SPD_DATA(n) HDMI_CORE_BASE(0x0A20 + 4 * (n))
#define HDMI_GAMUT_CON HDMI_CORE_BASE(0x0B00)
#define HDMI_GAMUT_HEADER0 HDMI_CORE_BASE(0x0B10)
#define HDMI_GAMUT_HEADER1 HDMI_CORE_BASE(0x0B14)
#define HDMI_GAMUT_HEADER2 HDMI_CORE_BASE(0x0B18)
#define HDMI_GAMUT_METADATA(n) HDMI_CORE_BASE(0x0B20 + 4 * (n))
#define HDMI_VSI_CON HDMI_CORE_BASE(0x0C00)
#define HDMI_VSI_HEADER0 HDMI_CORE_BASE(0x0C10)
#define HDMI_VSI_HEADER1 HDMI_CORE_BASE(0x0C14)
#define HDMI_VSI_HEADER2 HDMI_CORE_BASE(0x0C18)
#define HDMI_VSI_DATA(n) HDMI_CORE_BASE(0x0C20 + 4 * (n))
#define HDMI_DC_CONTROL HDMI_CORE_BASE(0x0D00)
#define HDMI_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x0D04)
#define HDMI_AN_SEED_SEL HDMI_CORE_BASE(0x0E48)
#define HDMI_AN_SEED_0 HDMI_CORE_BASE(0x0E58)
#define HDMI_AN_SEED_1 HDMI_CORE_BASE(0x0E5C)
#define HDMI_AN_SEED_2 HDMI_CORE_BASE(0x0E60)
#define HDMI_AN_SEED_3 HDMI_CORE_BASE(0x0E64)
/* HDCP related registers */
#define HDMI_HDCP_SHA1(n) HDMI_CORE_BASE(0x7000 + 4 * (n))
#define HDMI_HDCP_KSV_LIST(n) HDMI_CORE_BASE(0x7050 + 4 * (n))
#define HDMI_HDCP_KSV_LIST_CON HDMI_CORE_BASE(0x7064)
#define HDMI_HDCP_SHA_RESULT HDMI_CORE_BASE(0x7070)
#define HDMI_HDCP_CTRL1 HDMI_CORE_BASE(0x7080)
#define HDMI_HDCP_CTRL2 HDMI_CORE_BASE(0x7084)
#define HDMI_HDCP_CHECK_RESULT HDMI_CORE_BASE(0x7090)
#define HDMI_HDCP_BKSV(n) HDMI_CORE_BASE(0x70A0 + 4 * (n))
#define HDMI_HDCP_AKSV(n) HDMI_CORE_BASE(0x70C0 + 4 * (n))
#define HDMI_HDCP_AN(n) HDMI_CORE_BASE(0x70E0 + 4 * (n))
#define HDMI_HDCP_BCAPS HDMI_CORE_BASE(0x7100)
#define HDMI_HDCP_BSTATUS_0 HDMI_CORE_BASE(0x7110)
#define HDMI_HDCP_BSTATUS_1 HDMI_CORE_BASE(0x7114)
#define HDMI_HDCP_RI_0 HDMI_CORE_BASE(0x7140)
#define HDMI_HDCP_RI_1 HDMI_CORE_BASE(0x7144)
#define HDMI_HDCP_I2C_INT HDMI_CORE_BASE(0x7180)
#define HDMI_HDCP_AN_INT HDMI_CORE_BASE(0x7190)
#define HDMI_HDCP_WDT_INT HDMI_CORE_BASE(0x71A0)
#define HDMI_HDCP_RI_INT HDMI_CORE_BASE(0x71B0)
#define HDMI_HDCP_RI_COMPARE_0 HDMI_CORE_BASE(0x71D0)
#define HDMI_HDCP_RI_COMPARE_1 HDMI_CORE_BASE(0x71D4)
#define HDMI_HDCP_FRAME_COUNT HDMI_CORE_BASE(0x71E0)
#define HDMI_RGB_ROUND_EN HDMI_CORE_BASE(0xD500)
#define HDMI_VACT_SPACE_R_0 HDMI_CORE_BASE(0xD504)
#define HDMI_VACT_SPACE_R_1 HDMI_CORE_BASE(0xD508)
#define HDMI_VACT_SPACE_G_0 HDMI_CORE_BASE(0xD50C)
#define HDMI_VACT_SPACE_G_1 HDMI_CORE_BASE(0xD510)
#define HDMI_VACT_SPACE_B_0 HDMI_CORE_BASE(0xD514)
#define HDMI_VACT_SPACE_B_1 HDMI_CORE_BASE(0xD518)
#define HDMI_BLUE_SCREEN_B_0 HDMI_CORE_BASE(0xD520)
#define HDMI_BLUE_SCREEN_B_1 HDMI_CORE_BASE(0xD524)
#define HDMI_BLUE_SCREEN_G_0 HDMI_CORE_BASE(0xD528)
#define HDMI_BLUE_SCREEN_G_1 HDMI_CORE_BASE(0xD52C)
#define HDMI_BLUE_SCREEN_R_0 HDMI_CORE_BASE(0xD530)
#define HDMI_BLUE_SCREEN_R_1 HDMI_CORE_BASE(0xD534)
/* HDMI I2S register */
#define HDMI_I2S_CLK_CON HDMI_I2S_BASE(0x000)
#define HDMI_I2S_CON_1 HDMI_I2S_BASE(0x004)
#define HDMI_I2S_CON_2 HDMI_I2S_BASE(0x008)
#define HDMI_I2S_PIN_SEL_0 HDMI_I2S_BASE(0x00c)
#define HDMI_I2S_PIN_SEL_1 HDMI_I2S_BASE(0x010)
#define HDMI_I2S_PIN_SEL_2 HDMI_I2S_BASE(0x014)
#define HDMI_I2S_PIN_SEL_3 HDMI_I2S_BASE(0x018)
#define HDMI_I2S_DSD_CON HDMI_I2S_BASE(0x01c)
#define HDMI_I2S_MUX_CON HDMI_I2S_BASE(0x020)
#define HDMI_I2S_CH_ST_CON HDMI_I2S_BASE(0x024)
#define HDMI_I2S_CH_ST_0 HDMI_I2S_BASE(0x028)
#define HDMI_I2S_CH_ST_1 HDMI_I2S_BASE(0x02c)
#define HDMI_I2S_CH_ST_2 HDMI_I2S_BASE(0x030)
#define HDMI_I2S_CH_ST_3 HDMI_I2S_BASE(0x034)
#define HDMI_I2S_CH_ST_4 HDMI_I2S_BASE(0x038)
#define HDMI_I2S_CH_ST_SH_0 HDMI_I2S_BASE(0x03c)
#define HDMI_I2S_CH_ST_SH_1 HDMI_I2S_BASE(0x040)
#define HDMI_I2S_CH_ST_SH_2 HDMI_I2S_BASE(0x044)
#define HDMI_I2S_CH_ST_SH_3 HDMI_I2S_BASE(0x048)
#define HDMI_I2S_CH_ST_SH_4 HDMI_I2S_BASE(0x04c)
#define HDMI_I2S_MUX_CH HDMI_I2S_BASE(0x054)
#define HDMI_I2S_MUX_CUV HDMI_I2S_BASE(0x058)
/* I2S bit definition */
/* I2S_CLK_CON */
#define HDMI_I2S_CLK_DIS (0)
#define HDMI_I2S_CLK_EN (1)
/* I2S_CON_1 */
#define HDMI_I2S_SCLK_FALLING_EDGE (0 << 1)
#define HDMI_I2S_SCLK_RISING_EDGE (1 << 1)
#define HDMI_I2S_L_CH_LOW_POL (0)
#define HDMI_I2S_L_CH_HIGH_POL (1)
/* I2S_CON_2 */
#define HDMI_I2S_MSB_FIRST_MODE (0 << 6)
#define HDMI_I2S_LSB_FIRST_MODE (1 << 6)
#define HDMI_I2S_BIT_CH_32FS (0 << 4)
#define HDMI_I2S_BIT_CH_48FS (1 << 4)
#define HDMI_I2S_BIT_CH_RESERVED (2 << 4)
#define HDMI_I2S_SDATA_16BIT (1 << 2)
#define HDMI_I2S_SDATA_20BIT (2 << 2)
#define HDMI_I2S_SDATA_24BIT (3 << 2)
#define HDMI_I2S_BASIC_FORMAT (0)
#define HDMI_I2S_L_JUST_FORMAT (2)
#define HDMI_I2S_R_JUST_FORMAT (3)
#define HDMI_I2S_CON_2_CLR (~(0xFF))
#define HDMI_I2S_SET_BIT_CH(x) (((x) & 0x7) << 4)
#define HDMI_I2S_SET_SDATA_BIT(x) (((x) & 0x7) << 2)
/* I2S_PIN_SEL_0 */
#define HDMI_I2S_SEL_SCLK(x) (((x) & 0x7) << 4)
#define HDMI_I2S_SEL_LRCK(x) ((x) & 0x7)
/* I2S_PIN_SEL_1 */
#define HDMI_I2S_SEL_SDATA1(x) (((x) & 0x7) << 4)
#define HDMI_I2S_SEL_SDATA2(x) ((x) & 0x7)
/* I2S_PIN_SEL_2 */
#define HDMI_I2S_SEL_SDATA3(x) (((x) & 0x7) << 4)
#define HDMI_I2S_SEL_SDATA2(x) ((x) & 0x7)
/* I2S_PIN_SEL_3 */
#define HDMI_I2S_SEL_DSD(x) ((x) & 0x7)
/* I2S_DSD_CON */
#define HDMI_I2S_DSD_CLK_RI_EDGE (1 << 1)
#define HDMI_I2S_DSD_CLK_FA_EDGE (0 << 1)
#define HDMI_I2S_DSD_ENABLE (1)
#define HDMI_I2S_DSD_DISABLE (0)
/* I2S_MUX_CON */
#define HDMI_I2S_NOISE_FILTER_ZERO (0 << 5)
#define HDMI_I2S_NOISE_FILTER_2_STAGE (1 << 5)
#define HDMI_I2S_NOISE_FILTER_3_STAGE (2 << 5)
#define HDMI_I2S_NOISE_FILTER_4_STAGE (3 << 5)
#define HDMI_I2S_NOISE_FILTER_5_STAGE (4 << 5)
#define HDMI_I2S_IN_DISABLE (1 << 4)
#define HDMI_I2S_IN_ENABLE (0 << 4)
#define HDMI_I2S_AUD_SPDIF (0 << 2)
#define HDMI_I2S_AUD_I2S (1 << 2)
#define HDMI_I2S_AUD_DSD (2 << 2)
#define HDMI_I2S_CUV_SPDIF_ENABLE (0 << 1)
#define HDMI_I2S_CUV_I2S_ENABLE (1 << 1)
#define HDMI_I2S_MUX_DISABLE (0)
#define HDMI_I2S_MUX_ENABLE (1)
#define HDMI_I2S_MUX_CON_CLR (~(0xFF))
/* I2S_CH_ST_CON */
#define HDMI_I2S_CH_STATUS_RELOAD (1)
#define HDMI_I2S_CH_ST_CON_CLR (~(1))
/* I2S_CH_ST_0 / I2S_CH_ST_SH_0 */
#define HDMI_I2S_CH_STATUS_MODE_0 (0 << 6)
#define HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH (0 << 3)
#define HDMI_I2S_2AUD_CH_WITH_PREEMPH (1 << 3)
#define HDMI_I2S_DEFAULT_EMPHASIS (0 << 3)
#define HDMI_I2S_COPYRIGHT (0 << 2)
#define HDMI_I2S_NO_COPYRIGHT (1 << 2)
#define HDMI_I2S_LINEAR_PCM (0 << 1)
#define HDMI_I2S_NO_LINEAR_PCM (1 << 1)
#define HDMI_I2S_CONSUMER_FORMAT (0)
#define HDMI_I2S_PROF_FORMAT (1)
#define HDMI_I2S_CH_ST_0_CLR (~(0xFF))
/* I2S_CH_ST_1 / I2S_CH_ST_SH_1 */
#define HDMI_I2S_CD_PLAYER (0x00)
#define HDMI_I2S_DAT_PLAYER (0x03)
#define HDMI_I2S_DCC_PLAYER (0x43)
#define HDMI_I2S_MINI_DISC_PLAYER (0x49)
/* I2S_CH_ST_2 / I2S_CH_ST_SH_2 */
#define HDMI_I2S_CHANNEL_NUM_MASK (0xF << 4)
#define HDMI_I2S_SOURCE_NUM_MASK (0xF)
#define HDMI_I2S_SET_CHANNEL_NUM(x) (((x) & (0xF)) << 4)
#define HDMI_I2S_SET_SOURCE_NUM(x) ((x) & (0xF))
/* I2S_CH_ST_3 / I2S_CH_ST_SH_3 */
#define HDMI_I2S_CLK_ACCUR_LEVEL_1 (1 << 4)
#define HDMI_I2S_CLK_ACCUR_LEVEL_2 (0 << 4)
#define HDMI_I2S_CLK_ACCUR_LEVEL_3 (2 << 4)
#define HDMI_I2S_SMP_FREQ_44_1 (0x0)
#define HDMI_I2S_SMP_FREQ_48 (0x2)
#define HDMI_I2S_SMP_FREQ_32 (0x3)
#define HDMI_I2S_SMP_FREQ_96 (0xA)
#define HDMI_I2S_SET_SMP_FREQ(x) ((x) & (0xF))
/* I2S_CH_ST_4 / I2S_CH_ST_SH_4 */
#define HDMI_I2S_ORG_SMP_FREQ_44_1 (0xF << 4)
#define HDMI_I2S_ORG_SMP_FREQ_88_2 (0x7 << 4)
#define HDMI_I2S_ORG_SMP_FREQ_22_05 (0xB << 4)
#define HDMI_I2S_ORG_SMP_FREQ_176_4 (0x3 << 4)
#define HDMI_I2S_WORD_LEN_NOT_DEFINE (0x0 << 1)
#define HDMI_I2S_WORD_LEN_MAX24_20BITS (0x1 << 1)
#define HDMI_I2S_WORD_LEN_MAX24_22BITS (0x2 << 1)
#define HDMI_I2S_WORD_LEN_MAX24_23BITS (0x4 << 1)
#define HDMI_I2S_WORD_LEN_MAX24_24BITS (0x5 << 1)
#define HDMI_I2S_WORD_LEN_MAX24_21BITS (0x6 << 1)
#define HDMI_I2S_WORD_LEN_MAX20_16BITS (0x1 << 1)
#define HDMI_I2S_WORD_LEN_MAX20_18BITS (0x2 << 1)
#define HDMI_I2S_WORD_LEN_MAX20_19BITS (0x4 << 1)
#define HDMI_I2S_WORD_LEN_MAX20_20BITS (0x5 << 1)
#define HDMI_I2S_WORD_LEN_MAX20_17BITS (0x6 << 1)
#define HDMI_I2S_WORD_LEN_MAX_24BITS (1)
#define HDMI_I2S_WORD_LEN_MAX_20BITS (0)
/* I2S_MUX_CH */
#define HDMI_I2S_CH3_R_EN (1 << 7)
#define HDMI_I2S_CH3_L_EN (1 << 6)
#define HDMI_I2S_CH3_EN (3 << 6)
#define HDMI_I2S_CH2_R_EN (1 << 5)
#define HDMI_I2S_CH2_L_EN (1 << 4)
#define HDMI_I2S_CH2_EN (3 << 4)
#define HDMI_I2S_CH1_R_EN (1 << 3)
#define HDMI_I2S_CH1_L_EN (1 << 2)
#define HDMI_I2S_CH1_EN (3 << 2)
#define HDMI_I2S_CH0_R_EN (1 << 1)
#define HDMI_I2S_CH0_L_EN (1)
#define HDMI_I2S_CH0_EN (3)
#define HDMI_I2S_CH_ALL_EN (0xFF)
#define HDMI_I2S_MUX_CH_CLR (~HDMI_I2S_CH_ALL_EN)
/* I2S_MUX_CUV */
#define HDMI_I2S_CUV_R_EN (1 << 1)
#define HDMI_I2S_CUV_L_EN (1)
#define HDMI_I2S_CUV_RL_EN (0x03)
/* I2S_CUV_L_R */
#define HDMI_I2S_CUV_R_DATA_MASK (0x7 << 4)
#define HDMI_I2S_CUV_L_DATA_MASK (0x7)
/* Timing generator registers */
/* TG configure/status registers */
#define HDMI_TG_VACT_ST3_L HDMI_TG_BASE(0x0068)
#define HDMI_TG_VACT_ST3_H HDMI_TG_BASE(0x006c)
#define HDMI_TG_VACT_ST4_L HDMI_TG_BASE(0x0070)
#define HDMI_TG_VACT_ST4_H HDMI_TG_BASE(0x0074)
#define HDMI_TG_3D HDMI_TG_BASE(0x00F0)
#endif /* SAMSUNG_REGS_HDMI_H */
+141
View File
@@ -0,0 +1,141 @@
/*
*
* Cloned from drivers/media/video/s5p-tv/regs-mixer.h
*
* Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* Mixer register header file for Samsung Mixer driver
*
* 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.
*/
#ifndef SAMSUNG_REGS_MIXER_H
#define SAMSUNG_REGS_MIXER_H
/*
* Register part
*/
#define MXR_STATUS 0x0000
#define MXR_CFG 0x0004
#define MXR_INT_EN 0x0008
#define MXR_INT_STATUS 0x000C
#define MXR_LAYER_CFG 0x0010
#define MXR_VIDEO_CFG 0x0014
#define MXR_GRAPHIC0_CFG 0x0020
#define MXR_GRAPHIC0_BASE 0x0024
#define MXR_GRAPHIC0_SPAN 0x0028
#define MXR_GRAPHIC0_SXY 0x002C
#define MXR_GRAPHIC0_WH 0x0030
#define MXR_GRAPHIC0_DXY 0x0034
#define MXR_GRAPHIC0_BLANK 0x0038
#define MXR_GRAPHIC1_CFG 0x0040
#define MXR_GRAPHIC1_BASE 0x0044
#define MXR_GRAPHIC1_SPAN 0x0048
#define MXR_GRAPHIC1_SXY 0x004C
#define MXR_GRAPHIC1_WH 0x0050
#define MXR_GRAPHIC1_DXY 0x0054
#define MXR_GRAPHIC1_BLANK 0x0058
#define MXR_BG_CFG 0x0060
#define MXR_BG_COLOR0 0x0064
#define MXR_BG_COLOR1 0x0068
#define MXR_BG_COLOR2 0x006C
#define MXR_CM_COEFF_Y 0x0080
#define MXR_CM_COEFF_CB 0x0084
#define MXR_CM_COEFF_CR 0x0088
#define MXR_GRAPHIC0_BASE_S 0x2024
#define MXR_GRAPHIC1_BASE_S 0x2044
/* for parametrized access to layer registers */
#define MXR_GRAPHIC_CFG(i) (0x0020 + (i) * 0x20)
#define MXR_GRAPHIC_BASE(i) (0x0024 + (i) * 0x20)
#define MXR_GRAPHIC_SPAN(i) (0x0028 + (i) * 0x20)
#define MXR_GRAPHIC_SXY(i) (0x002C + (i) * 0x20)
#define MXR_GRAPHIC_WH(i) (0x0030 + (i) * 0x20)
#define MXR_GRAPHIC_DXY(i) (0x0034 + (i) * 0x20)
#define MXR_GRAPHIC_BLANK(i) (0x0038 + (i) * 0x20)
#define MXR_GRAPHIC_BASE_S(i) (0x2024 + (i) * 0x20)
/*
* Bit definition part
*/
/* generates mask for range of bits */
#define MXR_MASK(high_bit, low_bit) \
(((2 << ((high_bit) - (low_bit))) - 1) << (low_bit))
#define MXR_MASK_VAL(val, high_bit, low_bit) \
(((val) << (low_bit)) & MXR_MASK(high_bit, low_bit))
/* bits for MXR_STATUS */
#define MXR_STATUS_16_BURST (1 << 7)
#define MXR_STATUS_BURST_MASK (1 << 7)
#define MXR_STATUS_BIG_ENDIAN (1 << 3)
#define MXR_STATUS_ENDIAN_MASK (1 << 3)
#define MXR_STATUS_SYNC_ENABLE (1 << 2)
#define MXR_STATUS_REG_RUN (1 << 0)
/* bits for MXR_CFG */
#define MXR_CFG_RGB601_0_255 (0 << 9)
#define MXR_CFG_RGB601_16_235 (1 << 9)
#define MXR_CFG_RGB709_0_255 (2 << 9)
#define MXR_CFG_RGB709_16_235 (3 << 9)
#define MXR_CFG_RGB_FMT_MASK 0x600
#define MXR_CFG_OUT_YUV444 (0 << 8)
#define MXR_CFG_OUT_RGB888 (1 << 8)
#define MXR_CFG_OUT_MASK (1 << 8)
#define MXR_CFG_DST_SDO (0 << 7)
#define MXR_CFG_DST_HDMI (1 << 7)
#define MXR_CFG_DST_MASK (1 << 7)
#define MXR_CFG_SCAN_HD_720 (0 << 6)
#define MXR_CFG_SCAN_HD_1080 (1 << 6)
#define MXR_CFG_GRP1_ENABLE (1 << 5)
#define MXR_CFG_GRP0_ENABLE (1 << 4)
#define MXR_CFG_VP_ENABLE (1 << 3)
#define MXR_CFG_SCAN_INTERLACE (0 << 2)
#define MXR_CFG_SCAN_PROGRASSIVE (1 << 2)
#define MXR_CFG_SCAN_NTSC (0 << 1)
#define MXR_CFG_SCAN_PAL (1 << 1)
#define MXR_CFG_SCAN_SD (0 << 0)
#define MXR_CFG_SCAN_HD (1 << 0)
#define MXR_CFG_SCAN_MASK 0x47
/* bits for MXR_GRAPHICn_CFG */
#define MXR_GRP_CFG_COLOR_KEY_DISABLE (1 << 21)
#define MXR_GRP_CFG_BLEND_PRE_MUL (1 << 20)
#define MXR_GRP_CFG_WIN_BLEND_EN (1 << 17)
#define MXR_GRP_CFG_PIXEL_BLEND_EN (1 << 16)
#define MXR_GRP_CFG_FORMAT_VAL(x) MXR_MASK_VAL(x, 11, 8)
#define MXR_GRP_CFG_FORMAT_MASK MXR_GRP_CFG_FORMAT_VAL(~0)
#define MXR_GRP_CFG_ALPHA_VAL(x) MXR_MASK_VAL(x, 7, 0)
/* bits for MXR_GRAPHICn_WH */
#define MXR_GRP_WH_H_SCALE(x) MXR_MASK_VAL(x, 28, 28)
#define MXR_GRP_WH_V_SCALE(x) MXR_MASK_VAL(x, 12, 12)
#define MXR_GRP_WH_WIDTH(x) MXR_MASK_VAL(x, 26, 16)
#define MXR_GRP_WH_HEIGHT(x) MXR_MASK_VAL(x, 10, 0)
/* bits for MXR_GRAPHICn_SXY */
#define MXR_GRP_SXY_SX(x) MXR_MASK_VAL(x, 26, 16)
#define MXR_GRP_SXY_SY(x) MXR_MASK_VAL(x, 10, 0)
/* bits for MXR_GRAPHICn_DXY */
#define MXR_GRP_DXY_DX(x) MXR_MASK_VAL(x, 26, 16)
#define MXR_GRP_DXY_DY(x) MXR_MASK_VAL(x, 10, 0)
/* bits for MXR_INT_EN */
#define MXR_INT_EN_VSYNC (1 << 11)
#define MXR_INT_EN_ALL (0x0f << 8)
/* bit for MXR_INT_STATUS */
#define MXR_INT_CLEAR_VSYNC (1 << 11)
#define MXR_INT_STATUS_VSYNC (1 << 0)
/* bit for MXR_LAYER_CFG */
#define MXR_LAYER_CFG_GRP1_VAL(x) MXR_MASK_VAL(x, 11, 8)
#define MXR_LAYER_CFG_GRP0_VAL(x) MXR_MASK_VAL(x, 7, 4)
#define MXR_LAYER_CFG_VP_VAL(x) MXR_MASK_VAL(x, 3, 0)
#endif /* SAMSUNG_REGS_MIXER_H */
+91
View File
@@ -0,0 +1,91 @@
/*
*
* Cloned from drivers/media/video/s5p-tv/regs-vp.h
*
* Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* Video processor register header file for Samsung Mixer driver
*
* 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.
*/
#ifndef SAMSUNG_REGS_VP_H
#define SAMSUNG_REGS_VP_H
/*
* Register part
*/
#define VP_ENABLE 0x0000
#define VP_SRESET 0x0004
#define VP_SHADOW_UPDATE 0x0008
#define VP_FIELD_ID 0x000C
#define VP_MODE 0x0010
#define VP_IMG_SIZE_Y 0x0014
#define VP_IMG_SIZE_C 0x0018
#define VP_PER_RATE_CTRL 0x001C
#define VP_TOP_Y_PTR 0x0028
#define VP_BOT_Y_PTR 0x002C
#define VP_TOP_C_PTR 0x0030
#define VP_BOT_C_PTR 0x0034
#define VP_ENDIAN_MODE 0x03CC
#define VP_SRC_H_POSITION 0x0044
#define VP_SRC_V_POSITION 0x0048
#define VP_SRC_WIDTH 0x004C
#define VP_SRC_HEIGHT 0x0050
#define VP_DST_H_POSITION 0x0054
#define VP_DST_V_POSITION 0x0058
#define VP_DST_WIDTH 0x005C
#define VP_DST_HEIGHT 0x0060
#define VP_H_RATIO 0x0064
#define VP_V_RATIO 0x0068
#define VP_POLY8_Y0_LL 0x006C
#define VP_POLY4_Y0_LL 0x00EC
#define VP_POLY4_C0_LL 0x012C
/*
* Bit definition part
*/
/* generates mask for range of bits */
#define VP_MASK(high_bit, low_bit) \
(((2 << ((high_bit) - (low_bit))) - 1) << (low_bit))
#define VP_MASK_VAL(val, high_bit, low_bit) \
(((val) << (low_bit)) & VP_MASK(high_bit, low_bit))
/* VP_ENABLE */
#define VP_ENABLE_ON (1 << 0)
/* VP_SRESET */
#define VP_SRESET_PROCESSING (1 << 0)
/* VP_SHADOW_UPDATE */
#define VP_SHADOW_UPDATE_ENABLE (1 << 0)
/* VP_MODE */
#define VP_MODE_NV12 (0 << 6)
#define VP_MODE_NV21 (1 << 6)
#define VP_MODE_LINE_SKIP (1 << 5)
#define VP_MODE_MEM_LINEAR (0 << 4)
#define VP_MODE_MEM_TILED (1 << 4)
#define VP_MODE_FMT_MASK (5 << 4)
#define VP_MODE_FIELD_ID_AUTO_TOGGLING (1 << 2)
#define VP_MODE_2D_IPC (1 << 1)
/* VP_IMG_SIZE_Y */
/* VP_IMG_SIZE_C */
#define VP_IMG_HSIZE(x) VP_MASK_VAL(x, 29, 16)
#define VP_IMG_VSIZE(x) VP_MASK_VAL(x, 13, 0)
/* VP_SRC_H_POSITION */
#define VP_SRC_H_POSITION_VAL(x) VP_MASK_VAL(x, 14, 4)
/* VP_ENDIAN_MODE */
#define VP_ENDIAN_MODE_LITTLE (1 << 0)
#endif /* SAMSUNG_REGS_VP_H */