diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-22 13:08:22 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-22 13:08:22 -0700 |
commit | be53bfdb8088e9d1924199cc1a96e113756b1075 (patch) | |
tree | 8c65eb9d82ca4c0f11c17cfdc44d5263820b415b /drivers/gpu/drm/exynos | |
parent | b2094ef840697bc8ca5d17a83b7e30fad5f1e9fa (diff) | |
parent | 5466c7b1683a23dbbcfb7ee4a71c4f23886001c7 (diff) |
Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
Pull drm main changes from Dave Airlie:
"This is the main drm pull request, I'm probably going to send two more
smaller ones, will explain below.
This contains a patch that is also in the fbdev tree, but it should be
the same patch, it added an API for hot unplugging framebuffer
devices, and I need that API for a new driver.
It also contains some changes to the i2c tree which Jean has acked,
and one change to moorestown platform stuff in x86.
Highlights:
- new drivers: UDL driver for USB displaylink devices, kms only,
should support correct hotplug operations.
- core: i2c speedups + better hotplug support, EDID overriding via
firmware interface - allows user to load a firmware for a broken
monitor/kvm from userspace, it even has documentation for it.
- exynos: new HDMI audio + hdmi 1.4 + virtual output driver
- gma500: code cleanup
- radeon: cleanups, CS optimisations, streamout support and pageflip
fix
- nouveau: NVD9 displayport support + more reclocking work
- i915: re-enabling GMBUS, finish gpu patch (might help hibernation
who knows), missed irq fixes, stencil tiling fixes, interlaced
support, aliasesd PPGTT support for SNB/IVB, swizzling for SNB/IVB,
semaphore fixes
As well as the usual bunch of cleanups and fixes all over the place.
I've got two things I'd like to merge a bit later:
a) AMD support for all their new radeonhd 7000 series GPU and APUs.
AMD dropped this a bit late due to insane internal review
processes, (please AMD just follow Intel and let open source guys
ship stuff early) however I don't want to penalise people who own
this hardware (since its been on sale for 3-4 months and GPU hw
doesn't exactly have a lifetime in years) and consign them to
using closed drivers for longer than necessary. The changes are
well contained and just plug into the driver new gpu functionality
so they should be fairly regression proof. I just want to give
them a bit of a run on the hw AMD kindly sent me.
b) drm prime/dma-buf interface code. This is just infrastructure
code to expose the dma-buf stuff to drm drivers and to userspace.
I'm not planning on pushing any driver support in this cycle
(except maybe exynos), but I'd like to get the infrastructure code
in so for the next cycle I can start getting the driver support
into the individual drivers. We have started driver support for
i915, nouveau and udl along with I think exynos and omap in
staging. However this code relies on the dma-buf tree being
pulled into your tree first since it needs the latest interfaces
from that tree. I'll push to get that tree sent asap.
(oh and any warnings you see in i915 are gcc's fault from what anyone
can see)."
Fix up trivial conflicts in arch/x86/platform/mrst/mrst.c due to the new
msic_thermal_platform_data() thermal function being added next to the
tc35876x_platform_data() i2c device function..
* 'drm-next' of git://people.freedesktop.org/~airlied/linux: (326 commits)
drm/i915: use DDC_ADDR instead of hard-coding it
drm/radeon: use DDC_ADDR instead of hard-coding it
drm: remove unneeded redefinition of DDC_ADDR
drm/exynos: added virtual display driver.
drm: allow loading an EDID as firmware to override broken monitor
drm/exynos: enable hdmi audio feature
drm/exynos: add default pixel format for plane
drm/exynos: cleanup exynos_hdmi.h
drm/exynos: add is_local member in exynos_drm_subdrv struct
drm/exynos: add subdrv open/close functions
drm/exynos: remove module of exynos drm subdrv
drm/exynos: release pending pageflip events when closed
drm/exynos: added new funtion to get/put dma address.
drm/exynos: update gem and buffer framework.
drm/exynos: added mode_fixup feature and code clean.
drm/exynos: add HDMI version 1.4 support
drm/exynos: remove exynos_mixer.h
gma500: Fix mmap frambuffer
drm/radeon: Drop radeon_gem_object_(un)pin.
drm/radeon: Restrict offset for legacy display engine.
...
Diffstat (limited to 'drivers/gpu/drm/exynos')
26 files changed, 3339 insertions, 716 deletions
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index b9e5266c341b..3343ac437fe5 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -1,7 +1,6 @@ config DRM_EXYNOS tristate "DRM Support for Samsung SoC EXYNOS Series" depends on DRM && PLAT_SAMSUNG - default n select DRM_KMS_HELPER select FB_CFB_FILLRECT select FB_CFB_COPYAREA @@ -12,16 +11,19 @@ config DRM_EXYNOS If M is selected the module will be called exynosdrm. config DRM_EXYNOS_FIMD - tristate "Exynos DRM FIMD" + bool "Exynos DRM FIMD" depends on DRM_EXYNOS && !FB_S3C - default n help Choose this option if you want to use Exynos FIMD for DRM. - If M is selected, the module will be called exynos_drm_fimd config DRM_EXYNOS_HDMI - tristate "Exynos DRM 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. - If M is selected, the module will be called exynos_drm_hdmi + +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. diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index 395e69c9a96e..9e0bff8badf9 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile @@ -8,7 +8,10 @@ exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \ exynos_drm_buf.o exynos_drm_gem.o exynos_drm_core.o \ exynos_drm_plane.o -obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o -obj-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o -obj-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o exynos_ddc.o \ - exynos_hdmiphy.o exynos_drm_hdmi.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 diff --git a/drivers/gpu/drm/exynos/exynos_ddc.c b/drivers/gpu/drm/exynos/exynos_ddc.c index 84b614fe26fd..7e1051d07f1f 100644 --- a/drivers/gpu/drm/exynos/exynos_ddc.c +++ b/drivers/gpu/drm/exynos/exynos_ddc.c @@ -55,4 +55,3 @@ struct i2c_driver ddc_driver = { .remove = __devexit_p(s5p_ddc_remove), .command = NULL, }; -EXPORT_SYMBOL(ddc_driver); diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.c b/drivers/gpu/drm/exynos/exynos_drm_buf.c index 3cf785c58186..4a3a5f72ed4a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_buf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_buf.c @@ -25,45 +25,161 @@ #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, - struct exynos_drm_gem_buf *buffer) + unsigned int flags, struct exynos_drm_gem_buf *buf) { + dma_addr_t start_addr, end_addr; + unsigned int npages, page_size, i = 0; + struct scatterlist *sgl; + int ret = 0; + DRM_DEBUG_KMS("%s\n", __FILE__); - buffer->kvaddr = dma_alloc_writecombine(dev->dev, buffer->size, - &buffer->dma_addr, GFP_KERNEL); - if (!buffer->kvaddr) { - DRM_ERROR("failed to allocate buffer.\n"); + if (flags & EXYNOS_BO_NONCONTIG) { + 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) + 1; + page_size = SECTION_SIZE; + } else if (buf->size >= SZ_64K) { + npages = (buf->size >> 16) + 1; + page_size = SZ_64K; + } else { + npages = (buf->size >> PAGE_SHIFT) + 1; + 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; } - DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n", - (unsigned long)buffer->kvaddr, - (unsigned long)buffer->dma_addr, - buffer->size); + 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; + } - return 0; + 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; + } + + start_addr = buf->dma_addr; + end_addr = buf->dma_addr + buf->size; + + buf->pages = kzalloc(sizeof(struct page) * npages, GFP_KERNEL); + if (!buf->pages) { + DRM_ERROR("failed to allocate pages.\n"); + ret = -ENOMEM; + goto err2; + } + + start_addr = buf->dma_addr; + end_addr = buf->dma_addr + buf->size; + + 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; + + 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; + if (end_addr - start_addr < page_size) + break; + sgl = sg_next(sgl); + i++; + } + + buf->pages[i] = phys_to_page(start_addr); + + sgl = sg_next(sgl); + sg_set_page(sgl, buf->pages[i+1], end_addr - start_addr, 0); + + 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, - struct exynos_drm_gem_buf *buffer) + unsigned int flags, struct exynos_drm_gem_buf *buf) { DRM_DEBUG_KMS("%s.\n", __FILE__); - if (buffer->dma_addr && buffer->size) - dma_free_writecombine(dev->dev, buffer->size, buffer->kvaddr, - (dma_addr_t)buffer->dma_addr); - else - DRM_DEBUG_KMS("buffer data are invalid.\n"); + /* + * release only physically continuous memory and + * non-continuous memory would be released by exynos + * gem framework. + */ + if (flags & EXYNOS_BO_NONCONTIG) { + 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_buf_create(struct drm_device *dev, - unsigned int size) +struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev, + unsigned int size) { struct exynos_drm_gem_buf *buffer; @@ -77,21 +193,11 @@ struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev, } buffer->size = size; - - /* - * allocate memory region with size and set the memory information - * to vaddr and dma_addr of a buffer object. - */ - if (lowlevel_buffer_allocate(dev, buffer) < 0) { - kfree(buffer); - return NULL; - } - return buffer; } -void exynos_drm_buf_destroy(struct drm_device *dev, - struct exynos_drm_gem_buf *buffer) +void exynos_drm_fini_buf(struct drm_device *dev, + struct exynos_drm_gem_buf *buffer) { DRM_DEBUG_KMS("%s.\n", __FILE__); @@ -100,12 +206,27 @@ void exynos_drm_buf_destroy(struct drm_device *dev, return; } - lowlevel_buffer_deallocate(dev, buffer); - kfree(buffer); buffer = NULL; } -MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); -MODULE_DESCRIPTION("Samsung SoC DRM Buffer Management Module"); -MODULE_LICENSE("GPL"); +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); +} diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.h b/drivers/gpu/drm/exynos/exynos_drm_buf.h index c913f2bad760..3388e4eb4ba2 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_buf.h +++ b/drivers/gpu/drm/exynos/exynos_drm_buf.h @@ -26,12 +26,22 @@ #ifndef _EXYNOS_DRM_BUF_H_ #define _EXYNOS_DRM_BUF_H_ -/* allocate physical memory. */ -struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev, - unsigned int size); +/* create and initialize buffer object. */ +struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev, + unsigned int size); -/* remove allocated physical memory. */ -void exynos_drm_buf_destroy(struct drm_device *dev, - struct exynos_drm_gem_buf *buffer); +/* 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 diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c index 99d5527b2ca6..bf791fa0e50d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_connector.c +++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c @@ -225,6 +225,29 @@ static struct drm_connector_helper_funcs exynos_connector_helper_funcs = { .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) @@ -262,7 +285,7 @@ static void exynos_drm_connector_destroy(struct drm_connector *connector) static struct drm_connector_funcs exynos_connector_funcs = { .dpms = drm_helper_connector_dpms, - .fill_modes = drm_helper_probe_single_connector_modes, + .fill_modes = exynos_drm_connector_fill_modes, .detect = exynos_drm_connector_detect, .destroy = exynos_drm_connector_destroy, }; @@ -292,6 +315,10 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev, 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; @@ -325,9 +352,3 @@ err_connector: kfree(exynos_connector); return NULL; } - -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 Connector Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c index d08a55896d50..411832e8e17a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_core.c +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c @@ -32,7 +32,6 @@ #include "exynos_drm_connector.h" #include "exynos_drm_fbdev.h" -static DEFINE_MUTEX(exynos_drm_mutex); static LIST_HEAD(exynos_drm_subdrv_list); static struct drm_device *drm_dev; @@ -60,6 +59,9 @@ static int exynos_drm_subdrv_probe(struct drm_device *dev, return ret; } + if (subdrv->is_local) + return 0; + /* create and initialize a encoder for this sub driver. */ encoder = exynos_drm_encoder_create(dev, &subdrv->manager, (1 << MAX_CRTC) - 1); @@ -116,13 +118,10 @@ int exynos_drm_device_register(struct drm_device *dev) if (!dev) return -EINVAL; - if (drm_dev) { - DRM_ERROR("Already drm device were registered\n"); - return -EBUSY; - } + drm_dev = dev; - mutex_lock(&exynos_drm_mutex); 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"); @@ -130,9 +129,6 @@ int exynos_drm_device_register(struct drm_device *dev) } } - drm_dev = dev; - mutex_unlock(&exynos_drm_mutex); - return 0; } EXPORT_SYMBOL_GPL(exynos_drm_device_register); @@ -143,86 +139,28 @@ int exynos_drm_device_unregister(struct drm_device *dev) DRM_DEBUG_DRIVER("%s\n", __FILE__); - if (!dev || dev != drm_dev) { + if (!dev) { WARN(1, "Unexpected drm device unregister!\n"); return -EINVAL; } - mutex_lock(&exynos_drm_mutex); list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) exynos_drm_subdrv_remove(dev, subdrv); drm_dev = NULL; - mutex_unlock(&exynos_drm_mutex); return 0; } EXPORT_SYMBOL_GPL(exynos_drm_device_unregister); -static int exynos_drm_mode_group_reinit(struct drm_device *dev) -{ - struct drm_mode_group *group = &dev->primary->mode_group; - uint32_t *id_list = group->id_list; - int ret; - - DRM_DEBUG_DRIVER("%s\n", __FILE__); - - ret = drm_mode_group_init_legacy_group(dev, group); - if (ret < 0) - return ret; - - kfree(id_list); - return 0; -} - int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv) { - int err; - DRM_DEBUG_DRIVER("%s\n", __FILE__); if (!subdrv) return -EINVAL; - mutex_lock(&exynos_drm_mutex); - if (drm_dev) { - err = exynos_drm_subdrv_probe(drm_dev, subdrv); - if (err) { - DRM_ERROR("failed to probe exynos drm subdrv\n"); - mutex_unlock(&exynos_drm_mutex); - return err; - } - - /* setup possible_clones. */ - exynos_drm_encoder_setup(drm_dev); - - /* - * if any specific driver such as fimd or hdmi driver called - * exynos_drm_subdrv_register() later than drm_load(), - * the fb helper should be re-initialized and re-configured. - */ - err = exynos_drm_fbdev_reinit(drm_dev); - if (err) { - DRM_ERROR("failed to reinitialize exynos drm fbdev\n"); - exynos_drm_subdrv_remove(drm_dev, subdrv); - mutex_unlock(&exynos_drm_mutex); - return err; - } - - err = exynos_drm_mode_group_reinit(drm_dev); - if (err) { - DRM_ERROR("failed to reinitialize mode group\n"); - exynos_drm_fbdev_fini(drm_dev); - exynos_drm_subdrv_remove(drm_dev, subdrv); - mutex_unlock(&exynos_drm_mutex); - return err; - } - } - - subdrv->drm_dev = drm_dev; - list_add_tail(&subdrv->list, &exynos_drm_subdrv_list); - mutex_unlock(&exynos_drm_mutex); return 0; } @@ -230,46 +168,48 @@ EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register); int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv) { - int ret = -EFAULT; - DRM_DEBUG_DRIVER("%s\n", __FILE__); - if (!subdrv) { - DRM_DEBUG("Unexpected exynos drm subdrv unregister!\n"); - return ret; - } + if (!subdrv) + return -EINVAL; - mutex_lock(&exynos_drm_mutex); - if (drm_dev) { - exynos_drm_subdrv_remove(drm_dev, subdrv); - list_del(&subdrv->list); + list_del(&subdrv->list); - /* - * fb helper should be updated once a sub driver is released - * to re-configure crtc and connector and also to re-setup - * drm framebuffer. - */ - ret = exynos_drm_fbdev_reinit(drm_dev); - if (ret < 0) { - DRM_ERROR("failed fb helper reinit.\n"); - goto fail; - } + return 0; +} +EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister); - ret = exynos_drm_mode_group_reinit(drm_dev); - if (ret < 0) { - DRM_ERROR("failed drm mode group reinit.\n"); - goto fail; +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->manager.dev, file); + if (ret) + goto err; } } -fail: - mutex_unlock(&exynos_drm_mutex); + return 0; + +err: + list_for_each_entry_reverse(subdrv, &subdrv->list, list) { + if (subdrv->close) + subdrv->close(dev, subdrv->manager.dev, file); + } return ret; } -EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister); +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; -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 Core Driver"); -MODULE_LICENSE("GPL"); + list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) { + if (subdrv->close) + subdrv->close(dev, subdrv->manager.dev, file); + } +} +EXPORT_SYMBOL_GPL(exynos_drm_subdrv_close); diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index de818831a511..3486ffed0bf0 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -249,7 +249,11 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, { DRM_DEBUG_KMS("%s\n", __FILE__); - mode = adjusted_mode; + /* + * 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); } @@ -426,9 +430,3 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc) exynos_drm_fn_encoder(private->crtc[crtc], &crtc, exynos_drm_disable_vblank); } - -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 CRTC Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 09cc13f791b3..a6819b5f8428 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -38,6 +38,7 @@ #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" @@ -144,11 +145,34 @@ static int exynos_drm_unload(struct drm_device *dev) 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) @@ -185,6 +209,8 @@ static struct drm_ioctl_desc exynos_ioctls[] = { 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 = { @@ -202,6 +228,7 @@ static struct drm_driver exynos_drm_driver = { 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, @@ -252,9 +279,60 @@ static struct platform_driver exynos_drm_platform_driver = { static int __init exynos_drm_init(void) { + int ret; + DRM_DEBUG_DRIVER("%s\n", __FILE__); - return platform_driver_register(&exynos_drm_platform_driver); +#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) @@ -262,6 +340,20 @@ 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); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 13540de90bfc..fbd0a232c93d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -32,9 +32,9 @@ #include <linux/module.h> #include "drm.h" -#define MAX_CRTC 2 +#define MAX_CRTC 3 #define MAX_PLANE 5 -#define MAX_FB_BUFFER 3 +#define MAX_FB_BUFFER 4 #define DEFAULT_ZPOS -1 struct drm_device; @@ -50,6 +50,8 @@ enum exynos_drm_output_type { EXYNOS_DISPLAY_TYPE_LCD, /* HDMI Interface. */ EXYNOS_DISPLAY_TYPE_HDMI, + /* Virtual Display Interface. */ + EXYNOS_DISPLAY_TYPE_VIDI, }; /* @@ -155,8 +157,10 @@ struct exynos_drm_display_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. @@ -164,7 +168,13 @@ struct exynos_drm_display_ops { 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); @@ -217,10 +227,13 @@ struct exynos_drm_private { * @list: sub driver has its own list object to register to exynos drm driver. * @drm_dev: pointer to drm_device and this pointer would be set * when sub driver calls exynos_drm_subdrv_register(). + * @is_local: appear encoder and connector disrelated device. * @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. * @manager: subdrv has its own manager to control a hardware appropriately * and we can access a hardware drawing on this manager. * @encoder: encoder object owned by this sub driver. @@ -229,9 +242,14 @@ struct exynos_drm_private { struct exynos_drm_subdrv { struct list_head list; struct drm_device *drm_dev; + bool is_local; 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 exynos_drm_manager manager; struct drm_encoder *encoder; @@ -254,15 +272,19 @@ 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 - * and then fb helper and drm mode group would be re-initialized. + * 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 and fb helper - * and drm mode group would be re-initialized. - */ +/* 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 diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c index ef4754f1519b..6e9ac7bd1dcf 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c @@ -111,9 +111,19 @@ 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__); - /* drm framework doesn't check NULL. */ + 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; } @@ -132,12 +142,11 @@ static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder, DRM_DEBUG_KMS("%s\n", __FILE__); - mode = adjusted_mode; - 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, mode); + manager_ops->mode_set(manager->dev, + adjusted_mode); if (overlay_ops && overlay_ops->mode_set) overlay_ops->mode_set(manager->dev, overlay); @@ -209,6 +218,7 @@ static unsigned int exynos_drm_encoder_clones(struct drm_encoder *encoder) 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: @@ -433,9 +443,3 @@ void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data) if (overlay_ops && overlay_ops->disable) overlay_ops->disable(manager->dev, zpos); } - -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 Encoder Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 3733fe6723d3..c38c8f468fa3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -211,9 +211,3 @@ void exynos_drm_mode_config_init(struct drm_device *dev) dev->mode_config.funcs = &exynos_drm_mode_config_funcs; } - -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 FB Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 54f8f074822f..d5586cc75163 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -125,7 +125,9 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, } size = mode_cmd.pitches[0] * mode_cmd.height; - exynos_gem_obj = exynos_drm_gem_create(dev, size); + + /* 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; @@ -314,89 +316,3 @@ void exynos_drm_fbdev_restore_mode(struct drm_device *dev) drm_fb_helper_restore_fbdev_mode(private->fb_helper); } - -int exynos_drm_fbdev_reinit(struct drm_device *dev) -{ - struct exynos_drm_private *private = dev->dev_private; - struct drm_fb_helper *fb_helper; - int ret; - - if (!private) - return -EINVAL; - - /* - * if all sub drivers were unloaded then num_connector is 0 - * so at this time, the framebuffers also should be destroyed. - */ - if (!dev->mode_config.num_connector) { - exynos_drm_fbdev_fini(dev); - return 0; - } - - fb_helper = private->fb_helper; - - if (fb_helper) { - struct list_head temp_list; - - INIT_LIST_HEAD(&temp_list); - - /* - * fb_helper is reintialized but kernel fb is reused - * so kernel_fb_list need to be backuped and restored - */ - if (!list_empty(&fb_helper->kernel_fb_list)) - list_replace_init(&fb_helper->kernel_fb_list, - &temp_list); - - drm_fb_helper_fini(fb_helper); - - ret = drm_fb_helper_init(dev, fb_helper, - dev->mode_config.num_crtc, MAX_CONNECTOR); - if (ret < 0) { - DRM_ERROR("failed to initialize drm fb helper\n"); - return ret; - } - - if (!list_empty(&temp_list)) - list_replace(&temp_list, &fb_helper->kernel_fb_list); - - ret = drm_fb_helper_single_add_all_connectors(fb_helper); - if (ret < 0) { - DRM_ERROR("failed to add fb helper to connectors\n"); - goto err; - } - - ret = drm_fb_helper_initial_config(fb_helper, PREFERRED_BPP); - if (ret < 0) { - DRM_ERROR("failed to set up hw configuration.\n"); - goto err; - } - } else { - /* - * if drm_load() failed whem drm load() was called prior - * to specific drivers, fb_helper must be NULL and so - * this fuction should be called again to re-initialize and - * re-configure the fb helper. it means that this function - * has been called by the specific drivers. - */ - ret = exynos_drm_fbdev_init(dev); - } - - return ret; - -err: - /* - * if drm_load() failed when drm load() was called prior - * to specific drivers, the fb_helper must be NULL and so check it. - */ - if (fb_helper) - drm_fb_helper_fini(fb_helper); - - return ret; -} - -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 FBDEV Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 56458eea0501..ecb6db229700 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -1007,7 +1007,7 @@ static const struct dev_pm_ops fimd_pm_ops = { SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL) }; -static struct platform_driver fimd_driver = { +struct platform_driver fimd_driver = { .probe = fimd_probe, .remove = __devexit_p(fimd_remove), .driver = { @@ -1016,21 +1016,3 @@ static struct platform_driver fimd_driver = { .pm = &fimd_pm_ops, }, }; - -static int __init fimd_init(void) -{ - return platform_driver_register(&fimd_driver); -} - -static void __exit fimd_exit(void) -{ - platform_driver_unregister(&fimd_driver); -} - -module_init(fimd_init); -module_exit(fimd_exit); - -MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); -MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); -MODULE_DESCRIPTION("Samsung DRM FIMD Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 025abb3e3b67..fa1aa94a3d8e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -26,6 +26,7 @@ #include "drmP.h" #include "drm.h" +#include <linux/shmem_fs.h> #include <drm/exynos_drm.h> #include "exynos_drm_drv.h" @@ -55,6 +56,178 @@ static unsigned int convert_to_vm_err_msg(int msg) return out_msg; } +static unsigned int mask_gem_flags(unsigned int flags) +{ + return flags &= EXYNOS_BO_NONCONTIG; +} + +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) { + unsigned long usize = buf->size; + + if (!buf->pages) + return -EINTR; + + while (usize > 0) { + pfn = page_to_pfn(buf->pages[page_offset++]); + vm_insert_mixed(vma, f_vaddr, pfn); + f_vaddr += PAGE_SIZE; + usize -= PAGE_SIZE; + } + + return 0; + } + + 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) @@ -90,7 +263,15 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj) DRM_DEBUG_KMS("handle count = %d\n", atomic_read(&obj->handle_count)); - exynos_drm_buf_destroy(obj->dev, exynos_gem_obj->buffer); + 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); @@ -99,6 +280,7 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj) 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, @@ -114,6 +296,7 @@ static struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev, return NULL; } + exynos_gem_obj->size = size; obj = &exynos_gem_obj->base; ret = drm_gem_object_init(dev, obj, size); @@ -129,27 +312,55 @@ static struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev, } struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, - unsigned long size) + unsigned int flags, + unsigned long size) { - struct exynos_drm_gem_buf *buffer; struct exynos_drm_gem_obj *exynos_gem_obj; + struct exynos_drm_gem_buf *buf; + int ret; size = roundup(size, PAGE_SIZE); DRM_DEBUG_KMS("%s: size = 0x%lx\n", __FILE__, size); - buffer = exynos_drm_buf_create(dev, size); - if (!buffer) + flags = mask_gem_flags(flags); + + 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) { - exynos_drm_buf_destroy(dev, buffer); - return ERR_PTR(-ENOMEM); + ret = -ENOMEM; + goto err; } - exynos_gem_obj->buffer = buffer; + 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; + } + } else { + ret = exynos_drm_alloc_buf(dev, buf, flags); + if (ret < 0) { + drm_gem_object_release(&exynos_gem_obj->base); + goto err; + } + } return exynos_gem_obj; +err: + exynos_drm_fini_buf(dev, buf); + return ERR_PTR(ret); } int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, @@ -161,7 +372,7 @@ int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, DRM_DEBUG_KMS("%s\n", __FILE__); - exynos_gem_obj = exynos_drm_gem_create(dev, args->size); + exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size); if (IS_ERR(exynos_gem_obj)) return PTR_ERR(exynos_gem_obj); @@ -175,6 +386,64 @@ int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, 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) { @@ -200,7 +469,8 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp, 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; + unsigned long pfn, vm_size, usize, uaddr = vma->vm_start; + int ret; DRM_DEBUG_KMS("%s\n", __FILE__); @@ -208,9 +478,9 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp, /* in case of direct mapping, always having non-cachable attribute */ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - vma->vm_file = filp; - vm_size = vma->vm_end - vma->vm_start; + 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. @@ -221,18 +491,37 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp, if (vm_size > buffer->size) return -EINVAL; - /* - * 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; + if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { + int i = 0; + + if (!buffer->pages) + return -EINVAL; + + 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; @@ -312,9 +601,9 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv, */ args->pitch = args->width * args->bpp >> 3; - args->size = args->pitch * args->height; + args->size = PAGE_ALIGN(args->pitch * args->height); - exynos_gem_obj = exynos_drm_gem_create(dev, args->size); + exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size); if (IS_ERR(exynos_gem_obj)) return PTR_ERR(exynos_gem_obj); @@ -398,20 +687,31 @@ int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) struct drm_gem_object *obj = vma->vm_private_data; struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); struct drm_device *dev = obj->dev; - unsigned long pfn; + 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); - pfn = (((unsigned long)exynos_gem_obj->buffer->dma_addr) >> - PAGE_SHIFT) + page_offset; + /* + * allocate all pages as desired size if user wants to allocate + * physically non-continuous memory. + */ + if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { + ret = exynos_drm_gem_get_pages(obj); + if (ret < 0) + goto err; + } - ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn); + ret = exynos_drm_gem_map_pages(obj, vma, f_vaddr, page_offset); + if (ret < 0) + DRM_ERROR("failed to map pages.\n"); +err: mutex_unlock(&dev->struct_mutex); return convert_to_vm_err_msg(ret); @@ -435,7 +735,3 @@ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) return ret; } - -MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); -MODULE_DESCRIPTION("Samsung SoC DRM GEM Module"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index 67cdc9168708..e40fbad8b705 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h @@ -36,11 +36,15 @@ * @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; }; @@ -55,6 +59,8 @@ struct exynos_drm_gem_buf { * 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. @@ -62,6 +68,8 @@ struct exynos_drm_gem_buf { 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 */ @@ -69,7 +77,8 @@ 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 long size); + unsigned int flags, + unsigned long size); /* * request gem object creation and buffer allocation as the size @@ -79,6 +88,24 @@ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, 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); diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c index ed8a319ed84b..14eb26b0ba1c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c @@ -38,7 +38,6 @@ struct drm_hdmi_context { struct exynos_drm_subdrv subdrv; struct exynos_drm_hdmi_context *hdmi_ctx; struct exynos_drm_hdmi_context *mixer_ctx; - struct work_struct work; }; void exynos_drm_display_ops_register(struct exynos_hdmi_display_ops @@ -49,7 +48,6 @@ void exynos_drm_display_ops_register(struct exynos_hdmi_display_ops if (display_ops) hdmi_display_ops = display_ops; } -EXPORT_SYMBOL(exynos_drm_display_ops_register); void exynos_drm_manager_ops_register(struct exynos_hdmi_manager_ops *manager_ops) @@ -59,7 +57,6 @@ void exynos_drm_manager_ops_register(struct exynos_hdmi_manager_ops if (manager_ops) hdmi_manager_ops = manager_ops; } -EXPORT_SYMBOL(exynos_drm_manager_ops_register); void exynos_drm_overlay_ops_register(struct exynos_hdmi_overlay_ops *overlay_ops) @@ -69,7 +66,6 @@ void exynos_drm_overlay_ops_register(struct exynos_hdmi_overlay_ops if (overlay_ops) hdmi_overlay_ops = overlay_ops; } -EXPORT_SYMBOL(exynos_drm_overlay_ops_register); static bool drm_hdmi_is_connected(struct device *dev) { @@ -155,6 +151,20 @@ static void drm_hdmi_disable_vblank(struct device *subdrv_dev) return hdmi_overlay_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_manager_ops && hdmi_manager_ops->mode_fixup) + hdmi_manager_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); @@ -165,6 +175,18 @@ static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode) hdmi_manager_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_manager_ops && hdmi_manager_ops->get_max_resol) + hdmi_manager_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); @@ -200,7 +222,9 @@ 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, }; @@ -249,7 +273,6 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev, struct drm_hdmi_context *ctx; struct platform_device *pdev = to_platform_device(dev); struct exynos_drm_common_hdmi_pd *pd; - int ret; DRM_DEBUG_KMS("%s\n", __FILE__); @@ -270,26 +293,13 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev, return -EFAULT; } - ret = platform_driver_register(&hdmi_driver); - if (ret) { - DRM_DEBUG_KMS("failed to register hdmi driver.\n"); - return ret; - } - - ret = platform_driver_register(&mixer_driver); - if (ret) { - DRM_DEBUG_KMS("failed to register mixer driver.\n"); - goto err_hdmidrv; - } - 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"); - ret = -EFAULT; - goto err_mixerdrv; + return -EFAULT; } ctx->hdmi_ctx->drm_dev = drm_dev; @@ -298,42 +308,12 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev, to_context(pd->mixer_dev); if (!ctx->mixer_ctx) { DRM_DEBUG_KMS("mixer context is null.\n"); - ret = -EFAULT; - goto err_mixerdrv; + return -EFAULT; } ctx->mixer_ctx->drm_dev = drm_dev; return 0; - -err_mixerdrv: - platform_driver_unregister(&mixer_driver); -err_hdmidrv: - platform_driver_unregister(&hdmi_driver); - return ret; -} - -static void hdmi_subdrv_remove(struct drm_device *drm_dev) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - platform_driver_unregister(&hdmi_driver); - platform_driver_unregister(&mixer_driver); -} - -static void exynos_drm_hdmi_late_probe(struct work_struct *work) -{ - struct drm_hdmi_context *ctx = container_of(work, - struct drm_hdmi_context, work); - - /* - * this function calls subdrv->probe() so this must be called - * after probe context. - * - * PS. subdrv->probe() will call platform_driver_register() to probe - * hdmi and mixer driver. - */ - exynos_drm_subdrv_register(&ctx->subdrv); } static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev) @@ -353,7 +333,6 @@ static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev) subdrv = &ctx->subdrv; subdrv->probe = hdmi_subdrv_probe; - subdrv->remove = hdmi_subdrv_remove; subdrv->manager.pipe = -1; subdrv->manager.ops = &drm_hdmi_manager_ops; subdrv->manager.overlay_ops = &drm_hdmi_overlay_ops; @@ -362,9 +341,7 @@ static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, subdrv); - INIT_WORK(&ctx->work, exynos_drm_hdmi_late_probe); - - schedule_work(&ctx->work); + exynos_drm_subdrv_register(subdrv); return 0; } @@ -400,7 +377,7 @@ static int __devexit exynos_drm_hdmi_remove(struct platform_device *pdev) return 0; } -static struct platform_driver exynos_drm_common_hdmi_driver = { +struct platform_driver exynos_drm_common_hdmi_driver = { .probe = exynos_drm_hdmi_probe, .remove = __devexit_p(exynos_drm_hdmi_remove), .driver = { @@ -409,31 +386,3 @@ static struct platform_driver exynos_drm_common_hdmi_driver = { .pm = &hdmi_pm_ops, }, }; - -static int __init exynos_drm_hdmi_init(void) -{ - int ret; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - ret = platform_driver_register(&exynos_drm_common_hdmi_driver); - if (ret) { - DRM_DEBUG_KMS("failed to register hdmi common driver.\n"); - return ret; - } - - return ret; -} - -static void __exit exynos_drm_hdmi_exit(void) -{ - platform_driver_unregister(&exynos_drm_common_hdmi_driver); -} - -module_init(exynos_drm_hdmi_init); -module_exit(exynos_drm_hdmi_exit); - -MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); -MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>"); -MODULE_DESCRIPTION("Samsung SoC DRM HDMI Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h index 3c29f790ee45..44497cfb6c74 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h @@ -47,7 +47,12 @@ struct exynos_hdmi_display_ops { }; struct exynos_hdmi_manager_ops { + 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); }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index bdcf770aa22e..c277a3a445f5 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -22,6 +22,10 @@ struct exynos_plane { bool enabled; }; +static const uint32_t formats[] = { + DRM_FORMAT_XRGB8888, +}; + static int exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int crtc_x, int crtc_y, @@ -115,9 +119,9 @@ int exynos_plane_init(struct drm_device *dev, unsigned int nr) exynos_plane->overlay.zpos = DEFAULT_ZPOS; - /* TODO: format */ return drm_plane_init(dev, &exynos_plane->base, possible_crtcs, - &exynos_plane_funcs, NULL, 0, false); + &exynos_plane_funcs, formats, ARRAY_SIZE(formats), + false); } int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c new file mode 100644 index 000000000000..8e1339f9fe1f --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -0,0 +1,676 @@ +/* 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 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->manager.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->probe = vidi_subdrv_probe; + subdrv->remove = vidi_subdrv_remove; + subdrv->manager.pipe = -1; + subdrv->manager.ops = &vidi_manager_ops; + subdrv->manager.overlay_ops = &vidi_overlay_ops; + subdrv->manager.display_ops = &vidi_display_ops; + subdrv->manager.dev = dev; + + 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, + }, +}; diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.h b/drivers/gpu/drm/exynos/exynos_drm_vidi.h new file mode 100644 index 000000000000..a4babe4e65d7 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.h @@ -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 diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 3429d3fd93f3..575a8cbd3533 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -41,44 +41,83 @@ #include "exynos_hdmi.h" #define HDMI_OVERLAY_NUMBER 3 +#define MAX_WIDTH 1920 +#define MAX_HEIGHT 1080 #define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev)) -static const u8 hdmiphy_conf27[32] = { +struct hdmi_resources { + struct clk *hdmi; + struct clk *sclk_hdmi; + struct clk *sclk_pixel; + struct clk *sclk_hdmiphy; + struct clk *hdmiphy; + struct regulator_bulk_data *regul_bulk; + int regul_count; +}; + +struct hdmi_context { + struct device *dev; + struct drm_device *drm_dev; + struct fb_videomode *default_timing; + unsigned int is_v13:1; + unsigned int default_win; + unsigned int default_bpp; + bool hpd_handle; + bool enabled; + + struct resource *regs_res; + void __iomem *regs; + unsigned int irq; + struct workqueue_struct *wq; + struct work_struct hotplug_work; + + struct i2c_client *ddc_port; + struct i2c_client *hdmiphy_port; + + /* current hdmiphy conf index */ + int cur_conf; + + struct hdmi_resources res; + void *parent_ctx; +}; + +/* HDMI Version 1.3 */ +static const u8 hdmiphy_v13_conf27[32] = { 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40, 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87, 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, }; -static const u8 hdmiphy_conf27_027[32] = { +static const u8 hdmiphy_v13_conf27_027[32] = { 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64, 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87, 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, }; -static const u8 hdmiphy_conf74_175[32] = { +static const u8 hdmiphy_v13_conf74_175[32] = { 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B, 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9, 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00, }; -static const u8 hdmiphy_conf74_25[32] = { +static const u8 hdmiphy_v13_conf74_25[32] = { 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40, 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba, 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0, 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00, }; -static const u8 hdmiphy_conf148_5[32] = { +static const u8 hdmiphy_v13_conf148_5[32] = { 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40, 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba, 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0, 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00, }; -struct hdmi_tg_regs { +struct hdmi_v13_tg_regs { u8 cmd; u8 h_fsz_l; u8 h_fsz_h; @@ -110,7 +149,7 @@ struct hdmi_tg_regs { u8 field_bot_hdmi_h; }; -struct hdmi_core_regs { +struct hdmi_v13_core_regs { u8 h_blank[2]; u8 v_blank[3]; u8 h_v_line[3]; @@ -123,12 +162,21 @@ struct hdmi_core_regs { u8 v_sync_gen3[3]; }; -struct hdmi_preset_conf { - struct hdmi_core_regs core; - struct hdmi_tg_regs tg; +struct hdmi_v13_preset_conf { + struct hdmi_v13_core_regs core; + struct hdmi_v13_tg_regs tg; }; -static const struct hdmi_preset_conf hdmi_conf_480p = { +struct hdmi_v13_conf { + int width; + int height; + int vrefresh; + bool interlace; + const u8 *hdmiphy_data; + const struct hdmi_v13_preset_conf *conf; +}; + +static const struct hdmi_v13_preset_conf hdmi_v13_conf_480p = { .core = { .h_blank = {0x8a, 0x00}, .v_blank = {0x0d, 0x6a, 0x01}, @@ -154,7 +202,7 @@ static const struct hdmi_preset_conf hdmi_conf_480p = { }, }; -static const struct hdmi_preset_conf hdmi_conf_720p60 = { +static const struct hdmi_v13_preset_conf hdmi_v13_conf_720p60 = { .core = { .h_blank = {0x72, 0x01}, .v_blank = {0xee, 0xf2, 0x00}, @@ -182,7 +230,7 @@ static const struct hdmi_preset_conf hdmi_conf_720p60 = { }, }; -static const struct hdmi_preset_conf hdmi_conf_1080i50 = { +static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080i50 = { .core = { .h_blank = {0xd0, 0x02}, .v_blank = {0x32, 0xB2, 0x00}, @@ -210,7 +258,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080i50 = { }, }; -static const struct hdmi_preset_conf hdmi_conf_1080p50 = { +static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080p50 = { .core = { .h_blank = {0xd0, 0x02}, .v_blank = {0x65, 0x6c, 0x01}, @@ -238,7 +286,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080p50 = { }, }; -static const struct hdmi_preset_conf hdmi_conf_1080i60 = { +static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080i60 = { .core = { .h_blank = {0x18, 0x01}, .v_blank = {0x32, 0xB2, 0x00}, @@ -266,7 +314,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080i60 = { }, }; -static const struct hdmi_preset_conf hdmi_conf_1080p60 = { +static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080p60 = { .core = { .h_blank = {0x18, 0x01}, .v_blank = {0x65, 0x6c, 0x01}, @@ -294,13 +342,530 @@ static const struct hdmi_preset_conf hdmi_conf_1080p60 = { }, }; +static const struct hdmi_v13_conf hdmi_v13_confs[] = { + { 1280, 720, 60, false, hdmiphy_v13_conf74_25, &hdmi_v13_conf_720p60 }, + { 1280, 720, 50, false, hdmiphy_v13_conf74_25, &hdmi_v13_conf_720p60 }, + { 720, 480, 60, false, hdmiphy_v13_conf27_027, &hdmi_v13_conf_480p }, + { 1920, 1080, 50, true, hdmiphy_v13_conf74_25, &hdmi_v13_conf_1080i50 }, + { 1920, 1080, 50, false, hdmiphy_v13_conf148_5, + &hdmi_v13_conf_1080p50 }, + { 1920, 1080, 60, true, hdmiphy_v13_conf74_25, &hdmi_v13_conf_1080i60 }, + { 1920, 1080, 60, false, hdmiphy_v13_conf148_5, + &hdmi_v13_conf_1080p60 }, +}; + +/* HDMI Version 1.4 */ +static const u8 hdmiphy_conf27_027[32] = { + 0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08, + 0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80, + 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, + 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, +}; + +static const u8 hdmiphy_conf74_25[32] = { + 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08, + 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80, + 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, + 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, +}; + +static const u8 hdmiphy_conf148_5[32] = { + 0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08, + 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80, + 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86, + 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00, +}; + +struct hdmi_tg_regs { + u8 cmd; + u8 h_fsz_l; + u8 h_fsz_h; + u8 hact_st_l; + u8 hact_st_h; + u8 hact_sz_l; + u8 hact_sz_h; + u8 v_fsz_l; + u8 v_fsz_h; + u8 vsync_l; + u8 vsync_h; + u8 vsync2_l; + u8 vsync2_h; + u8 vact_st_l; + u8 vact_st_h; + u8 vact_sz_l; + u8 vact_sz_h; + u8 field_chg_l; + u8 field_chg_h; + u8 vact_st2_l; + u8 vact_st2_h; + u8 vact_st3_l; + u8 vact_st3_h; + u8 vact_st4_l; + u8 vact_st4_h; + u8 vsync_top_hdmi_l; + u8 vsync_top_hdmi_h; + u8 vsync_bot_hdmi_l; + u8 vsync_bot_hdmi_h; + u8 field_top_hdmi_l; + u8 field_top_hdmi_h; + u8 field_bot_hdmi_l; + u8 field_bot_hdmi_h; + u8 tg_3d; +}; + +struct hdmi_core_regs { + u8 h_blank[2]; + u8 v2_blank[2]; + u8 v1_blank[2]; + u8 v_line[2]; + u8 h_line[2]; + u8 hsync_pol[1]; + u8 vsync_pol[1]; + u8 int_pro_mode[1]; + u8 v_blank_f0[2]; + u8 v_blank_f1[2]; + u8 h_sync_start[2]; + u8 h_sync_end[2]; + u8 v_sync_line_bef_2[2]; + u8 v_sync_line_bef_1[2]; + u8 v_sync_line_aft_2[2]; + u8 v_sync_line_aft_1[2]; + u8 v_sync_line_aft_pxl_2[2]; + u8 v_sync_line_aft_pxl_1[2]; + u8 v_blank_f2[2]; /* for 3D mode */ + u8 v_blank_f3[2]; /* for 3D mode */ + u8 v_blank_f4[2]; /* for 3D mode */ + u8 v_blank_f5[2]; /* for 3D mode */ + u8 v_sync_line_aft_3[2]; + u8 v_sync_line_aft_4[2]; + u8 v_sync_line_aft_5[2]; + u8 v_sync_line_aft_6[2]; + u8 v_sync_line_aft_pxl_3[2]; + u8 v_sync_line_aft_pxl_4[2]; + u8 v_sync_line_aft_pxl_5[2]; + u8 v_sync_line_aft_pxl_6[2]; + u8 vact_space_1[2]; + u8 vact_space_2[2]; + u8 vact_space_3[2]; + u8 vact_space_4[2]; + u8 vact_space_5[2]; + u8 vact_space_6[2]; +}; + +struct hdmi_preset_conf { + struct hdmi_core_regs core; + struct hdmi_tg_regs tg; +}; + +struct hdmi_conf { + int width; + int height; + int vrefresh; + bool interlace; + const u8 *hdmiphy_data; + const struct hdmi_preset_conf *conf; +}; + +static const struct hdmi_preset_conf hdmi_conf_480p60 = { + .core = { + .h_blank = {0x8a, 0x00}, + .v2_blank = {0x0d, 0x02}, + .v1_blank = {0x2d, 0x00}, + .v_line = {0x0d, 0x02}, + .h_line = {0x5a, 0x03}, + .hsync_pol = {0x01}, + .vsync_pol = {0x01}, + .int_pro_mode = {0x00}, + .v_blank_f0 = {0xff, 0xff}, + .v_blank_f1 = {0xff, 0xff}, + .h_sync_start = {0x0e, 0x00}, + .h_sync_end = {0x4c, 0x00}, + .v_sync_line_bef_2 = {0x0f, 0x00}, + .v_sync_line_bef_1 = {0x09, 0x00}, + .v_sync_line_aft_2 = {0xff, 0xff}, + .v_sync_line_aft_1 = {0xff, 0xff}, + .v_sync_line_aft_pxl_2 = {0xff, 0xff}, + .v_sync_line_aft_pxl_1 = {0xff, 0xff}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + .vact_space_1 = {0xff, 0xff}, + .vact_space_2 = {0xff, 0xff}, + .vact_space_3 = {0xff, 0xff}, + .vact_space_4 = {0xff, 0xff}, + .vact_space_5 = {0xff, 0xff}, + .vact_space_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0x5a, 0x03, /* h_fsz */ + 0x8a, 0x00, 0xd0, 0x02, /* hact */ + 0x0d, 0x02, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x2d, 0x00, 0xe0, 0x01, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x48, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + +static const struct hdmi_preset_conf hdmi_conf_720p50 = { + .core = { + .h_blank = {0xbc, 0x02}, + .v2_blank = {0xee, 0x02}, + .v1_blank = {0x1e, 0x00}, + .v_line = {0xee, 0x02}, + .h_line = {0xbc, 0x07}, + .hsync_pol = {0x00}, + .vsync_pol = {0x00}, + .int_pro_mode = {0x00}, + .v_blank_f0 = {0xff, 0xff}, + .v_blank_f1 = {0xff, 0xff}, + .h_sync_start = {0xb6, 0x01}, + .h_sync_end = {0xde, 0x01}, + .v_sync_line_bef_2 = {0x0a, 0x00}, + .v_sync_line_bef_1 = {0x05, 0x00}, + .v_sync_line_aft_2 = {0xff, 0xff}, + .v_sync_line_aft_1 = {0xff, 0xff}, + .v_sync_line_aft_pxl_2 = {0xff, 0xff}, + .v_sync_line_aft_pxl_1 = {0xff, 0xff}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + .vact_space_1 = {0xff, 0xff}, + .vact_space_2 = {0xff, 0xff}, + .vact_space_3 = {0xff, 0xff}, + .vact_space_4 = {0xff, 0xff}, + .vact_space_5 = {0xff, 0xff}, + .vact_space_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0xbc, 0x07, /* h_fsz */ + 0xbc, 0x02, 0x00, 0x05, /* hact */ + 0xee, 0x02, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x1e, 0x00, 0xd0, 0x02, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x48, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + +static const struct hdmi_preset_conf hdmi_conf_720p60 = { + .core = { + .h_blank = {0x72, 0x01}, + .v2_blank = {0xee, 0x02}, + .v1_blank = {0x1e, 0x00}, + .v_line = {0xee, 0x02}, + .h_line = {0x72, 0x06}, + .hsync_pol = {0x00}, + .vsync_pol = {0x00}, + .int_pro_mode = {0x00}, + .v_blank_f0 = {0xff, 0xff}, + .v_blank_f1 = {0xff, 0xff}, + .h_sync_start = {0x6c, 0x00}, + .h_sync_end = {0x94, 0x00}, + .v_sync_line_bef_2 = {0x0a, 0x00}, + .v_sync_line_bef_1 = {0x05, 0x00}, + .v_sync_line_aft_2 = {0xff, 0xff}, + .v_sync_line_aft_1 = {0xff, 0xff}, + .v_sync_line_aft_pxl_2 = {0xff, 0xff}, + .v_sync_line_aft_pxl_1 = {0xff, 0xff}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + .vact_space_1 = {0xff, 0xff}, + .vact_space_2 = {0xff, 0xff}, + .vact_space_3 = {0xff, 0xff}, + .vact_space_4 = {0xff, 0xff}, + .vact_space_5 = {0xff, 0xff}, + .vact_space_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0x72, 0x06, /* h_fsz */ + 0x72, 0x01, 0x00, 0x05, /* hact */ + 0xee, 0x02, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x1e, 0x00, 0xd0, 0x02, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x48, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + +static const struct hdmi_preset_conf hdmi_conf_1080i50 = { + .core = { + .h_blank = {0xd0, 0x02}, + .v2_blank = {0x32, 0x02}, + .v1_blank = {0x16, 0x00}, + .v_line = {0x65, 0x04}, + .h_line = {0x50, 0x0a}, + .hsync_pol = {0x00}, + .vsync_pol = {0x00}, + .int_pro_mode = {0x01}, + .v_blank_f0 = {0x49, 0x02}, + .v_blank_f1 = {0x65, 0x04}, + .h_sync_start = {0x0e, 0x02}, + .h_sync_end = {0x3a, 0x02}, + .v_sync_line_bef_2 = {0x07, 0x00}, + .v_sync_line_bef_1 = {0x02, 0x00}, + .v_sync_line_aft_2 = {0x39, 0x02}, + .v_sync_line_aft_1 = {0x34, 0x02}, + .v_sync_line_aft_pxl_2 = {0x38, 0x07}, + .v_sync_line_aft_pxl_1 = {0x38, 0x07}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + .vact_space_1 = {0xff, 0xff}, + .vact_space_2 = {0xff, 0xff}, + .vact_space_3 = {0xff, 0xff}, + .vact_space_4 = {0xff, 0xff}, + .vact_space_5 = {0xff, 0xff}, + .vact_space_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0x50, 0x0a, /* h_fsz */ + 0xd0, 0x02, 0x80, 0x07, /* hact */ + 0x65, 0x04, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x16, 0x00, 0x1c, 0x02, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x49, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + +static const struct hdmi_preset_conf hdmi_conf_1080i60 = { + .core = { + .h_blank = {0x18, 0x01}, + .v2_blank = {0x32, 0x02}, + .v1_blank = {0x16, 0x00}, + .v_line = {0x65, 0x04}, + .h_line = {0x98, 0x08}, + .hsync_pol = {0x00}, + .vsync_pol = {0x00}, + .int_pro_mode = {0x01}, + .v_blank_f0 = {0x49, 0x02}, + .v_blank_f1 = {0x65, 0x04}, + .h_sync_start = {0x56, 0x00}, + .h_sync_end = {0x82, 0x00}, + .v_sync_line_bef_2 = {0x07, 0x00}, + .v_sync_line_bef_1 = {0x02, 0x00}, + .v_sync_line_aft_2 = {0x39, 0x02}, + .v_sync_line_aft_1 = {0x34, 0x02}, + .v_sync_line_aft_pxl_2 = {0xa4, 0x04}, + .v_sync_line_aft_pxl_1 = {0xa4, 0x04}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + .vact_space_1 = {0xff, 0xff}, + .vact_space_2 = {0xff, 0xff}, + .vact_space_3 = {0xff, 0xff}, + .vact_space_4 = {0xff, 0xff}, + .vact_space_5 = {0xff, 0xff}, + .vact_space_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0x98, 0x08, /* h_fsz */ + 0x18, 0x01, 0x80, 0x07, /* hact */ + 0x65, 0x04, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x16, 0x00, 0x1c, 0x02, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x49, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + +static const struct hdmi_preset_conf hdmi_conf_1080p50 = { + .core = { + .h_blank = {0xd0, 0x02}, + .v2_blank = {0x65, 0x04}, + .v1_blank = {0x2d, 0x00}, + .v_line = {0x65, 0x04}, + .h_line = {0x50, 0x0a}, + .hsync_pol = {0x00}, + .vsync_pol = {0x00}, + .int_pro_mode = {0x00}, + .v_blank_f0 = {0xff, 0xff}, + .v_blank_f1 = {0xff, 0xff}, + .h_sync_start = {0x0e, 0x02}, + .h_sync_end = {0x3a, 0x02}, + .v_sync_line_bef_2 = {0x09, 0x00}, + .v_sync_line_bef_1 = {0x04, 0x00}, + .v_sync_line_aft_2 = {0xff, 0xff}, + .v_sync_line_aft_1 = {0xff, 0xff}, + .v_sync_line_aft_pxl_2 = {0xff, 0xff}, + .v_sync_line_aft_pxl_1 = {0xff, 0xff}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + .vact_space_1 = {0xff, 0xff}, + .vact_space_2 = {0xff, 0xff}, + .vact_space_3 = {0xff, 0xff}, + .vact_space_4 = {0xff, 0xff}, + .vact_space_5 = {0xff, 0xff}, + .vact_space_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0x50, 0x0a, /* h_fsz */ + 0xd0, 0x02, 0x80, 0x07, /* hact */ + 0x65, 0x04, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x2d, 0x00, 0x38, 0x04, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x48, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + +static const struct hdmi_preset_conf hdmi_conf_1080p60 = { + .core = { + .h_blank = {0x18, 0x01}, + .v2_blank = {0x65, 0x04}, + .v1_blank = {0x2d, 0x00}, + .v_line = {0x65, 0x04}, + .h_line = {0x98, 0x08}, + .hsync_pol = {0x00}, + .vsync_pol = {0x00}, + .int_pro_mode = {0x00}, + .v_blank_f0 = {0xff, 0xff}, + .v_blank_f1 = {0xff, 0xff}, + .h_sync_start = {0x56, 0x00}, + .h_sync_end = {0x82, 0x00}, + .v_sync_line_bef_2 = {0x09, 0x00}, + .v_sync_line_bef_1 = {0x04, 0x00}, + .v_sync_line_aft_2 = {0xff, 0xff}, + .v_sync_line_aft_1 = {0xff, 0xff}, + .v_sync_line_aft_pxl_2 = {0xff, 0xff}, + .v_sync_line_aft_pxl_1 = {0xff, 0xff}, + .v_blank_f2 = {0xff, 0xff}, + .v_blank_f3 = {0xff, 0xff}, + .v_blank_f4 = {0xff, 0xff}, + .v_blank_f5 = {0xff, 0xff}, + .v_sync_line_aft_3 = {0xff, 0xff}, + .v_sync_line_aft_4 = {0xff, 0xff}, + .v_sync_line_aft_5 = {0xff, 0xff}, + .v_sync_line_aft_6 = {0xff, 0xff}, + .v_sync_line_aft_pxl_3 = {0xff, 0xff}, + .v_sync_line_aft_pxl_4 = {0xff, 0xff}, + .v_sync_line_aft_pxl_5 = {0xff, 0xff}, + .v_sync_line_aft_pxl_6 = {0xff, 0xff}, + /* other don't care */ + }, + .tg = { + 0x00, /* cmd */ + 0x98, 0x08, /* h_fsz */ + 0x18, 0x01, 0x80, 0x07, /* hact */ + 0x65, 0x04, /* v_fsz */ + 0x01, 0x00, 0x33, 0x02, /* vsync */ + 0x2d, 0x00, 0x38, 0x04, /* vact */ + 0x33, 0x02, /* field_chg */ + 0x48, 0x02, /* vact_st2 */ + 0x00, 0x00, /* vact_st3 */ + 0x00, 0x00, /* vact_st4 */ + 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ + 0x01, 0x00, 0x33, 0x02, /* field top/bot */ + 0x00, /* 3d FP */ + }, +}; + static const struct hdmi_conf hdmi_confs[] = { + { 720, 480, 60, false, hdmiphy_conf27_027, &hdmi_conf_480p60 }, + { 1280, 720, 50, false, hdmiphy_conf74_25, &hdmi_conf_720p50 }, { 1280, 720, 60, false, hdmiphy_conf74_25, &hdmi_conf_720p60 }, - { 1280, 720, 50, false, hdmiphy_conf74_25, &hdmi_conf_720p60 }, - { 720, 480, 60, false, hdmiphy_conf27_027, &hdmi_conf_480p }, { 1920, 1080, 50, true, hdmiphy_conf74_25, &hdmi_conf_1080i50 }, - { 1920, 1080, 50, false, hdmiphy_conf148_5, &hdmi_conf_1080p50 }, { 1920, 1080, 60, true, hdmiphy_conf74_25, &hdmi_conf_1080i60 }, + { 1920, 1080, 50, false, hdmiphy_conf148_5, &hdmi_conf_1080p50 }, { 1920, 1080, 60, false, hdmiphy_conf148_5, &hdmi_conf_1080p60 }, }; @@ -324,7 +889,7 @@ static inline void hdmi_reg_writemask(struct hdmi_context *hdata, writel(value, hdata->regs + reg_id); } -static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) +static void hdmi_v13_regs_dump(struct hdmi_context *hdata, char *prefix) { #define DUMPREG(reg_id) \ DRM_DEBUG_KMS("%s:" #reg_id " = %08x\n", prefix, \ @@ -333,6 +898,101 @@ static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) DUMPREG(HDMI_INTC_FLAG); DUMPREG(HDMI_INTC_CON); DUMPREG(HDMI_HPD_STATUS); + DUMPREG(HDMI_V13_PHY_RSTOUT); + DUMPREG(HDMI_V13_PHY_VPLL); + DUMPREG(HDMI_V13_PHY_CMU); + DUMPREG(HDMI_V13_CORE_RSTOUT); + + DRM_DEBUG_KMS("%s: ---- CORE REGISTERS ----\n", prefix); + DUMPREG(HDMI_CON_0); + DUMPREG(HDMI_CON_1); + DUMPREG(HDMI_CON_2); + DUMPREG(HDMI_SYS_STATUS); + DUMPREG(HDMI_V13_PHY_STATUS); + DUMPREG(HDMI_STATUS_EN); + DUMPREG(HDMI_HPD); + DUMPREG(HDMI_MODE_SEL); + DUMPREG(HDMI_V13_HPD_GEN); + DUMPREG(HDMI_V13_DC_CONTROL); + DUMPREG(HDMI_V13_VIDEO_PATTERN_GEN); + + DRM_DEBUG_KMS("%s: ---- CORE SYNC REGISTERS ----\n", prefix); + DUMPREG(HDMI_H_BLANK_0); + DUMPREG(HDMI_H_BLANK_1); + DUMPREG(HDMI_V13_V_BLANK_0); + DUMPREG(HDMI_V13_V_BLANK_1); + DUMPREG(HDMI_V13_V_BLANK_2); + DUMPREG(HDMI_V13_H_V_LINE_0); + DUMPREG(HDMI_V13_H_V_LINE_1); + DUMPREG(HDMI_V13_H_V_LINE_2); + DUMPREG(HDMI_VSYNC_POL); + DUMPREG(HDMI_INT_PRO_MODE); + DUMPREG(HDMI_V13_V_BLANK_F_0); + DUMPREG(HDMI_V13_V_BLANK_F_1); + DUMPREG(HDMI_V13_V_BLANK_F_2); + DUMPREG(HDMI_V13_H_SYNC_GEN_0); + DUMPREG(HDMI_V13_H_SYNC_GEN_1); + DUMPREG(HDMI_V13_H_SYNC_GEN_2); + DUMPREG(HDMI_V13_V_SYNC_GEN_1_0); + DUMPREG(HDMI_V13_V_SYNC_GEN_1_1); + DUMPREG(HDMI_V13_V_SYNC_GEN_1_2); + DUMPREG(HDMI_V13_V_SYNC_GEN_2_0); + DUMPREG(HDMI_V13_V_SYNC_GEN_2_1); + DUMPREG(HDMI_V13_V_SYNC_GEN_2_2); + DUMPREG(HDMI_V13_V_SYNC_GEN_3_0); + DUMPREG(HDMI_V13_V_SYNC_GEN_3_1); + DUMPREG(HDMI_V13_V_SYNC_GEN_3_2); + + DRM_DEBUG_KMS("%s: ---- TG REGISTERS ----\n", prefix); + DUMPREG(HDMI_TG_CMD); + DUMPREG(HDMI_TG_H_FSZ_L); + DUMPREG(HDMI_TG_H_FSZ_H); + DUMPREG(HDMI_TG_HACT_ST_L); + DUMPREG(HDMI_TG_HACT_ST_H); + DUMPREG(HDMI_TG_HACT_SZ_L); + DUMPREG(HDMI_TG_HACT_SZ_H); + DUMPREG(HDMI_TG_V_FSZ_L); + DUMPREG(HDMI_TG_V_FSZ_H); + DUMPREG(HDMI_TG_VSYNC_L); + DUMPREG(HDMI_TG_VSYNC_H); + DUMPREG(HDMI_TG_VSYNC2_L); + DUMPREG(HDMI_TG_VSYNC2_H); + DUMPREG(HDMI_TG_VACT_ST_L); + DUMPREG(HDMI_TG_VACT_ST_H); + DUMPREG(HDMI_TG_VACT_SZ_L); + DUMPREG(HDMI_TG_VACT_SZ_H); + DUMPREG(HDMI_TG_FIELD_CHG_L); + DUMPREG(HDMI_TG_FIELD_CHG_H); + DUMPREG(HDMI_TG_VACT_ST2_L); + DUMPREG(HDMI_TG_VACT_ST2_H); + DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L); + DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H); + DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L); + DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H); + DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L); + DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H); + DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L); + DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H); +#undef DUMPREG +} + +static void hdmi_v14_regs_dump(struct hdmi_context *hdata, char *prefix) +{ + int i; + +#define DUMPREG(reg_id) \ + DRM_DEBUG_KMS("%s:" #reg_id " = %08x\n", prefix, \ + readl(hdata->regs + reg_id)) + + DRM_DEBUG_KMS("%s: ---- CONTROL REGISTERS ----\n", prefix); + DUMPREG(HDMI_INTC_CON); + DUMPREG(HDMI_INTC_FLAG); + DUMPREG(HDMI_HPD_STATUS); + DUMPREG(HDMI_INTC_CON_1); + DUMPREG(HDMI_INTC_FLAG_1); + DUMPREG(HDMI_PHY_STATUS_0); + DUMPREG(HDMI_PHY_STATUS_PLL); + DUMPREG(HDMI_PHY_CON_0); DUMPREG(HDMI_PHY_RSTOUT); DUMPREG(HDMI_PHY_VPLL); DUMPREG(HDMI_PHY_CMU); @@ -343,40 +1003,93 @@ static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) DUMPREG(HDMI_CON_1); DUMPREG(HDMI_CON_2); DUMPREG(HDMI_SYS_STATUS); - DUMPREG(HDMI_PHY_STATUS); + DUMPREG(HDMI_PHY_STATUS_0); DUMPREG(HDMI_STATUS_EN); DUMPREG(HDMI_HPD); DUMPREG(HDMI_MODE_SEL); - DUMPREG(HDMI_HPD_GEN); + DUMPREG(HDMI_ENC_EN); DUMPREG(HDMI_DC_CONTROL); DUMPREG(HDMI_VIDEO_PATTERN_GEN); DRM_DEBUG_KMS("%s: ---- CORE SYNC REGISTERS ----\n", prefix); DUMPREG(HDMI_H_BLANK_0); DUMPREG(HDMI_H_BLANK_1); - DUMPREG(HDMI_V_BLANK_0); - DUMPREG(HDMI_V_BLANK_1); - DUMPREG(HDMI_V_BLANK_2); - DUMPREG(HDMI_H_V_LINE_0); - DUMPREG(HDMI_H_V_LINE_1); - DUMPREG(HDMI_H_V_LINE_2); + DUMPREG(HDMI_V2_BLANK_0); + DUMPREG(HDMI_V2_BLANK_1); + DUMPREG(HDMI_V1_BLANK_0); + DUMPREG(HDMI_V1_BLANK_1); + DUMPREG(HDMI_V_LINE_0); + DUMPREG(HDMI_V_LINE_1); + DUMPREG(HDMI_H_LINE_0); + DUMPREG(HDMI_H_LINE_1); + DUMPREG(HDMI_HSYNC_POL); + DUMPREG(HDMI_VSYNC_POL); DUMPREG(HDMI_INT_PRO_MODE); - DUMPREG(HDMI_V_BLANK_F_0); - DUMPREG(HDMI_V_BLANK_F_1); - DUMPREG(HDMI_V_BLANK_F_2); - DUMPREG(HDMI_H_SYNC_GEN_0); - DUMPREG(HDMI_H_SYNC_GEN_1); - DUMPREG(HDMI_H_SYNC_GEN_2); - DUMPREG(HDMI_V_SYNC_GEN_1_0); - DUMPREG(HDMI_V_SYNC_GEN_1_1); - DUMPREG(HDMI_V_SYNC_GEN_1_2); - DUMPREG(HDMI_V_SYNC_GEN_2_0); - DUMPREG(HDMI_V_SYNC_GEN_2_1); - DUMPREG(HDMI_V_SYNC_GEN_2_2); - DUMPREG(HDMI_V_SYNC_GEN_3_0); - DUMPREG(HDMI_V_SYNC_GEN_3_1); - DUMPREG(HDMI_V_SYNC_GEN_3_2); + DUMPREG(HDMI_V_BLANK_F0_0); + DUMPREG(HDMI_V_BLANK_F0_1); + DUMPREG(HDMI_V_BLANK_F1_0); + DUMPREG(HDMI_V_BLANK_F1_1); + + DUMPREG(HDMI_H_SYNC_START_0); + DUMPREG(HDMI_H_SYNC_START_1); + DUMPREG(HDMI_H_SYNC_END_0); + DUMPREG(HDMI_H_SYNC_END_1); + + DUMPREG(HDMI_V_SYNC_LINE_BEF_2_0); + DUMPREG(HDMI_V_SYNC_LINE_BEF_2_1); + DUMPREG(HDMI_V_SYNC_LINE_BEF_1_0); + DUMPREG(HDMI_V_SYNC_LINE_BEF_1_1); + + DUMPREG(HDMI_V_SYNC_LINE_AFT_2_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_2_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_1_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_1_1); + + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_2_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_2_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_1_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_1_1); + + DUMPREG(HDMI_V_BLANK_F2_0); + DUMPREG(HDMI_V_BLANK_F2_1); + DUMPREG(HDMI_V_BLANK_F3_0); + DUMPREG(HDMI_V_BLANK_F3_1); + DUMPREG(HDMI_V_BLANK_F4_0); + DUMPREG(HDMI_V_BLANK_F4_1); + DUMPREG(HDMI_V_BLANK_F5_0); + DUMPREG(HDMI_V_BLANK_F5_1); + + DUMPREG(HDMI_V_SYNC_LINE_AFT_3_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_3_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_4_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_4_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_5_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_5_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_6_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_6_1); + + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_3_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_3_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_4_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_4_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_5_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_5_1); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_6_0); + DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_6_1); + + DUMPREG(HDMI_VACT_SPACE_1_0); + DUMPREG(HDMI_VACT_SPACE_1_1); + DUMPREG(HDMI_VACT_SPACE_2_0); + DUMPREG(HDMI_VACT_SPACE_2_1); + DUMPREG(HDMI_VACT_SPACE_3_0); + DUMPREG(HDMI_VACT_SPACE_3_1); + DUMPREG(HDMI_VACT_SPACE_4_0); + DUMPREG(HDMI_VACT_SPACE_4_1); + DUMPREG(HDMI_VACT_SPACE_5_0); + DUMPREG(HDMI_VACT_SPACE_5_1); + DUMPREG(HDMI_VACT_SPACE_6_0); + DUMPREG(HDMI_VACT_SPACE_6_1); DRM_DEBUG_KMS("%s: ---- TG REGISTERS ----\n", prefix); DUMPREG(HDMI_TG_CMD); @@ -400,6 +1113,10 @@ static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) DUMPREG(HDMI_TG_FIELD_CHG_H); DUMPREG(HDMI_TG_VACT_ST2_L); DUMPREG(HDMI_TG_VACT_ST2_H); + DUMPREG(HDMI_TG_VACT_ST3_L); + DUMPREG(HDMI_TG_VACT_ST3_H); + DUMPREG(HDMI_TG_VACT_ST4_L); + DUMPREG(HDMI_TG_VACT_ST4_H); DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L); DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H); DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L); @@ -408,10 +1125,49 @@ static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H); DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L); DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H); + DUMPREG(HDMI_TG_3D); + + DRM_DEBUG_KMS("%s: ---- PACKET REGISTERS ----\n", prefix); + DUMPREG(HDMI_AVI_CON); + DUMPREG(HDMI_AVI_HEADER0); + DUMPREG(HDMI_AVI_HEADER1); + DUMPREG(HDMI_AVI_HEADER2); + DUMPREG(HDMI_AVI_CHECK_SUM); + DUMPREG(HDMI_VSI_CON); + DUMPREG(HDMI_VSI_HEADER0); + DUMPREG(HDMI_VSI_HEADER1); + DUMPREG(HDMI_VSI_HEADER2); + for (i = 0; i < 7; ++i) + DUMPREG(HDMI_VSI_DATA(i)); + #undef DUMPREG } -static int hdmi_conf_index(struct drm_display_mode *mode) +static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix) +{ + if (hdata->is_v13) + hdmi_v13_regs_dump(hdata, prefix); + else + hdmi_v14_regs_dump(hdata, prefix); +} + +static int hdmi_v13_conf_index(struct drm_display_mode *mode) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hdmi_v13_confs); ++i) + if (hdmi_v13_confs[i].width == mode->hdisplay && + hdmi_v13_confs[i].height == mode->vdisplay && + hdmi_v13_confs[i].vrefresh == mode->vrefresh && + hdmi_v13_confs[i].interlace == + ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? + true : false)) + return i; + + return -EINVAL; +} + +static int hdmi_v14_conf_index(struct drm_display_mode *mode) { int i; @@ -424,7 +1180,16 @@ static int hdmi_conf_index(struct drm_display_mode *mode) true : false)) return i; - return -1; + return -EINVAL; +} + +static int hdmi_conf_index(struct hdmi_context *hdata, + struct drm_display_mode *mode) +{ + if (hdata->is_v13) + return hdmi_v13_conf_index(mode); + + return hdmi_v14_conf_index(mode); } static bool hdmi_is_connected(void *ctx) @@ -462,29 +1227,69 @@ static int hdmi_get_edid(void *ctx, struct drm_connector *connector, return 0; } -static int hdmi_check_timing(void *ctx, void *timing) +static int hdmi_v13_check_timing(struct fb_videomode *check_timing) { - struct fb_videomode *check_timing = timing; int i; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + DRM_DEBUG_KMS("valid mode : xres=%d, yres=%d, refresh=%d, intl=%d\n", + check_timing->xres, check_timing->yres, + check_timing->refresh, (check_timing->vmode & + FB_VMODE_INTERLACED) ? true : false); - DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", check_timing->xres, - check_timing->yres, check_timing->refresh, - check_timing->vmode); + for (i = 0; i < ARRAY_SIZE(hdmi_v13_confs); ++i) + if (hdmi_v13_confs[i].width == check_timing->xres && + hdmi_v13_confs[i].height == check_timing->yres && + hdmi_v13_confs[i].vrefresh == check_timing->refresh && + hdmi_v13_confs[i].interlace == + ((check_timing->vmode & FB_VMODE_INTERLACED) ? + true : false)) + return 0; - for (i = 0; i < ARRAY_SIZE(hdmi_confs); ++i) + /* TODO */ + + return -EINVAL; +} + +static int hdmi_v14_check_timing(struct fb_videomode *check_timing) +{ + int i; + + DRM_DEBUG_KMS("valid mode : xres=%d, yres=%d, refresh=%d, intl=%d\n", + check_timing->xres, check_timing->yres, + check_timing->refresh, (check_timing->vmode & + FB_VMODE_INTERLACED) ? true : false); + + for (i = 0; i < ARRAY_SIZE(hdmi_confs); i++) if (hdmi_confs[i].width == check_timing->xres && hdmi_confs[i].height == check_timing->yres && hdmi_confs[i].vrefresh == check_timing->refresh && hdmi_confs[i].interlace == ((check_timing->vmode & FB_VMODE_INTERLACED) ? true : false)) - return 0; + return 0; + + /* TODO */ return -EINVAL; } +static int hdmi_check_timing(void *ctx, void *timing) +{ + struct hdmi_context *hdata = (struct hdmi_context *)ctx; + struct fb_videomode *check_timing = timing; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", check_timing->xres, + check_timing->yres, check_timing->refresh, + check_timing->vmode); + + if (hdata->is_v13) + return hdmi_v13_check_timing(check_timing); + else + return hdmi_v14_check_timing(check_timing); +} + static int hdmi_display_power_on(void *ctx, int mode) { DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); @@ -514,15 +1319,185 @@ static struct exynos_hdmi_display_ops display_ops = { .power_on = hdmi_display_power_on, }; +static void hdmi_set_acr(u32 freq, u8 *acr) +{ + u32 n, cts; + + switch (freq) { + case 32000: + n = 4096; + cts = 27000; + break; + case 44100: + n = 6272; + cts = 30000; + break; + case 88200: + n = 12544; + cts = 30000; + break; + case 176400: + n = 25088; + cts = 30000; + break; + case 48000: + n = 6144; + cts = 27000; + break; + case 96000: + n = 12288; + cts = 27000; + break; + case 192000: + n = 24576; + cts = 27000; + break; + default: + n = 0; + cts = 0; + break; + } + + acr[1] = cts >> 16; + acr[2] = cts >> 8 & 0xff; + acr[3] = cts & 0xff; + + acr[4] = n >> 16; + acr[5] = n >> 8 & 0xff; + acr[6] = n & 0xff; +} + +static void hdmi_reg_acr(struct hdmi_context *hdata, u8 *acr) +{ + hdmi_reg_writeb(hdata, HDMI_ACR_N0, acr[6]); + hdmi_reg_writeb(hdata, HDMI_ACR_N1, acr[5]); + hdmi_reg_writeb(hdata, HDMI_ACR_N2, acr[4]); + hdmi_reg_writeb(hdata, HDMI_ACR_MCTS0, acr[3]); + hdmi_reg_writeb(hdata, HDMI_ACR_MCTS1, acr[2]); + hdmi_reg_writeb(hdata, HDMI_ACR_MCTS2, acr[1]); + hdmi_reg_writeb(hdata, HDMI_ACR_CTS0, acr[3]); + hdmi_reg_writeb(hdata, HDMI_ACR_CTS1, acr[2]); + hdmi_reg_writeb(hdata, HDMI_ACR_CTS2, acr[1]); + + if (hdata->is_v13) + hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 4); + else + hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4); +} + +static void hdmi_audio_init(struct hdmi_context *hdata) +{ + u32 sample_rate, bits_per_sample, frame_size_code; + u32 data_num, bit_ch, sample_frq; + u32 val; + u8 acr[7]; + + sample_rate = 44100; + bits_per_sample = 16; + frame_size_code = 0; + + switch (bits_per_sample) { + case 20: + data_num = 2; + bit_ch = 1; + break; + case 24: + data_num = 3; + bit_ch = 1; + break; + default: + data_num = 1; + bit_ch = 0; + break; + } + + hdmi_set_acr(sample_rate, acr); + hdmi_reg_acr(hdata, acr); + + hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE + | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE + | HDMI_I2S_MUX_ENABLE); + + hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CH, HDMI_I2S_CH0_EN + | HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN); + + hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN); + + sample_frq = (sample_rate == 44100) ? 0 : + (sample_rate == 48000) ? 2 : + (sample_rate == 32000) ? 3 : + (sample_rate == 96000) ? 0xa : 0x0; + + hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS); + hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN); + + val = hdmi_reg_read(hdata, HDMI_I2S_DSD_CON) | 0x01; + hdmi_reg_writeb(hdata, HDMI_I2S_DSD_CON, val); + + /* Configuration I2S input ports. Configure I2S_PIN_SEL_0~4 */ + hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_0, HDMI_I2S_SEL_SCLK(5) + | HDMI_I2S_SEL_LRCK(6)); + hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_1, HDMI_I2S_SEL_SDATA1(1) + | HDMI_I2S_SEL_SDATA2(4)); + hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_2, HDMI_I2S_SEL_SDATA3(1) + | HDMI_I2S_SEL_SDATA2(2)); + hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_3, HDMI_I2S_SEL_DSD(0)); + + /* I2S_CON_1 & 2 */ + hdmi_reg_writeb(hdata, HDMI_I2S_CON_1, HDMI_I2S_SCLK_FALLING_EDGE + | HDMI_I2S_L_CH_LOW_POL); + hdmi_reg_writeb(hdata, HDMI_I2S_CON_2, HDMI_I2S_MSB_FIRST_MODE + | HDMI_I2S_SET_BIT_CH(bit_ch) + | HDMI_I2S_SET_SDATA_BIT(data_num) + | HDMI_I2S_BASIC_FORMAT); + + /* Configure register related to CUV information */ + hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_0, HDMI_I2S_CH_STATUS_MODE_0 + | HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH + | HDMI_I2S_COPYRIGHT + | HDMI_I2S_LINEAR_PCM + | HDMI_I2S_CONSUMER_FORMAT); + hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER); + hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0)); + hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2 + | HDMI_I2S_SET_SMP_FREQ(sample_frq)); + hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_4, + HDMI_I2S_ORG_SMP_FREQ_44_1 + | HDMI_I2S_WORD_LEN_MAX24_24BITS + | HDMI_I2S_WORD_LEN_MAX_24BITS); + + hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD); +} + +static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff) +{ + u32 mod; + + mod = hdmi_reg_read(hdata, HDMI_MODE_SEL); + if (mod & HDMI_DVI_MODE_EN) + return; + + hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0); + hdmi_reg_writemask(hdata, HDMI_CON_0, onoff ? + HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK); +} + static void hdmi_conf_reset(struct hdmi_context *hdata) { + u32 reg; + /* disable hpd handle for drm */ hdata->hpd_handle = false; + if (hdata->is_v13) + reg = HDMI_V13_CORE_RSTOUT; + else + reg = HDMI_CORE_RSTOUT; + /* resetting HDMI core */ - hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, HDMI_CORE_SW_RSTOUT); + hdmi_reg_writemask(hdata, reg, 0, HDMI_CORE_SW_RSTOUT); mdelay(10); - hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, HDMI_CORE_SW_RSTOUT); + hdmi_reg_writemask(hdata, reg, ~0, HDMI_CORE_SW_RSTOUT); mdelay(10); /* enable hpd handle for drm */ @@ -546,27 +1521,126 @@ static void hdmi_conf_init(struct hdmi_context *hdata) HDMI_MODE_HDMI_EN, HDMI_MODE_MASK); /* disable bluescreen */ hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN); - /* choose bluescreen (fecal) color */ - hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_0, 0x12); - hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_1, 0x34); - hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_2, 0x56); - /* enable AVI packet every vsync, fixes purple line problem */ - hdmi_reg_writeb(hdata, HDMI_AVI_CON, 0x02); - /* force RGB, look to CEA-861-D, table 7 for more detail */ - hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(0), 0 << 5); - hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5); - - hdmi_reg_writeb(hdata, HDMI_SPD_CON, 0x02); - hdmi_reg_writeb(hdata, HDMI_AUI_CON, 0x02); - hdmi_reg_writeb(hdata, HDMI_ACR_CON, 0x04); + + if (hdata->is_v13) { + /* choose bluescreen (fecal) color */ + hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12); + hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34); + hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_2, 0x56); + + /* enable AVI packet every vsync, fixes purple line problem */ + hdmi_reg_writeb(hdata, HDMI_V13_AVI_CON, 0x02); + /* force RGB, look to CEA-861-D, table 7 for more detail */ + hdmi_reg_writeb(hdata, HDMI_V13_AVI_BYTE(0), 0 << 5); + hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5); + + hdmi_reg_writeb(hdata, HDMI_V13_SPD_CON, 0x02); + hdmi_reg_writeb(hdata, HDMI_V13_AUI_CON, 0x02); + hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04); + } else { + /* enable AVI packet every vsync, fixes purple line problem */ + hdmi_reg_writeb(hdata, HDMI_AVI_CON, 0x02); + hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(1), 2 << 5); + hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5); + } /* enable hpd handle for drm */ hdata->hpd_handle = true; } -static void hdmi_timing_apply(struct hdmi_context *hdata, - const struct hdmi_preset_conf *conf) +static void hdmi_v13_timing_apply(struct hdmi_context *hdata) { + const struct hdmi_v13_preset_conf *conf = + hdmi_v13_confs[hdata->cur_conf].conf; + const struct hdmi_v13_core_regs *core = &conf->core; + const struct hdmi_v13_tg_regs *tg = &conf->tg; + int tries; + + /* setting core registers */ + hdmi_reg_writeb(hdata, HDMI_H_BLANK_0, core->h_blank[0]); + hdmi_reg_writeb(hdata, HDMI_H_BLANK_1, core->h_blank[1]); + hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_0, core->v_blank[0]); + hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_1, core->v_blank[1]); + hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_2, core->v_blank[2]); + hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_0, core->h_v_line[0]); + hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_1, core->h_v_line[1]); + hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_2, core->h_v_line[2]); + hdmi_reg_writeb(hdata, HDMI_VSYNC_POL, core->vsync_pol[0]); + hdmi_reg_writeb(hdata, HDMI_INT_PRO_MODE, core->int_pro_mode[0]); + hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_0, core->v_blank_f[0]); + hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_1, core->v_blank_f[1]); + hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_2, core->v_blank_f[2]); + hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_0, core->h_sync_gen[0]); + hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_1, core->h_sync_gen[1]); + hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_2, core->h_sync_gen[2]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_0, core->v_sync_gen1[0]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_1, core->v_sync_gen1[1]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_2, core->v_sync_gen1[2]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_0, core->v_sync_gen2[0]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_1, core->v_sync_gen2[1]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_2, core->v_sync_gen2[2]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_0, core->v_sync_gen3[0]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_1, core->v_sync_gen3[1]); + hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_2, core->v_sync_gen3[2]); + /* Timing generator registers */ + hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz_l); + hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz_h); + hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_L, tg->hact_st_l); + hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_H, tg->hact_st_h); + hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_L, tg->hact_sz_l); + hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_H, tg->hact_sz_h); + hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_L, tg->v_fsz_l); + hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_H, tg->v_fsz_h); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_L, tg->vsync_l); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_H, tg->vsync_h); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_L, tg->vsync2_l); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_H, tg->vsync2_h); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_L, tg->vact_st_l); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_H, tg->vact_st_h); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_L, tg->vact_sz_l); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_H, tg->vact_sz_h); + hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_L, tg->field_chg_l); + hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg_h); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2_l); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2_h); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l); + hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi_h); + hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi_l); + hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h); + hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l); + hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h); + + /* waiting for HDMIPHY's PLL to get to steady state */ + for (tries = 100; tries; --tries) { + u32 val = hdmi_reg_read(hdata, HDMI_V13_PHY_STATUS); + if (val & HDMI_PHY_STATUS_READY) + break; + mdelay(1); + } + /* steady state not achieved */ + if (tries == 0) { + DRM_ERROR("hdmiphy's pll could not reach steady state.\n"); + hdmi_regs_dump(hdata, "timing apply"); + } + + clk_disable(hdata->res.sclk_hdmi); + clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_hdmiphy); + clk_enable(hdata->res.sclk_hdmi); + + /* enable HDMI and timing generator */ + hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN); + if (core->int_pro_mode[0]) + hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN | + HDMI_FIELD_EN); + else + hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN); +} + +static void hdmi_v14_timing_apply(struct hdmi_context *hdata) +{ + const struct hdmi_preset_conf *conf = hdmi_confs[hdata->cur_conf].conf; const struct hdmi_core_regs *core = &conf->core; const struct hdmi_tg_regs *tg = &conf->tg; int tries; @@ -574,29 +1648,102 @@ static void hdmi_timing_apply(struct hdmi_context *hdata, /* setting core registers */ hdmi_reg_writeb(hdata, HDMI_H_BLANK_0, core->h_blank[0]); hdmi_reg_writeb(hdata, HDMI_H_BLANK_1, core->h_blank[1]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_0, core->v_blank[0]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_1, core->v_blank[1]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_2, core->v_blank[2]); - hdmi_reg_writeb(hdata, HDMI_H_V_LINE_0, core->h_v_line[0]); - hdmi_reg_writeb(hdata, HDMI_H_V_LINE_1, core->h_v_line[1]); - hdmi_reg_writeb(hdata, HDMI_H_V_LINE_2, core->h_v_line[2]); + hdmi_reg_writeb(hdata, HDMI_V2_BLANK_0, core->v2_blank[0]); + hdmi_reg_writeb(hdata, HDMI_V2_BLANK_1, core->v2_blank[1]); + hdmi_reg_writeb(hdata, HDMI_V1_BLANK_0, core->v1_blank[0]); + hdmi_reg_writeb(hdata, HDMI_V1_BLANK_1, core->v1_blank[1]); + hdmi_reg_writeb(hdata, HDMI_V_LINE_0, core->v_line[0]); + hdmi_reg_writeb(hdata, HDMI_V_LINE_1, core->v_line[1]); + hdmi_reg_writeb(hdata, HDMI_H_LINE_0, core->h_line[0]); + hdmi_reg_writeb(hdata, HDMI_H_LINE_1, core->h_line[1]); + hdmi_reg_writeb(hdata, HDMI_HSYNC_POL, core->hsync_pol[0]); hdmi_reg_writeb(hdata, HDMI_VSYNC_POL, core->vsync_pol[0]); hdmi_reg_writeb(hdata, HDMI_INT_PRO_MODE, core->int_pro_mode[0]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_0, core->v_blank_f[0]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_1, core->v_blank_f[1]); - hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_2, core->v_blank_f[2]); - hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_0, core->h_sync_gen[0]); - hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_1, core->h_sync_gen[1]); - hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_2, core->h_sync_gen[2]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_0, core->v_sync_gen1[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_1, core->v_sync_gen1[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_2, core->v_sync_gen1[2]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_0, core->v_sync_gen2[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_1, core->v_sync_gen2[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_2, core->v_sync_gen2[2]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_0, core->v_sync_gen3[0]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_1, core->v_sync_gen3[1]); - hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_2, core->v_sync_gen3[2]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F0_0, core->v_blank_f0[0]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F0_1, core->v_blank_f0[1]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F1_0, core->v_blank_f1[0]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F1_1, core->v_blank_f1[1]); + hdmi_reg_writeb(hdata, HDMI_H_SYNC_START_0, core->h_sync_start[0]); + hdmi_reg_writeb(hdata, HDMI_H_SYNC_START_1, core->h_sync_start[1]); + hdmi_reg_writeb(hdata, HDMI_H_SYNC_END_0, core->h_sync_end[0]); + hdmi_reg_writeb(hdata, HDMI_H_SYNC_END_1, core->h_sync_end[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_2_0, + core->v_sync_line_bef_2[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_2_1, + core->v_sync_line_bef_2[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_1_0, + core->v_sync_line_bef_1[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_1_1, + core->v_sync_line_bef_1[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_2_0, + core->v_sync_line_aft_2[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_2_1, + core->v_sync_line_aft_2[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_1_0, + core->v_sync_line_aft_1[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_1_1, + core->v_sync_line_aft_1[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0, + core->v_sync_line_aft_pxl_2[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_1, + core->v_sync_line_aft_pxl_2[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0, + core->v_sync_line_aft_pxl_1[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_1, + core->v_sync_line_aft_pxl_1[1]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F2_0, core->v_blank_f2[0]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F2_1, core->v_blank_f2[1]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F3_0, core->v_blank_f3[0]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F3_1, core->v_blank_f3[1]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F4_0, core->v_blank_f4[0]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F4_1, core->v_blank_f4[1]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F5_0, core->v_blank_f5[0]); + hdmi_reg_writeb(hdata, HDMI_V_BLANK_F5_1, core->v_blank_f5[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_3_0, + core->v_sync_line_aft_3[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_3_1, + core->v_sync_line_aft_3[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_4_0, + core->v_sync_line_aft_4[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_4_1, + core->v_sync_line_aft_4[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_5_0, + core->v_sync_line_aft_5[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_5_1, + core->v_sync_line_aft_5[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_6_0, + core->v_sync_line_aft_6[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_6_1, + core->v_sync_line_aft_6[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0, + core->v_sync_line_aft_pxl_3[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_1, + core->v_sync_line_aft_pxl_3[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0, + core->v_sync_line_aft_pxl_4[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_1, + core->v_sync_line_aft_pxl_4[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0, + core->v_sync_line_aft_pxl_5[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_1, + core->v_sync_line_aft_pxl_5[1]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0, + core->v_sync_line_aft_pxl_6[0]); + hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_1, + core->v_sync_line_aft_pxl_6[1]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_1_0, core->vact_space_1[0]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_1_1, core->vact_space_1[1]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_2_0, core->vact_space_2[0]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_2_1, core->vact_space_2[1]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_3_0, core->vact_space_3[0]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_3_1, core->vact_space_3[1]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_4_0, core->vact_space_4[0]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_4_1, core->vact_space_4[1]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_5_0, core->vact_space_5[0]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_5_1, core->vact_space_5[1]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_6_0, core->vact_space_6[0]); + hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_6_1, core->vact_space_6[1]); + /* Timing generator registers */ hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz_l); hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz_h); @@ -618,6 +1765,10 @@ static void hdmi_timing_apply(struct hdmi_context *hdata, hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg_h); hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2_l); hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2_h); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST3_L, tg->vact_st3_l); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST3_H, tg->vact_st3_h); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST4_L, tg->vact_st4_l); + hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST4_H, tg->vact_st4_h); hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l); hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h); hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l); @@ -626,10 +1777,11 @@ static void hdmi_timing_apply(struct hdmi_context *hdata, hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h); hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l); hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h); + hdmi_reg_writeb(hdata, HDMI_TG_3D, tg->tg_3d); /* waiting for HDMIPHY's PLL to get to steady state */ for (tries = 100; tries; --tries) { - u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS); + u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS_0); if (val & HDMI_PHY_STATUS_READY) break; mdelay(1); @@ -653,9 +1805,18 @@ static void hdmi_timing_apply(struct hdmi_context *hdata, hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN); } +static void hdmi_timing_apply(struct hdmi_context *hdata) +{ + if (hdata->is_v13) + hdmi_v13_timing_apply(hdata); + else + hdmi_v14_timing_apply(hdata); +} + static void hdmiphy_conf_reset(struct hdmi_context *hdata) { u8 buffer[2]; + u32 reg; clk_disable(hdata->res.sclk_hdmi); clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_pixel); @@ -668,15 +1829,21 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata) if (hdata->hdmiphy_port) i2c_master_send(hdata->hdmiphy_port, buffer, 2); + if (hdata->is_v13) + reg = HDMI_V13_PHY_RSTOUT; + else + reg = HDMI_PHY_RSTOUT; + /* reset hdmiphy */ - hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT); + hdmi_reg_writemask(hdata, reg, ~0, HDMI_PHY_SW_RSTOUT); mdelay(10); - hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT); + hdmi_reg_writemask(hdata, reg, 0, HDMI_PHY_SW_RSTOUT); mdelay(10); } static void hdmiphy_conf_apply(struct hdmi_context *hdata) { + const u8 *hdmiphy_data; u8 buffer[32]; u8 operation[2]; u8 read_buffer[32] = {0, }; @@ -689,7 +1856,12 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata) } /* pixel clock */ - memcpy(buffer, hdmi_confs[hdata->cur_conf].hdmiphy_data, 32); + if (hdata->is_v13) + hdmiphy_data = hdmi_v13_confs[hdata->cur_conf].hdmiphy_data; + else + hdmiphy_data = hdmi_confs[hdata->cur_conf].hdmiphy_data; + + memcpy(buffer, hdmiphy_data, 32); ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32); if (ret != 32) { DRM_ERROR("failed to configure HDMIPHY via I2C\n"); @@ -721,9 +1893,6 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata) static void hdmi_conf_apply(struct hdmi_context *hdata) { - const struct hdmi_preset_conf *conf = - hdmi_confs[hdata->cur_conf].conf; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); hdmiphy_conf_reset(hdata); @@ -731,13 +1900,55 @@ static void hdmi_conf_apply(struct hdmi_context *hdata) hdmi_conf_reset(hdata); hdmi_conf_init(hdata); + hdmi_audio_init(hdata); /* setting core registers */ - hdmi_timing_apply(hdata, conf); + hdmi_timing_apply(hdata); + hdmi_audio_control(hdata, true); hdmi_regs_dump(hdata, "start"); } +static void hdmi_mode_fixup(void *ctx, struct drm_connector *connector, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_display_mode *m; + struct hdmi_context *hdata = (struct hdmi_context *)ctx; + int index; + + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + drm_mode_set_crtcinfo(adjusted_mode, 0); + + if (hdata->is_v13) + index = hdmi_v13_conf_index(adjusted_mode); + else + index = hdmi_v14_conf_index(adjusted_mode); + + /* just return if user desired mode exists. */ + if (index >= 0) + return; + + /* + * otherwise, find the most suitable mode among modes and change it + * to adjusted_mode. + */ + list_for_each_entry(m, &connector->modes, head) { + if (hdata->is_v13) + index = hdmi_v13_conf_index(m); + else + index = hdmi_v14_conf_index(m); + + if (index >= 0) { + DRM_INFO("desired mode doesn't exist so\n"); + DRM_INFO("use the most suitable mode among modes.\n"); + memcpy(adjusted_mode, m, sizeof(*m)); + break; + } + } +} + static void hdmi_mode_set(void *ctx, void *mode) { struct hdmi_context *hdata = (struct hdmi_context *)ctx; @@ -745,13 +1956,22 @@ static void hdmi_mode_set(void *ctx, void *mode) DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - conf_idx = hdmi_conf_index(mode); - if (conf_idx >= 0 && conf_idx < ARRAY_SIZE(hdmi_confs)) + conf_idx = hdmi_conf_index(hdata, mode); + if (conf_idx >= 0) hdata->cur_conf = conf_idx; else DRM_DEBUG_KMS("not supported mode\n"); } +static void hdmi_get_max_resol(void *ctx, unsigned int *width, + unsigned int *height) +{ + DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); + + *width = MAX_WIDTH; + *height = MAX_HEIGHT; +} + static void hdmi_commit(void *ctx) { struct hdmi_context *hdata = (struct hdmi_context *)ctx; @@ -770,13 +1990,16 @@ static void hdmi_disable(void *ctx) DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); if (hdata->enabled) { + hdmi_audio_control(hdata, false); hdmiphy_conf_reset(hdata); hdmi_conf_reset(hdata); } } static struct exynos_hdmi_manager_ops manager_ops = { + .mode_fixup = hdmi_mode_fixup, .mode_set = hdmi_mode_set, + .get_max_resol = hdmi_get_max_resol, .commit = hdmi_commit, .disable = hdmi_disable, }; @@ -926,7 +2149,7 @@ static void hdmi_resource_poweron(struct hdmi_context *hdata) hdmiphy_conf_reset(hdata); hdmi_conf_reset(hdata); hdmi_conf_init(hdata); - + hdmi_audio_init(hdata); } static void hdmi_resource_poweroff(struct hdmi_context *hdata) @@ -978,14 +2201,12 @@ void hdmi_attach_ddc_client(struct i2c_client *ddc) if (ddc) hdmi_ddc = ddc; } -EXPORT_SYMBOL(hdmi_attach_ddc_client); void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy) { if (hdmiphy) hdmi_hdmiphy = hdmiphy; } -EXPORT_SYMBOL(hdmi_attach_hdmiphy_client); static int __devinit hdmi_probe(struct platform_device *pdev) { @@ -1022,6 +2243,7 @@ static int __devinit hdmi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, drm_hdmi_ctx); + hdata->is_v13 = pdata->is_v13; hdata->default_win = pdata->default_win; hdata->default_timing = &pdata->timing; hdata->default_bpp = pdata->bpp; @@ -1167,10 +2389,3 @@ struct platform_driver hdmi_driver = { .pm = &hdmi_pm_ops, }, }; -EXPORT_SYMBOL(hdmi_driver); - -MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>"); -MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); -MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); -MODULE_DESCRIPTION("Samsung DRM HDMI core Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.h b/drivers/gpu/drm/exynos/exynos_hdmi.h index 31d6cf84c1aa..1c3b6d8f1fe7 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.h +++ b/drivers/gpu/drm/exynos/exynos_hdmi.h @@ -28,56 +28,6 @@ #ifndef _EXYNOS_HDMI_H_ #define _EXYNOS_HDMI_H_ -struct hdmi_conf { - int width; - int height; - int vrefresh; - bool interlace; - const u8 *hdmiphy_data; - const struct hdmi_preset_conf *conf; -}; - -struct hdmi_resources { - struct clk *hdmi; - struct clk *sclk_hdmi; - struct clk *sclk_pixel; - struct clk *sclk_hdmiphy; - struct clk *hdmiphy; - struct regulator_bulk_data *regul_bulk; - int regul_count; -}; - -struct hdmi_context { - struct device *dev; - struct drm_device *drm_dev; - struct fb_videomode *default_timing; - unsigned int default_win; - unsigned int default_bpp; - bool hpd_handle; - bool enabled; - - struct resource *regs_res; - /** base address of HDMI registers */ - void __iomem *regs; - /** HDMI hotplug interrupt */ - unsigned int irq; - /** workqueue for delayed work */ - struct workqueue_struct *wq; - /** hotplug handling work */ - struct work_struct hotplug_work; - - struct i2c_client *ddc_port; - struct i2c_client *hdmiphy_port; - - /** current hdmiphy conf index */ - int cur_conf; - /** other resources */ - struct hdmi_resources res; - - void *parent_ctx; -}; - - void hdmi_attach_ddc_client(struct i2c_client *ddc); void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy); diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 93846e810e38..4d5f41e19527 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -36,11 +36,57 @@ #include "exynos_drm_drv.h" #include "exynos_drm_hdmi.h" -#include "exynos_hdmi.h" -#include "exynos_mixer.h" + +#define HDMI_OVERLAY_NUMBER 3 #define get_mixer_context(dev) platform_get_drvdata(to_platform_device(dev)) +struct hdmi_win_data { + dma_addr_t dma_addr; + void __iomem *vaddr; + dma_addr_t chroma_dma_addr; + void __iomem *chroma_vaddr; + uint32_t pixel_format; + unsigned int bpp; + unsigned int crtc_x; + unsigned int crtc_y; + unsigned int crtc_width; + unsigned int crtc_height; + unsigned int fb_x; + unsigned int fb_y; + unsigned int fb_width; + unsigned int fb_height; + unsigned int mode_width; + unsigned int mode_height; + unsigned int scan_flags; +}; + +struct mixer_resources { + struct device *dev; + int irq; + void __iomem *mixer_regs; + void __iomem *vp_regs; + spinlock_t reg_slock; + struct clk *mixer; + struct clk *vp; + struct clk *sclk_mixer; + struct clk *sclk_hdmi; + struct clk *sclk_dac; +}; + +struct mixer_context { + struct fb_videomode *default_timing; + unsigned int default_win; + unsigned int default_bpp; + unsigned int irq; + int pipe; + bool interlace; + bool vp_enabled; + + struct mixer_resources mixer_res; + struct hdmi_win_data win_data[HDMI_OVERLAY_NUMBER]; +}; + static const u8 filter_y_horiz_tap8[] = { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, @@ -1066,10 +1112,3 @@ struct platform_driver mixer_driver = { .probe = mixer_probe, .remove = __devexit_p(mixer_remove), }; -EXPORT_SYMBOL(mixer_driver); - -MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>"); -MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); -MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); -MODULE_DESCRIPTION("Samsung DRM HDMI mixer Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/exynos/exynos_mixer.h b/drivers/gpu/drm/exynos/exynos_mixer.h deleted file mode 100644 index cebacfefc077..000000000000 --- a/drivers/gpu/drm/exynos/exynos_mixer.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Authors: - * Seung-Woo Kim <sw0312.kim@samsung.com> - * 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_MIXER_H_ -#define _EXYNOS_MIXER_H_ - -#define HDMI_OVERLAY_NUMBER 3 - -struct hdmi_win_data { - dma_addr_t dma_addr; - void __iomem *vaddr; - dma_addr_t chroma_dma_addr; - void __iomem *chroma_vaddr; - uint32_t pixel_format; - unsigned int bpp; - unsigned int crtc_x; - unsigned int crtc_y; - unsigned int crtc_width; - unsigned int crtc_height; - unsigned int fb_x; - unsigned int fb_y; - unsigned int fb_width; - unsigned int fb_height; - unsigned int mode_width; - unsigned int mode_height; - unsigned int scan_flags; -}; - -struct mixer_resources { - struct device *dev; - /** interrupt index */ - int irq; - /** pointer to Mixer registers */ - void __iomem *mixer_regs; - /** pointer to Video Processor registers */ - void __iomem *vp_regs; - /** spinlock for protection of registers */ - spinlock_t reg_slock; - /** other resources */ - struct clk *mixer; - struct clk *vp; - struct clk *sclk_mixer; - struct clk *sclk_hdmi; - struct clk *sclk_dac; -}; - -struct mixer_context { - unsigned int default_win; - struct fb_videomode *default_timing; - unsigned int default_bpp; - - /** mixer interrupt */ - unsigned int irq; - /** current crtc pipe for vblank */ - int pipe; - /** interlace scan mode */ - bool interlace; - /** vp enabled status */ - bool vp_enabled; - - /** mixer and vp resources */ - struct mixer_resources mixer_res; - - /** overlay window data */ - struct hdmi_win_data win_data[HDMI_OVERLAY_NUMBER]; -}; - -#endif diff --git a/drivers/gpu/drm/exynos/regs-hdmi.h b/drivers/gpu/drm/exynos/regs-hdmi.h index 72e6b52be740..3c04bea842ce 100644 --- a/drivers/gpu/drm/exynos/regs-hdmi.h +++ b/drivers/gpu/drm/exynos/regs-hdmi.h @@ -19,64 +19,67 @@ * 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_PHY_RSTOUT HDMI_CTRL_BASE(0x0014) -#define HDMI_PHY_VPLL HDMI_CTRL_BASE(0x0018) -#define HDMI_PHY_CMU HDMI_CTRL_BASE(0x001C) -#define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0020) +#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_PHY_STATUS HDMI_CORE_BASE(0x0014) +#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_BLUE_SCREEN_0 HDMI_CORE_BASE(0x0050) -#define HDMI_BLUE_SCREEN_1 HDMI_CORE_BASE(0x0054) -#define HDMI_BLUE_SCREEN_2 HDMI_CORE_BASE(0x0058) +#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_V_BLANK_0 HDMI_CORE_BASE(0x00B0) -#define HDMI_V_BLANK_1 HDMI_CORE_BASE(0x00B4) -#define HDMI_V_BLANK_2 HDMI_CORE_BASE(0x00B8) -#define HDMI_H_V_LINE_0 HDMI_CORE_BASE(0x00C0) -#define HDMI_H_V_LINE_1 HDMI_CORE_BASE(0x00C4) -#define HDMI_H_V_LINE_2 HDMI_CORE_BASE(0x00C8) +#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_V_BLANK_F_0 HDMI_CORE_BASE(0x0110) -#define HDMI_V_BLANK_F_1 HDMI_CORE_BASE(0x0114) -#define HDMI_V_BLANK_F_2 HDMI_CORE_BASE(0x0118) -#define HDMI_H_SYNC_GEN_0 HDMI_CORE_BASE(0x0120) -#define HDMI_H_SYNC_GEN_1 HDMI_CORE_BASE(0x0124) -#define HDMI_H_SYNC_GEN_2 HDMI_CORE_BASE(0x0128) -#define HDMI_V_SYNC_GEN_1_0 HDMI_CORE_BASE(0x0130) -#define HDMI_V_SYNC_GEN_1_1 HDMI_CORE_BASE(0x0134) -#define HDMI_V_SYNC_GEN_1_2 HDMI_CORE_BASE(0x0138) -#define HDMI_V_SYNC_GEN_2_0 HDMI_CORE_BASE(0x0140) -#define HDMI_V_SYNC_GEN_2_1 HDMI_CORE_BASE(0x0144) -#define HDMI_V_SYNC_GEN_2_2 HDMI_CORE_BASE(0x0148) -#define HDMI_V_SYNC_GEN_3_0 HDMI_CORE_BASE(0x0150) -#define HDMI_V_SYNC_GEN_3_1 HDMI_CORE_BASE(0x0154) -#define HDMI_V_SYNC_GEN_3_2 HDMI_CORE_BASE(0x0158) -#define HDMI_ACR_CON HDMI_CORE_BASE(0x0180) -#define HDMI_AVI_CON HDMI_CORE_BASE(0x0300) -#define HDMI_AVI_BYTE(n) HDMI_CORE_BASE(0x0320 + 4 * (n)) -#define HDMI_DC_CONTROL HDMI_CORE_BASE(0x05C0) -#define HDMI_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x05C4) -#define HDMI_HPD_GEN HDMI_CORE_BASE(0x05C8) -#define HDMI_AUI_CON HDMI_CORE_BASE(0x0360) -#define HDMI_SPD_CON HDMI_CORE_BASE(0x0400) +#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) @@ -130,6 +133,9 @@ /* 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 */ @@ -138,10 +144,418 @@ /* 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 */ |