* Re: [PATCH 03/15] OMAPDSS: fix DPI and SDI device ids
From: Tony Lindgren @ 2013-09-17 16:20 UTC (permalink / raw)
To: Tomi Valkeinen; +Cc: linux-omap, linux-fbdev, Archit Taneja
In-Reply-To: <1377783120-14001-4-git-send-email-tomi.valkeinen@ti.com>
* Tomi Valkeinen <tomi.valkeinen@ti.com> [130829 06:40]:
> The DPI and SDI platform devices are currently created with the ID of
> -1. The ID doesn't currently affect anything.
>
> However, we have added regulator supply entries for "omapdss_dpi.0" and
> "omapdss_sdi.0" to the board files, although these supply entries are
> not yet used. As the ID used for the devices is -1, these regulator
> supply entries will not work.
>
> To fix the issue, assign ID of 0 to the devices. In the future there may
> be more than one DPI or SDI output, so it makes sense to have a proper
> ID for them.
This should be safe to queue by you along with other DSS patches:
Acked-by: Tony Lindgren <tony@atomide.com>
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
> ---
> arch/arm/mach-omap2/display.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/display.c b/arch/arm/mach-omap2/display.c
> index ff37be1..03a0516 100644
> --- a/arch/arm/mach-omap2/display.c
> +++ b/arch/arm/mach-omap2/display.c
> @@ -400,7 +400,7 @@ int __init omap_display_init(struct omap_dss_board_info *board_data)
>
> /* Create devices for DPI and SDI */
>
> - pdev = create_simple_dss_pdev("omapdss_dpi", -1,
> + pdev = create_simple_dss_pdev("omapdss_dpi", 0,
> board_data, sizeof(*board_data), dss_pdev);
> if (IS_ERR(pdev)) {
> pr_err("Could not build platform_device for omapdss_dpi\n");
> @@ -408,7 +408,7 @@ int __init omap_display_init(struct omap_dss_board_info *board_data)
> }
>
> if (cpu_is_omap34xx()) {
> - pdev = create_simple_dss_pdev("omapdss_sdi", -1,
> + pdev = create_simple_dss_pdev("omapdss_sdi", 0,
> board_data, sizeof(*board_data), dss_pdev);
> if (IS_ERR(pdev)) {
> pr_err("Could not build platform_device for omapdss_sdi\n");
> --
> 1.8.1.2
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v11 0/8] PHY framework
From: Felipe Balbi @ 2013-09-17 15:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <5226F5E2.5010409@ti.com>
[-- Attachment #1: Type: text/plain, Size: 2279 bytes --]
On Wed, Sep 04, 2013 at 02:27:06PM +0530, Kishon Vijay Abraham I wrote:
> On Tuesday 03 September 2013 09:20 PM, Greg KH wrote:
> > On Tue, Sep 03, 2013 at 08:55:23PM +0530, Kishon Vijay Abraham I wrote:
> >> Hi Greg,
> >>
> >> On Wednesday 28 August 2013 12:50 AM, Felipe Balbi wrote:
> >>> Hi,
> >>>
> >>> On Mon, Aug 26, 2013 at 01:44:49PM +0530, Kishon Vijay Abraham I wrote:
> >>>> On Wednesday 21 August 2013 11:16 AM, Kishon Vijay Abraham I wrote:
> >>>>> Added a generic PHY framework that provides a set of APIs for the PHY drivers
> >>>>> to create/destroy a PHY and APIs for the PHY users to obtain a reference to
> >>>>> the PHY with or without using phandle.
> >>>>>
> >>>>> This framework will be of use only to devices that uses external PHY (PHY
> >>>>> functionality is not embedded within the controller).
> >>>>>
> >>>>> The intention of creating this framework is to bring the phy drivers spread
> >>>>> all over the Linux kernel to drivers/phy to increase code re-use and to
> >>>>> increase code maintainability.
> >>>>>
> >>>>> Comments to make PHY as bus wasn't done because PHY devices can be part of
> >>>>> other bus and making a same device attached to multiple bus leads to bad
> >>>>> design.
> >>>>>
> >>>>> If the PHY driver has to send notification on connect/disconnect, the PHY
> >>>>> driver should make use of the extcon framework. Using this susbsystem
> >>>>> to use extcon framwork will have to be analysed.
> >>>>>
> >>>>> You can find this patch series @
> >>>>> git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy.git testing
> >>>>
> >>>> Looks like there are not further comments on this series. Can you take this in
> >>>> your misc tree?
> >>>
> >>> Do you want me to queue these for you ? There are quite a few users for
> >>> this framework already and I know of at least 2 others which will show
> >>> up for v3.13.
> >>
> >> Can you queue this patch series? There are quite a few users already for this
> >> framework.
> >
> > It will have to wait for 3.13 as the merge window for new features has
> > been closed for a week or so. Sorry, I'll queue this up after 3.12-rc1
> > is out.
>
> Alright, thanks.
Just a gentle ping on this one...
cheers
--
balbi
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH] pwm-backlight: allow for non-increasing brightness levels
From: Mike Dunn @ 2013-09-17 14:23 UTC (permalink / raw)
To: Sascha Hauer
Cc: thierry.reding-Re5JQEeQqe8AvxtiuMwx3w, Richard Purdie, Jingoo Han,
Jean-Christophe Plagniol-Villard, Tomi Valkeinen, Grant Likely,
Rob Herring, linux-pwm-u79uwXL29TY76Z2rM5mHXA,
linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Robert Jarzmik, Marek Vasut
In-Reply-To: <20130917093622.GB30088-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
On 09/17/2013 02:36 AM, Sascha Hauer wrote:
> Hi Mike,
>
> On Thu, Sep 12, 2013 at 11:35:52AM -0700, Mike Dunn wrote:
>> Currently the driver assumes that the values specified in the brightness-levels
>> device tree property increase as they are parsed from left to right. But boards
>> that invert the signal between the PWM output and the backlight will need to
>> specify decreasing brightness-levels. This patch removes the assumption that
>> the last element of the array is the max value, and instead searches the array
>> for the max value and uses that as the normalizing value when determining the
>> duty cycle.
>
> Note there's also support for inverted PWMs in the PWM framework
> provided your hardware supports this.
Yes, and in fact my first solution was to implement simulated polarity inversion
in the pwm driver, but that was shot down because polarity inversion is not
actually supported by the pwm hardware. My inverter is external in the path
between the pwm output and the backlight. Not sure of the reason for its presence.
Thanks,
Mike
^ permalink raw reply
* Re: [PATCH 15/19] video: s1d13xxxfb: use dev_get_platdata()
From: Kristoffer Eriksson @ 2013-09-17 14:19 UTC (permalink / raw)
To: linux-fbdev
In-Reply-To: <001501ceb364$340fdf40$9c2f9dc0$%han@samsung.com>
Acked-by: Kristoffer Ericson <kristoffer.ericson@gmail.com>
Jingoo Han skrev 2013-09-17 07:10:
> Use the wrapper function for retrieving the platform data instead of
> accessing dev->platform_data directly. This is a cosmetic change
> to make the code simpler and enhance the readability.
>
> Signed-off-by: Jingoo Han <jg1.han@samsung.com>
> ---
> drivers/video/s1d13xxxfb.c | 12 ++++++------
> 1 file changed, 6 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/video/s1d13xxxfb.c b/drivers/video/s1d13xxxfb.c
> index 05c2dc3..1399a46 100644
> --- a/drivers/video/s1d13xxxfb.c
> +++ b/drivers/video/s1d13xxxfb.c
> @@ -777,8 +777,8 @@ static int s1d13xxxfb_probe(struct platform_device *pdev)
> printk(KERN_INFO "Epson S1D13XXX FB Driver\n");
>
> /* enable platform-dependent hardware glue, if any */
> - if (pdev->dev.platform_data)
> - pdata = pdev->dev.platform_data;
> + if (dev_get_platdata(&pdev->dev))
> + pdata = dev_get_platdata(&pdev->dev);
>
> if (pdata && pdata->platform_init_video)
> pdata->platform_init_video();
> @@ -923,8 +923,8 @@ static int s1d13xxxfb_suspend(struct platform_device *dev, pm_message_t state)
> lcd_enable(s1dfb, 0);
> crt_enable(s1dfb, 0);
>
> - if (dev->dev.platform_data)
> - pdata = dev->dev.platform_data;
> + if (dev_get_platdata(&dev->dev))
> + pdata = dev_get_platdata(&dev->dev);
>
> #if 0
> if (!s1dfb->disp_save)
> @@ -973,8 +973,8 @@ static int s1d13xxxfb_resume(struct platform_device *dev)
> while ((s1d13xxxfb_readreg(s1dfb, S1DREG_PS_STATUS) & 0x01))
> udelay(10);
>
> - if (dev->dev.platform_data)
> - pdata = dev->dev.platform_data;
> + if (dev_get_platdata(&dev->dev))
> + pdata = dev_get_platdata(&dev->dev);
>
> if (s1dfb->regs_save) {
> /* will write RO regs, *should* get away with it :) */
^ permalink raw reply
* [PATCH v2 2/2] dma-buf: Add user interfaces for dmabuf sync support
From: Inki Dae @ 2013-09-17 12:23 UTC (permalink / raw)
To: dri-devel, linux-fbdev, linux-arm-kernel, linux-media,
linaro-kernel
Cc: Roger.Teague, jesse.barker, jesse.barker, maarten.lankhorst,
sumit.semwal, kyungmin.park, myungjoo.ham, Inki Dae
In-Reply-To: <1379420616-9194-1-git-send-email-inki.dae@samsung.com>
This patch adds lock and poll callbacks to dma buf file operations,
and these callbacks will be called by fcntl and select system calls.
fcntl and select system calls can be used to wait for the completion
of DMA or CPU access to a shared dmabuf. The difference of them is
fcntl system call takes a lock after the completion but select system
call doesn't. So in case of fcntl system call, it's useful when a task
wants to access a shared dmabuf without any broken. On the other hand,
it's useful when a task wants to just wait for the completion.
Changelog v2:
- Add select system call support.
. The purpose of this feature is to wait for the completion of DMA or
CPU access to a dmabuf without that caller locks the dmabuf again
after the completion.
That is useful when caller wants to be aware of the completion of
DMA access to the dmabuf, and the caller doesn't use intefaces for
the DMA device driver.
Signed-off-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
drivers/base/dma-buf.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 81 insertions(+)
diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c
index 3985751..73234ba 100644
--- a/drivers/base/dma-buf.c
+++ b/drivers/base/dma-buf.c
@@ -29,6 +29,7 @@
#include <linux/export.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/poll.h>
#include <linux/dmabuf-sync.h>
static inline int is_dma_buf_file(struct file *);
@@ -106,10 +107,90 @@ static loff_t dma_buf_llseek(struct file *file, loff_t offset, int whence)
return base + offset;
}
+static unsigned int dma_buf_poll(struct file *filp,
+ struct poll_table_struct *poll)
+{
+ struct dma_buf *dmabuf;
+ struct dmabuf_sync_reservation *robj;
+ int ret = 0;
+
+ if (!is_dma_buf_file(filp))
+ return POLLERR;
+
+ dmabuf = filp->private_data;
+ if (!dmabuf || !dmabuf->sync)
+ return POLLERR;
+
+ robj = dmabuf->sync;
+
+ mutex_lock(&robj->lock);
+
+ robj->polled = true;
+
+ /*
+ * CPU or DMA access to this buffer has been completed, and
+ * the blocked task has been waked up. Return poll event
+ * so that the task can get out of select().
+ */
+ if (robj->poll_event) {
+ robj->poll_event = false;
+ mutex_unlock(&robj->lock);
+ return POLLIN | POLLOUT;
+ }
+
+ /*
+ * There is no anyone accessing this buffer so just return.
+ */
+ if (!robj->locked) {
+ mutex_unlock(&robj->lock);
+ return POLLIN | POLLOUT;
+ }
+
+ poll_wait(filp, &robj->poll_wait, poll);
+
+ mutex_unlock(&robj->lock);
+
+ return ret;
+}
+
+static int dma_buf_lock(struct file *file, int cmd, struct file_lock *fl)
+{
+ struct dma_buf *dmabuf;
+ unsigned int type;
+ bool wait = false;
+
+ if (!is_dma_buf_file(file))
+ return -EINVAL;
+
+ dmabuf = file->private_data;
+
+ if ((fl->fl_type & F_UNLCK) = F_UNLCK) {
+ dmabuf_sync_single_unlock(dmabuf);
+ return 0;
+ }
+
+ /* convert flock type to dmabuf sync type. */
+ if ((fl->fl_type & F_WRLCK) = F_WRLCK)
+ type = DMA_BUF_ACCESS_W;
+ else if ((fl->fl_type & F_RDLCK) = F_RDLCK)
+ type = DMA_BUF_ACCESS_R;
+ else
+ return -EINVAL;
+
+ if (fl->fl_flags & FL_SLEEP)
+ wait = true;
+
+ /* TODO. the locking to certain region should also be considered. */
+
+ return dmabuf_sync_single_lock(dmabuf, type, wait);
+}
+
static const struct file_operations dma_buf_fops = {
.release = dma_buf_release,
.mmap = dma_buf_mmap_internal,
.llseek = dma_buf_llseek,
+ .poll = dma_buf_poll,
+ .lock = dma_buf_lock,
};
/*
--
1.7.9.5
^ permalink raw reply related
* [PATCH v9 1/2] dmabuf-sync: Add a buffer synchronization framework
From: Inki Dae @ 2013-09-17 12:23 UTC (permalink / raw)
To: dri-devel, linux-fbdev, linux-arm-kernel, linux-media,
linaro-kernel
Cc: Roger.Teague, jesse.barker, jesse.barker, maarten.lankhorst,
sumit.semwal, kyungmin.park, myungjoo.ham, Inki Dae
In-Reply-To: <1379420616-9194-1-git-send-email-inki.dae@samsung.com>
This patch adds a buffer synchronization framework based on DMA BUF[1]
and and based on ww-mutexes[2] for lock mechanism, and is rebased on
linux-3.12-rc1
The purpose of this framework is to provide not only buffer access control
to CPU and DMA but also easy-to-use interfaces for device drivers and
user application. This framework can be used for all dma devices using
system memory as dma buffer, especially for most ARM based SoCs.
Changelog v9:
- Delete only one sync object matched at DEL_OBJ_FROM_RSV macro.
- Fix memory leak issue at dmabuf_sync_single_lock()
Changelog v8:
Consider the write-and-then-read ordering pointed out by David Herrmann,
- The ordering issue means that a task don't take a lock to the dmabuf
so this task would be stalled even though this task requested a lock to
the dmabuf between other task unlocked and tries to lock the dmabuf
again. For this, we have added a wait event mechanism using only generic
APIs, wait_event_timeout and wake_up functions.
The below is how to handle the ordering issue using this mechanism:
1. Check if there is a sync object added prior to current task's one.
2. If exists, it unlocks the dmabuf so that other task can take a lock
to the dmabuf first.
3. Wait for the wake up event from other task: current task will be
waked up when other task unlocks the dmabuf.
4. Take a lock to the dmabuf again.
- Code cleanups.
Changelog v7:
Fix things pointed out by Konrad Rzeszutek Wilk,
- Use EXPORT_SYMBOL_GPL instead of EXPORT_SYMBOL.
- make sure to unlock and unreference all dmabuf objects
when dmabuf_sync_fini() is called.
- Add more comments.
- code cleanups.
Changelog v6:
- Fix sync lock to multiple reads.
- Add select system call support.
. Wake up poll_wait when a dmabuf is unlocked.
- Remove unnecessary the use of mutex lock.
- Add private backend ops callbacks.
. This ops has one callback for device drivers to clean up their
sync object resource when the sync object is freed. For this,
device drivers should implement the free callback properly.
- Update document file.
Changelog v5:
- Rmove a dependence on reservation_object: the reservation_object is used
to hook up to ttm and dma-buf for easy sharing of reservations across
devices. However, the dmabuf sync can be used for all dma devices; v4l2
and drm based drivers, so doesn't need the reservation_object anymore.
With regared to this, it adds 'void *sync' to dma_buf structure.
- All patches are rebased on mainline, Linux v3.10.
Changelog v4:
- Add user side interface for buffer synchronization mechanism and update
descriptions related to the user side interface.
Changelog v3:
- remove cache operation relevant codes and update document file.
Changelog v2:
- use atomic_add_unless to avoid potential bug.
- add a macro for checking valid access type.
- code clean.
The mechanism of this framework has the following steps,
1. Register dmabufs to a sync object - A task gets a new sync object and
can add one or more dmabufs that the task wants to access.
This registering should be performed when a device context or an event
context such as a page flip event is created or before CPU accesses a shared
buffer.
dma_buf_sync_get(a sync object, a dmabuf);
2. Lock a sync object - A task tries to lock all dmabufs added in its own
sync object. Basically, the lock mechanism uses ww-mutex[1] to avoid dead
lock issue and for race condition between CPU and CPU, CPU and DMA, and DMA
and DMA. Taking a lock means that others cannot access all locked dmabufs
until the task that locked the corresponding dmabufs, unlocks all the locked
dmabufs.
This locking should be performed before DMA or CPU accesses these dmabufs.
dma_buf_sync_lock(a sync object);
3. Unlock a sync object - The task unlocks all dmabufs added in its own sync
object. The unlock means that the DMA or CPU accesses to the dmabufs have
been completed so that others may access them.
This unlocking should be performed after DMA or CPU has completed accesses
to the dmabufs.
dma_buf_sync_unlock(a sync object);
4. Unregister one or all dmabufs from a sync object - A task unregisters
the given dmabufs from the sync object. This means that the task dosen't
want to lock the dmabufs.
The unregistering should be performed after DMA or CPU has completed
accesses to the dmabufs or when dma_buf_sync_lock() is failed.
dma_buf_sync_put(a sync object, a dmabuf);
dma_buf_sync_put_all(a sync object);
The described steps may be summarized as:
get -> lock -> CPU or DMA access to a buffer/s -> unlock -> put
This framework includes the following two features.
1. read (shared) and write (exclusive) locks - A task is required to declare
the access type when the task tries to register a dmabuf;
READ, WRITE, READ DMA, or WRITE DMA.
The below is example codes,
struct dmabuf_sync *sync;
sync = dmabuf_sync_init(...);
...
dmabuf_sync_get(sync, dmabuf, DMA_BUF_ACCESS_R);
...
And the below can be used as access types:
DMA_BUF_ACCESS_R - CPU will access a buffer for read.
DMA_BUF_ACCESS_W - CPU will access a buffer for read or write.
DMA_BUF_ACCESS_DMA_R - DMA will access a buffer for read
DMA_BUF_ACCESS_DMA_W - DMA will access a buffer for read or
write.
2. Mandatory resource releasing - a task cannot hold a lock indefinitely.
A task may never try to unlock a buffer after taking a lock to the buffer.
In this case, a timer handler to the corresponding sync object is called
in five (default) seconds and then the timed-out buffer is unlocked by work
queue handler to avoid lockups and to enforce resources of the buffer.
The below is how to use interfaces for device driver:
1. Allocate and Initialize a sync object:
static void xxx_dmabuf_sync_free(void *priv)
{
struct xxx_context *ctx = priv;
if (!ctx)
return;
ctx->sync = NULL;
}
...
static struct dmabuf_sync_priv_ops driver_specific_ops = {
.free = xxx_dmabuf_sync_free,
};
...
struct dmabuf_sync *sync;
sync = dmabuf_sync_init("test sync", &driver_specific_ops, ctx);
...
2. Add a dmabuf to the sync object when setting up dma buffer relevant
registers:
dmabuf_sync_get(sync, dmabuf, DMA_BUF_ACCESS_READ);
...
3. Lock all dmabufs of the sync object before DMA or CPU accesses
the dmabufs:
dmabuf_sync_lock(sync);
...
4. Now CPU or DMA can access all dmabufs locked in step 3.
5. Unlock all dmabufs added in a sync object after DMA or CPU access
to these dmabufs is completed:
dmabuf_sync_unlock(sync);
And call the following functions to release all resources,
dmabuf_sync_put_all(sync);
dmabuf_sync_fini(sync);
You can refer to actual example codes:
"drm/exynos: add dmabuf sync support for g2d driver" and
"drm/exynos: add dmabuf sync support for kms framework" from
https://git.kernel.org/cgit/linux/kernel/git/daeinki/
drm-exynos.git/log/?h=dmabuf-sync
And this framework includes fcntl[3] and select system call as interfaces
exported to user. As you know, user sees a buffer object as a dma-buf file
descriptor. fcntl() call with the file descriptor means to lock some buffer
region being managed by the dma-buf object. And select() call with the file
descriptor means to poll the completion event of CPU or DMA access to
the dma-buf.
The below is how to use interfaces for user application:
fcntl system call:
struct flock filelock;
1. Lock a dma buf:
filelock.l_type = F_WRLCK or F_RDLCK;
/* lock entire region to the dma buf. */
filelock.lwhence = SEEK_CUR;
filelock.l_start = 0;
filelock.l_len = 0;
fcntl(dmabuf fd, F_SETLKW or F_SETLK, &filelock);
...
CPU access to the dma buf
2. Unlock a dma buf:
filelock.l_type = F_UNLCK;
fcntl(dmabuf fd, F_SETLKW or F_SETLK, &filelock);
close(dmabuf fd) call would also unlock the dma buf. And for more
detail, please refer to [3]
select system call:
fd_set wdfs or rdfs;
FD_ZERO(&wdfs or &rdfs);
FD_SET(fd, &wdfs or &rdfs);
select(fd + 1, &rdfs, NULL, NULL, NULL);
or
select(fd + 1, NULL, &wdfs, NULL, NULL);
Every time select system call is called, a caller will wait for
the completion of DMA or CPU access to a shared buffer if there
is someone accessing the shared buffer. If no anyone then select
system call will be returned at once.
References:
[1] http://lwn.net/Articles/470339/
[2] https://patchwork.kernel.org/patch/2625361/
[3] http://linux.die.net/man/2/fcntl
Signed-off-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
Documentation/dma-buf-sync.txt | 286 ++++++++++++
drivers/base/Kconfig | 7 +
drivers/base/Makefile | 1 +
drivers/base/dma-buf.c | 5 +
drivers/base/dmabuf-sync.c | 951 ++++++++++++++++++++++++++++++++++++++++
include/linux/dma-buf.h | 16 +
include/linux/dmabuf-sync.h | 257 +++++++++++
7 files changed, 1523 insertions(+)
create mode 100644 Documentation/dma-buf-sync.txt
create mode 100644 drivers/base/dmabuf-sync.c
create mode 100644 include/linux/dmabuf-sync.h
diff --git a/Documentation/dma-buf-sync.txt b/Documentation/dma-buf-sync.txt
new file mode 100644
index 0000000..5945c8a
--- /dev/null
+++ b/Documentation/dma-buf-sync.txt
@@ -0,0 +1,286 @@
+ DMA Buffer Synchronization Framework
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Inki Dae
+ <inki dot dae at samsung dot com>
+ <daeinki at gmail dot com>
+
+This document is a guide for device-driver writers describing the DMA buffer
+synchronization API. This document also describes how to use the API to
+use buffer synchronization mechanism between DMA and DMA, CPU and DMA, and
+CPU and CPU.
+
+The DMA Buffer synchronization API provides buffer synchronization mechanism;
+i.e., buffer access control to CPU and DMA, and easy-to-use interfaces for
+device drivers and user application. And this API can be used for all dma
+devices using system memory as dma buffer, especially for most ARM based SoCs.
+
+
+Motivation
+----------
+
+Buffer synchronization issue between DMA and DMA:
+ Sharing a buffer, a device cannot be aware of when the other device
+ will access the shared buffer: a device may access a buffer containing
+ wrong data if the device accesses the shared buffer while another
+ device is still accessing the shared buffer.
+ Therefore, a user process should have waited for the completion of DMA
+ access by another device before a device tries to access the shared
+ buffer.
+
+Buffer synchronization issue between CPU and DMA:
+ A user process should consider that when having to send a buffer, filled
+ by CPU, to a device driver for the device driver to access the buffer as
+ a input buffer while CPU and DMA are sharing the buffer.
+ This means that the user process needs to understand how the device
+ driver is worked. Hence, the conventional mechanism not only makes
+ user application complicated but also incurs performance overhead.
+
+Buffer synchronization issue between CPU and CPU:
+ In case that two processes share one buffer; shared with DMA also,
+ they may need some mechanism to allow process B to access the shared
+ buffer after the completion of CPU access by process A.
+ Therefore, process B should have waited for the completion of CPU access
+ by process A using the mechanism before trying to access the shared
+ buffer.
+
+What is the best way to solve these buffer synchronization issues?
+ We may need a common object that a device driver and a user process
+ notify the common object of when they try to access a shared buffer.
+ That way we could decide when we have to allow or not to allow for CPU
+ or DMA to access the shared buffer through the common object.
+ If so, what could become the common object? Right, that's a dma-buf[1].
+ Now we have already been using the dma-buf to share one buffer with
+ other drivers.
+
+
+Basic concept
+-------------
+
+The mechanism of this framework has the following steps,
+ 1. Register dmabufs to a sync object - A task gets a new sync object and
+ can add one or more dmabufs that the task wants to access.
+ This registering should be performed when a device context or an event
+ context such as a page flip event is created or before CPU accesses a shared
+ buffer.
+
+ dma_buf_sync_get(a sync object, a dmabuf);
+
+ 2. Lock a sync object - A task tries to lock all dmabufs added in its own
+ sync object. Basically, the lock mechanism uses ww-mutexes[2] to avoid dead
+ lock issue and for race condition between CPU and CPU, CPU and DMA, and DMA
+ and DMA. Taking a lock means that others cannot access all locked dmabufs
+ until the task that locked the corresponding dmabufs, unlocks all the locked
+ dmabufs.
+ This locking should be performed before DMA or CPU accesses these dmabufs.
+
+ dma_buf_sync_lock(a sync object);
+
+ 3. Unlock a sync object - The task unlocks all dmabufs added in its own sync
+ object. The unlock means that the DMA or CPU accesses to the dmabufs have
+ been completed so that others may access them.
+ This unlocking should be performed after DMA or CPU has completed accesses
+ to the dmabufs.
+
+ dma_buf_sync_unlock(a sync object);
+
+ 4. Unregister one or all dmabufs from a sync object - A task unregisters
+ the given dmabufs from the sync object. This means that the task dosen't
+ want to lock the dmabufs.
+ The unregistering should be performed after DMA or CPU has completed
+ accesses to the dmabufs or when dma_buf_sync_lock() is failed.
+
+ dma_buf_sync_put(a sync object, a dmabuf);
+ dma_buf_sync_put_all(a sync object);
+
+ The described steps may be summarized as:
+ get -> lock -> CPU or DMA access to a buffer/s -> unlock -> put
+
+This framework includes the following two features.
+ 1. read (shared) and write (exclusive) locks - A task is required to declare
+ the access type when the task tries to register a dmabuf;
+ READ, WRITE, READ DMA, or WRITE DMA.
+
+ The below is example codes,
+ struct dmabuf_sync *sync;
+
+ sync = dmabuf_sync_init(NULL, "test sync");
+
+ dmabuf_sync_get(sync, dmabuf, DMA_BUF_ACCESS_R);
+ ...
+
+ 2. Mandatory resource releasing - a task cannot hold a lock indefinitely.
+ A task may never try to unlock a buffer after taking a lock to the buffer.
+ In this case, a timer handler to the corresponding sync object is called
+ in five (default) seconds and then the timed-out buffer is unlocked by work
+ queue handler to avoid lockups and to enforce resources of the buffer.
+
+
+Access types
+------------
+
+DMA_BUF_ACCESS_R - CPU will access a buffer for read.
+DMA_BUF_ACCESS_W - CPU will access a buffer for read or write.
+DMA_BUF_ACCESS_DMA_R - DMA will access a buffer for read
+DMA_BUF_ACCESS_DMA_W - DMA will access a buffer for read or write.
+
+
+Generic user interfaces
+-----------------------
+
+And this framework includes fcntl[3] and select system calls as interfaces
+exported to user. As you know, user sees a buffer object as a dma-buf file
+descriptor. fcntl() call with the file descriptor means to lock some buffer
+region being managed by the dma-buf object. And select call with the file
+descriptor means to poll the completion event of CPU or DMA access to
+the dma-buf.
+
+
+API set
+-------
+
+bool is_dmabuf_sync_supported(void)
+ - Check if dmabuf sync is supported or not.
+
+struct dmabuf_sync *dmabuf_sync_init(const char *name,
+ struct dmabuf_sync_priv_ops *ops,
+ void priv*)
+ - Allocate and initialize a new sync object. The caller can get a new
+ sync object for buffer synchronization. ops is used for device driver
+ to clean up its own sync object. For this, each device driver should
+ implement a free callback. priv is used for device driver to get its
+ device context when free callback is called.
+
+void dmabuf_sync_fini(struct dmabuf_sync *sync)
+ - Release all resources to the sync object.
+
+int dmabuf_sync_get(struct dmabuf_sync *sync, void *sync_buf,
+ unsigned int type)
+ - Get dmabuf sync object. Internally, this function allocates
+ a dmabuf_sync object and adds a given dmabuf to it, and also takes
+ a reference to the dmabuf. The caller can tie up multiple dmabufs
+ into one sync object by calling this function several times.
+
+void dmabuf_sync_put(struct dmabuf_sync *sync, struct dma_buf *dmabuf)
+ - Put dmabuf sync object to a given dmabuf. Internally, this function
+ removes a given dmabuf from a sync object and remove the sync object.
+ At this time, the dmabuf is putted.
+
+void dmabuf_sync_put_all(struct dmabuf_sync *sync)
+ - Put dmabuf sync object to dmabufs. Internally, this function removes
+ all dmabufs from a sync object and remove the sync object.
+ At this time, all dmabufs are putted.
+
+int dmabuf_sync_lock(struct dmabuf_sync *sync)
+ - Lock all dmabufs added in a sync object. The caller should call this
+ function prior to CPU or DMA access to the dmabufs so that others can
+ not access the dmabufs. Internally, this function avoids dead lock
+ issue with ww-mutexes.
+
+int dmabuf_sync_single_lock(struct dma_buf *dmabuf)
+ - Lock a dmabuf. The caller should call this
+ function prior to CPU or DMA access to the dmabuf so that others can
+ not access the dmabuf.
+
+int dmabuf_sync_unlock(struct dmabuf_sync *sync)
+ - Unlock all dmabufs added in a sync object. The caller should call
+ this function after CPU or DMA access to the dmabufs is completed so
+ that others can access the dmabufs.
+
+void dmabuf_sync_single_unlock(struct dma_buf *dmabuf)
+ - Unlock a dmabuf. The caller should call this function after CPU or
+ DMA access to the dmabuf is completed so that others can access
+ the dmabuf.
+
+
+Tutorial for device driver
+--------------------------
+
+1. Allocate and Initialize a sync object:
+ static void xxx_dmabuf_sync_free(void *priv)
+ {
+ struct xxx_context *ctx = priv;
+
+ if (!ctx)
+ return;
+
+ ctx->sync = NULL;
+ }
+ ...
+
+ static struct dmabuf_sync_priv_ops driver_specific_ops = {
+ .free = xxx_dmabuf_sync_free,
+ };
+ ...
+
+ struct dmabuf_sync *sync;
+
+ sync = dmabuf_sync_init("test sync", &driver_specific_ops, ctx);
+ ...
+
+2. Add a dmabuf to the sync object when setting up dma buffer relevant registers:
+ dmabuf_sync_get(sync, dmabuf, DMA_BUF_ACCESS_READ);
+ ...
+
+3. Lock all dmabufs of the sync object before DMA or CPU accesses the dmabufs:
+ dmabuf_sync_lock(sync);
+ ...
+
+4. Now CPU or DMA can access all dmabufs locked in step 3.
+
+5. Unlock all dmabufs added in a sync object after DMA or CPU access to these
+ dmabufs is completed:
+ dmabuf_sync_unlock(sync);
+
+ And call the following functions to release all resources,
+ dmabuf_sync_put_all(sync);
+ dmabuf_sync_fini(sync);
+
+
+Tutorial for user application
+-----------------------------
+fcntl system call:
+
+ struct flock filelock;
+
+1. Lock a dma buf:
+ filelock.l_type = F_WRLCK or F_RDLCK;
+
+ /* lock entire region to the dma buf. */
+ filelock.lwhence = SEEK_CUR;
+ filelock.l_start = 0;
+ filelock.l_len = 0;
+
+ fcntl(dmabuf fd, F_SETLKW or F_SETLK, &filelock);
+ ...
+ CPU access to the dma buf
+
+2. Unlock a dma buf:
+ filelock.l_type = F_UNLCK;
+
+ fcntl(dmabuf fd, F_SETLKW or F_SETLK, &filelock);
+
+ close(dmabuf fd) call would also unlock the dma buf. And for more
+ detail, please refer to [3]
+
+
+select system call:
+
+ fd_set wdfs or rdfs;
+
+ FD_ZERO(&wdfs or &rdfs);
+ FD_SET(fd, &wdfs or &rdfs);
+
+ select(fd + 1, &rdfs, NULL, NULL, NULL);
+ or
+ select(fd + 1, NULL, &wdfs, NULL, NULL);
+
+ Every time select system call is called, a caller will wait for
+ the completion of DMA or CPU access to a shared buffer if there
+ is someone accessing the shared buffer. If no anyone then select
+ system call will be returned at once.
+
+References:
+[1] http://lwn.net/Articles/470339/
+[2] https://patchwork.kernel.org/patch/2625361/
+[3] http://linux.die.net/man/2/fcntl
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index e373671..23e0fbf 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -200,6 +200,13 @@ config DMA_SHARED_BUFFER
APIs extension; the file's descriptor can then be passed on to other
driver.
+config DMABUF_SYNC
+ bool "DMABUF Synchronization Framework"
+ depends on DMA_SHARED_BUFFER
+ help
+ This option enables dmabuf sync framework for buffer synchronization between
+ DMA and DMA, CPU and DMA, and CPU and CPU.
+
config DMA_CMA
bool "DMA Contiguous Memory Allocator"
depends on HAVE_DMA_CONTIGUOUS && CMA
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 94e8a80..e88d9b8 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -11,6 +11,7 @@ obj-y += power/
obj-$(CONFIG_HAS_DMA) += dma-mapping.o
obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o
obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf.o reservation.o
+obj-$(CONFIG_DMABUF_SYNC) += dmabuf-sync.o
obj-$(CONFIG_ISA) += isa.o
obj-$(CONFIG_FW_LOADER) += firmware_class.o
obj-$(CONFIG_NUMA) += node.o
diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c
index 1e16cbd..3985751 100644
--- a/drivers/base/dma-buf.c
+++ b/drivers/base/dma-buf.c
@@ -29,6 +29,7 @@
#include <linux/export.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/dmabuf-sync.h>
static inline int is_dma_buf_file(struct file *);
@@ -56,6 +57,8 @@ static int dma_buf_release(struct inode *inode, struct file *file)
list_del(&dmabuf->list_node);
mutex_unlock(&db_list.lock);
+ dmabuf_sync_reservation_fini(dmabuf);
+
kfree(dmabuf);
return 0;
}
@@ -168,6 +171,8 @@ struct dma_buf *dma_buf_export_named(void *priv, const struct dma_buf_ops *ops,
file->f_mode |= FMODE_LSEEK;
dmabuf->file = file;
+ dmabuf_sync_reservation_init(dmabuf);
+
mutex_init(&dmabuf->lock);
INIT_LIST_HEAD(&dmabuf->attachments);
diff --git a/drivers/base/dmabuf-sync.c b/drivers/base/dmabuf-sync.c
new file mode 100644
index 0000000..b954865
--- /dev/null
+++ b/drivers/base/dmabuf-sync.c
@@ -0,0 +1,951 @@
+/*
+ * Copyright (C) 2013 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+
+#include <linux/dmabuf-sync.h>
+
+#define MAX_SYNC_TIMEOUT 5 /* Second. */
+#define MAX_WAIT_TIMEOUT 2000 /* Millisecond. */
+
+#define WAKE_UP_SYNC_OBJ(obj) { \
+ if (obj->waiting) { \
+ obj->waiting = false; \
+ wake_up(&obj->wq); \
+ } \
+ }
+
+#define DEL_OBJ_FROM_RSV(obj, rsv) { \
+ struct dmabuf_sync_object *e, *n; \
+ \
+ list_for_each_entry_safe(e, n, &rsv->syncs, r_head) { \
+ if (e = obj && !e->task) { \
+ list_del_init(&e->r_head); \
+ break; \
+ } \
+ } \
+}
+
+int dmabuf_sync_enabled = 1;
+
+MODULE_PARM_DESC(enabled, "Check if dmabuf sync is supported or not");
+module_param_named(enabled, dmabuf_sync_enabled, int, 0444);
+
+DEFINE_WW_CLASS(dmabuf_sync_ww_class);
+EXPORT_SYMBOL_GPL(dmabuf_sync_ww_class);
+
+static void dmabuf_sync_timeout_worker(struct work_struct *work)
+{
+ struct dmabuf_sync *sync = container_of(work, struct dmabuf_sync, work);
+ struct dmabuf_sync_object *sobj;
+
+ mutex_lock(&sync->lock);
+
+ list_for_each_entry(sobj, &sync->syncs, head) {
+ struct dmabuf_sync_reservation *rsvp = sobj->robj;
+
+ mutex_lock(&rsvp->lock);
+
+ pr_warn("%s: timeout = 0x%p [type = %d:%d, "
+ "refcnt = %d, locked = %d]\n",
+ sync->name, sobj->dmabuf,
+ rsvp->accessed_type,
+ sobj->access_type,
+ atomic_read(&rsvp->shared_cnt),
+ rsvp->locked);
+
+ if (rsvp->polled) {
+ rsvp->poll_event = true;
+ rsvp->polled = false;
+ wake_up_interruptible(&rsvp->poll_wait);
+ }
+
+ /*
+ * Wake up a task blocked by dmabuf_sync_wait_prev_objs().
+ *
+ * If sobj->waiting is true, the task is waiting for the wake
+ * up event so wake up the task if a given time period is
+ * elapsed and current task is timed out.
+ */
+ WAKE_UP_SYNC_OBJ(sobj);
+
+ /* Delete a sync object from reservation object of dmabuf. */
+ DEL_OBJ_FROM_RSV(sobj, rsvp);
+
+ if (atomic_add_unless(&rsvp->shared_cnt, -1, 1)) {
+ mutex_unlock(&rsvp->lock);
+ continue;
+ }
+
+ /* unlock only valid sync object. */
+ if (!rsvp->locked) {
+ mutex_unlock(&rsvp->lock);
+ continue;
+ }
+
+ mutex_unlock(&rsvp->lock);
+ ww_mutex_unlock(&rsvp->sync_lock);
+
+ mutex_lock(&rsvp->lock);
+ rsvp->locked = false;
+
+ if (sobj->access_type & DMA_BUF_ACCESS_R)
+ pr_warn("%s: r-unlocked = 0x%p\n",
+ sync->name, sobj->dmabuf);
+ else
+ pr_warn("%s: w-unlocked = 0x%p\n",
+ sync->name, sobj->dmabuf);
+
+ mutex_unlock(&rsvp->lock);
+ }
+
+ sync->status = 0;
+ mutex_unlock(&sync->lock);
+
+ dmabuf_sync_put_all(sync);
+ dmabuf_sync_fini(sync);
+}
+
+static void dmabuf_sync_lock_timeout(unsigned long arg)
+{
+ struct dmabuf_sync *sync = (struct dmabuf_sync *)arg;
+
+ schedule_work(&sync->work);
+}
+
+static void dmabuf_sync_wait_prev_objs(struct dmabuf_sync_object *sobj,
+ struct dmabuf_sync_reservation *rsvp,
+ struct ww_acquire_ctx *ctx)
+{
+ mutex_lock(&rsvp->lock);
+
+ /*
+ * This function handles the write-and-then-read ordering issue.
+ *
+ * The ordering issue:
+ * There is a case that a task don't take a lock to a dmabuf so
+ * this task would be stalled even though this task requested a lock
+ * to the dmabuf between other task unlocked and tries to lock
+ * the dmabuf again.
+ *
+ * How to handle the ordering issue:
+ * 1. Check if there is a sync object added prior to current task's one.
+ * 2. If exists, it unlocks the dmabuf so that other task can take
+ * a lock to the dmabuf first.
+ * 3. Wait for the wake up event from other task: current task will be
+ * waked up when other task unlocks the dmabuf.
+ * 4. Take a lock to the dmabuf again.
+ */
+ if (!list_empty(&rsvp->syncs)) {
+ struct dmabuf_sync_object *r_sobj, *next;
+
+ list_for_each_entry_safe(r_sobj, next, &rsvp->syncs,
+ r_head) {
+ long timeout;
+
+ /*
+ * Find a sync object added to rsvp->syncs by other task
+ * before current task tries to lock the dmabuf again.
+ * If sobj = r_sobj, it means that there is no any task
+ * that added its own sync object to rsvp->syncs so out
+ * of this loop.
+ */
+ if (sobj = r_sobj)
+ break;
+
+ /*
+ * Unlock the dmabuf if there is a sync object added
+ * to rsvp->syncs so that other task can take a lock
+ * first.
+ */
+ if (rsvp->locked) {
+ ww_mutex_unlock(&rsvp->sync_lock);
+ rsvp->locked = false;
+ }
+
+ r_sobj->waiting = true;
+
+ atomic_inc(&r_sobj->refcnt);
+ mutex_unlock(&rsvp->lock);
+
+ /* Wait for the wake up event from other task. */
+ timeout = wait_event_timeout(r_sobj->wq,
+ !r_sobj->waiting,
+ msecs_to_jiffies(MAX_WAIT_TIMEOUT));
+ if (!timeout) {
+ r_sobj->waiting = false;
+ pr_warn("wait event timeout: sobj = 0x%p\n",
+ r_sobj);
+
+ /*
+ * A sync object from fcntl system call has no
+ * timeout handler so delete ane free r_sobj
+ * once timeout here without checking refcnt.
+ */
+ if (r_sobj->task) {
+ list_del_init(&r_sobj->r_head);
+ kfree(r_sobj);
+ }
+ }
+
+ if (!atomic_add_unless(&r_sobj->refcnt, -1, 1))
+ kfree(r_sobj);
+
+ /*
+ * Other task unlocked the dmabuf so take a lock again.
+ */
+ ww_mutex_lock(&rsvp->sync_lock, ctx);
+
+ mutex_lock(&rsvp->lock);
+ rsvp->locked = true;
+ }
+ }
+
+ mutex_unlock(&rsvp->lock);
+}
+
+static int dmabuf_sync_lock_objs(struct dmabuf_sync *sync,
+ struct ww_acquire_ctx *ctx)
+{
+ struct dmabuf_sync_object *contended_sobj = NULL;
+ struct dmabuf_sync_object *res_sobj = NULL;
+ struct dmabuf_sync_object *sobj = NULL;
+ int ret;
+
+ if (ctx)
+ ww_acquire_init(ctx, &dmabuf_sync_ww_class);
+
+retry:
+ list_for_each_entry(sobj, &sync->syncs, head) {
+ struct dmabuf_sync_reservation *rsvp = sobj->robj;
+
+ if (WARN_ON(!rsvp))
+ continue;
+
+ mutex_lock(&rsvp->lock);
+
+ /*
+ * Add a sync object to reservation object of dmabuf
+ * to handle the write-and-then-read ordering issue.
+ *
+ * For more details, see dmabuf_sync_wait_prev_objs function.
+ */
+ list_add_tail(&sobj->r_head, &rsvp->syncs);
+
+ /* Don't lock in case of read and read. */
+ if (rsvp->accessed_type & DMA_BUF_ACCESS_R &&
+ sobj->access_type & DMA_BUF_ACCESS_R) {
+ atomic_inc(&rsvp->shared_cnt);
+ mutex_unlock(&rsvp->lock);
+ continue;
+ }
+
+ if (sobj = res_sobj) {
+ res_sobj = NULL;
+ mutex_unlock(&rsvp->lock);
+ continue;
+ }
+
+ mutex_unlock(&rsvp->lock);
+
+ ret = ww_mutex_lock(&rsvp->sync_lock, ctx);
+ if (ret < 0) {
+ contended_sobj = sobj;
+
+ if (ret = -EDEADLK)
+ pr_warn("%s: deadlock = 0x%p\n",
+ sync->name, sobj->dmabuf);
+ goto err;
+ }
+
+ mutex_lock(&rsvp->lock);
+ rsvp->locked = true;
+
+ mutex_unlock(&rsvp->lock);
+
+ /*
+ * Check if there is a sync object added to reservation object
+ * of dmabuf before current task takes a lock to the dmabuf.
+ * And ithen wait for the for the wake up event from other task
+ * if exists.
+ */
+ dmabuf_sync_wait_prev_objs(sobj, rsvp, ctx);
+ }
+
+ if (ctx)
+ ww_acquire_done(ctx);
+
+ init_timer(&sync->timer);
+
+ sync->timer.data = (unsigned long)sync;
+ sync->timer.function = dmabuf_sync_lock_timeout;
+ sync->timer.expires = jiffies + (HZ * MAX_SYNC_TIMEOUT);
+
+ add_timer(&sync->timer);
+
+ return 0;
+
+err:
+ list_for_each_entry_continue_reverse(sobj, &sync->syncs, head) {
+ struct dmabuf_sync_reservation *rsvp = sobj->robj;
+
+ mutex_lock(&rsvp->lock);
+
+ /* Don't need to unlock in case of read and read. */
+ if (atomic_add_unless(&rsvp->shared_cnt, -1, 1)) {
+ mutex_unlock(&rsvp->lock);
+ continue;
+ }
+
+ /*
+ * Delete a sync object from reservation object of dmabuf.
+ *
+ * The sync object was added to reservation object of dmabuf
+ * just before ww_mutex_lock() is called.
+ */
+ DEL_OBJ_FROM_RSV(sobj, rsvp);
+ mutex_unlock(&rsvp->lock);
+
+ ww_mutex_unlock(&rsvp->sync_lock);
+
+ mutex_lock(&rsvp->lock);
+ rsvp->locked = false;
+ mutex_unlock(&rsvp->lock);
+ }
+
+ if (res_sobj) {
+ struct dmabuf_sync_reservation *rsvp = res_sobj->robj;
+
+ mutex_lock(&rsvp->lock);
+
+ if (!atomic_add_unless(&rsvp->shared_cnt, -1, 1)) {
+ /*
+ * Delete a sync object from reservation object
+ * of dmabuf.
+ */
+ DEL_OBJ_FROM_RSV(sobj, rsvp);
+ mutex_unlock(&rsvp->lock);
+
+ ww_mutex_unlock(&rsvp->sync_lock);
+
+ mutex_lock(&rsvp->lock);
+ rsvp->locked = false;
+ }
+
+ mutex_unlock(&rsvp->lock);
+ }
+
+ if (ret = -EDEADLK) {
+ ww_mutex_lock_slow(&contended_sobj->robj->sync_lock, ctx);
+ res_sobj = contended_sobj;
+
+ goto retry;
+ }
+
+ if (ctx)
+ ww_acquire_fini(ctx);
+
+ return ret;
+}
+
+static void dmabuf_sync_unlock_objs(struct dmabuf_sync *sync,
+ struct ww_acquire_ctx *ctx)
+{
+ struct dmabuf_sync_object *sobj;
+
+ if (list_empty(&sync->syncs))
+ return;
+
+ mutex_lock(&sync->lock);
+
+ list_for_each_entry(sobj, &sync->syncs, head) {
+ struct dmabuf_sync_reservation *rsvp = sobj->robj;
+
+ mutex_lock(&rsvp->lock);
+
+ if (rsvp->polled) {
+ rsvp->poll_event = true;
+ rsvp->polled = false;
+ wake_up_interruptible(&rsvp->poll_wait);
+ }
+
+ /*
+ * Wake up a task blocked by dmabuf_sync_wait_prev_objs().
+ *
+ * If sobj->waiting is true, the task is waiting for wake_up
+ * call. So wake up the task if a given time period was
+ * elapsed so current task was timed out.
+ */
+ WAKE_UP_SYNC_OBJ(sobj);
+
+ /* Delete a sync object from reservation object of dmabuf. */
+ DEL_OBJ_FROM_RSV(sobj, rsvp);
+
+ if (atomic_add_unless(&rsvp->shared_cnt, -1, 1)) {
+ mutex_unlock(&rsvp->lock);
+ continue;
+ }
+
+ mutex_unlock(&rsvp->lock);
+
+ ww_mutex_unlock(&rsvp->sync_lock);
+
+ mutex_lock(&rsvp->lock);
+ rsvp->locked = false;
+ mutex_unlock(&rsvp->lock);
+ }
+
+ mutex_unlock(&sync->lock);
+
+ if (ctx)
+ ww_acquire_fini(ctx);
+
+ del_timer(&sync->timer);
+}
+
+/**
+ * dmabuf_sync_is_supported - Check if dmabuf sync is supported or not.
+ */
+bool dmabuf_sync_is_supported(void)
+{
+ return dmabuf_sync_enabled = 1;
+}
+EXPORT_SYMBOL_GPL(dmabuf_sync_is_supported);
+
+/**
+ * dmabuf_sync_init - Allocate and initialize a dmabuf sync.
+ *
+ * @priv: A device private data.
+ * @name: A sync object name.
+ *
+ * This function should be called when a device context or an event
+ * context such as a page flip event is created. And the created
+ * dmabuf_sync object should be set to the context.
+ * The caller can get a new sync object for buffer synchronization
+ * through this function.
+ */
+struct dmabuf_sync *dmabuf_sync_init(const char *name,
+ struct dmabuf_sync_priv_ops *ops,
+ void *priv)
+{
+ struct dmabuf_sync *sync;
+
+ sync = kzalloc(sizeof(*sync), GFP_KERNEL);
+ if (!sync)
+ return ERR_PTR(-ENOMEM);
+
+ strncpy(sync->name, name, DMABUF_SYNC_NAME_SIZE);
+
+ sync->ops = ops;
+ sync->priv = priv;
+ INIT_LIST_HEAD(&sync->syncs);
+ mutex_init(&sync->lock);
+ INIT_WORK(&sync->work, dmabuf_sync_timeout_worker);
+
+ return sync;
+}
+EXPORT_SYMBOL_GPL(dmabuf_sync_init);
+
+/**
+ * dmabuf_sync_fini - Release a given dmabuf sync.
+ *
+ * @sync: An object to dmabuf_sync structure.
+ *
+ * This function should be called if some operation is failed after
+ * dmabuf_sync_init call to release relevant resources, and after
+ * dmabuf_sync_unlock function is called.
+ */
+void dmabuf_sync_fini(struct dmabuf_sync *sync)
+{
+ struct dmabuf_sync_object *sobj;
+
+ if (WARN_ON(!sync))
+ return;
+
+ if (list_empty(&sync->syncs))
+ goto free_sync;
+
+ list_for_each_entry(sobj, &sync->syncs, head) {
+ struct dmabuf_sync_reservation *rsvp = sobj->robj;
+
+ mutex_lock(&rsvp->lock);
+
+ if (rsvp->locked) {
+ mutex_unlock(&rsvp->lock);
+ ww_mutex_unlock(&rsvp->sync_lock);
+
+ mutex_lock(&rsvp->lock);
+ rsvp->locked = false;
+ }
+
+ mutex_unlock(&rsvp->lock);
+ }
+
+ /*
+ * If !list_empty(&sync->syncs) then it means that dmabuf_sync_put()
+ * or dmabuf_sync_put_all() was never called. So unreference all
+ * dmabuf objects added to sync->syncs, and remove them from the syncs.
+ */
+ dmabuf_sync_put_all(sync);
+
+free_sync:
+ if (sync->ops && sync->ops->free)
+ sync->ops->free(sync->priv);
+
+ kfree(sync);
+}
+EXPORT_SYMBOL_GPL(dmabuf_sync_fini);
+
+/*
+ * dmabuf_sync_get_obj - Add a given object to sync's list.
+ *
+ * @sync: An object to dmabuf_sync structure.
+ * @dmabuf: An object to dma_buf structure.
+ * @type: A access type to a dma buf.
+ * The DMA_BUF_ACCESS_R means that this dmabuf could be accessed by
+ * others for read access. On the other hand, the DMA_BUF_ACCESS_W
+ * means that this dmabuf couldn't be accessed by others but would be
+ * accessed by caller's dma exclusively. And the DMA_BUF_ACCESS_DMA can be
+ * combined.
+ *
+ * This function creates and initializes a new dmabuf sync object and it adds
+ * the dmabuf sync object to syncs list to track and manage all dmabufs.
+ */
+static int dmabuf_sync_get_obj(struct dmabuf_sync *sync, struct dma_buf *dmabuf,
+ unsigned int type)
+{
+ struct dmabuf_sync_object *sobj;
+
+ if (!dmabuf->sync)
+ return -EFAULT;
+
+ if (!IS_VALID_DMA_BUF_ACCESS_TYPE(type))
+ return -EINVAL;
+
+ if ((type & DMA_BUF_ACCESS_RW) = DMA_BUF_ACCESS_RW)
+ type &= ~DMA_BUF_ACCESS_R;
+
+ sobj = kzalloc(sizeof(*sobj), GFP_KERNEL);
+ if (!sobj)
+ return -ENOMEM;
+
+ get_dma_buf(dmabuf);
+
+ sobj->dmabuf = dmabuf;
+ sobj->robj = dmabuf->sync;
+ sobj->access_type = type;
+ atomic_set(&sobj->refcnt, 1);
+ init_waitqueue_head(&sobj->wq);
+
+ mutex_lock(&sync->lock);
+ list_add_tail(&sobj->head, &sync->syncs);
+ mutex_unlock(&sync->lock);
+
+ return 0;
+}
+
+/*
+ * dmabuf_sync_put_obj - Release a given sync object.
+ *
+ * @sync: An object to dmabuf_sync structure.
+ *
+ * This function should be called if some operation failed after
+ * dmabuf_sync_get_obj call to release a given sync object.
+ */
+static void dmabuf_sync_put_obj(struct dmabuf_sync *sync,
+ struct dma_buf *dmabuf)
+{
+ struct dmabuf_sync_object *sobj;
+
+ mutex_lock(&sync->lock);
+
+ list_for_each_entry(sobj, &sync->syncs, head) {
+ if (sobj->dmabuf != dmabuf)
+ continue;
+
+ dma_buf_put(sobj->dmabuf);
+
+ list_del_init(&sobj->head);
+
+ if (!atomic_add_unless(&sobj->refcnt, -1, 1))
+ kfree(sobj);
+ break;
+ }
+
+ if (list_empty(&sync->syncs))
+ sync->status = 0;
+
+ mutex_unlock(&sync->lock);
+}
+
+/*
+ * dmabuf_sync_put_objs - Release all sync objects of dmabuf_sync.
+ *
+ * @sync: An object to dmabuf_sync structure.
+ *
+ * This function should be called if some operation failed after
+ * dmabuf_sync_get_obj call to release all sync objects.
+ */
+static void dmabuf_sync_put_objs(struct dmabuf_sync *sync)
+{
+ struct dmabuf_sync_object *sobj, *next;
+
+ mutex_lock(&sync->lock);
+
+ list_for_each_entry_safe(sobj, next, &sync->syncs, head) {
+ dma_buf_put(sobj->dmabuf);
+
+ list_del_init(&sobj->head);
+
+ if (!atomic_add_unless(&sobj->refcnt, -1, 1))
+ kfree(sobj);
+ }
+
+ mutex_unlock(&sync->lock);
+
+ sync->status = 0;
+}
+
+/**
+ * dmabuf_sync_lock - lock all dmabufs added to syncs list.
+ *
+ * @sync: An object to dmabuf_sync structure.
+ *
+ * The caller should call this function prior to CPU or DMA access to
+ * the dmabufs so that others can not access the dmabufs.
+ * Internally, this function avoids dead lock issue with ww-mutex.
+ */
+int dmabuf_sync_lock(struct dmabuf_sync *sync)
+{
+ int ret;
+
+ if (!sync)
+ return -EFAULT;
+
+ if (list_empty(&sync->syncs))
+ return -EINVAL;
+
+ if (sync->status != DMABUF_SYNC_GOT)
+ return -EINVAL;
+
+ ret = dmabuf_sync_lock_objs(sync, &sync->ctx);
+ if (ret < 0)
+ return ret;
+
+ sync->status = DMABUF_SYNC_LOCKED;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dmabuf_sync_lock);
+
+/**
+ * dmabuf_sync_unlock - unlock all objects added to syncs list.
+ *
+ * @sync: An object to dmabuf_sync structure.
+ *
+ * The caller should call this function after CPU or DMA access to
+ * the dmabufs is completed so that others can access the dmabufs.
+ */
+int dmabuf_sync_unlock(struct dmabuf_sync *sync)
+{
+ if (!sync)
+ return -EFAULT;
+
+ /* If current dmabuf sync object wasn't reserved then just return. */
+ if (sync->status != DMABUF_SYNC_LOCKED)
+ return -EAGAIN;
+
+ dmabuf_sync_unlock_objs(sync, &sync->ctx);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dmabuf_sync_unlock);
+
+/**
+ * dmabuf_sync_single_lock - lock a dma buf.
+ *
+ * @dmabuf: A dma buf object that tries to lock.
+ * @type: A access type to a dma buf.
+ * The DMA_BUF_ACCESS_R means that this dmabuf could be accessed by
+ * others for read access. On the other hand, the DMA_BUF_ACCESS_W
+ * means that this dmabuf couldn't be accessed by others but would be
+ * accessed by caller's dma exclusively. And the DMA_BUF_ACCESS_DMA can
+ * be combined with other.
+ * @wait: Indicate whether caller is blocked or not.
+ * true means that caller will be blocked, and false means that this
+ * function will return -EAGAIN if this caller can't take the lock
+ * right now.
+ *
+ * The caller should call this function prior to CPU or DMA access to the dmabuf
+ * so that others cannot access the dmabuf.
+ */
+int dmabuf_sync_single_lock(struct dma_buf *dmabuf, unsigned int type,
+ bool wait)
+{
+ struct dmabuf_sync_reservation *robj;
+ struct dmabuf_sync_object *sobj;
+
+ if (!dmabuf->sync)
+ return -EFAULT;
+
+ if (!IS_VALID_DMA_BUF_ACCESS_TYPE(type))
+ return -EINVAL;
+
+ get_dma_buf(dmabuf);
+ robj = dmabuf->sync;
+
+ sobj = kzalloc(sizeof(*sobj), GFP_KERNEL);
+ if (!sobj) {
+ dma_buf_put(dmabuf);
+ return -ENOMEM;
+ }
+
+ sobj->dmabuf = dmabuf;
+ sobj->task = (unsigned long)current;
+ atomic_set(&sobj->refcnt, 1);
+ init_waitqueue_head(&sobj->wq);
+
+ mutex_lock(&robj->lock);
+
+ /*
+ * Add a sync object to reservation object of dmabuf to handle
+ * the write-and-then-read ordering issue.
+ */
+ list_add_tail(&sobj->r_head, &robj->syncs);
+
+ /* Don't lock in case of read and read. */
+ if (robj->accessed_type & DMA_BUF_ACCESS_R && type & DMA_BUF_ACCESS_R) {
+ atomic_inc(&robj->shared_cnt);
+ mutex_unlock(&robj->lock);
+ return 0;
+ }
+
+ /*
+ * In case of F_SETLK, just return -EAGAIN if this dmabuf has already
+ * been locked.
+ */
+ if (!wait && robj->locked) {
+ list_del_init(&sobj->r_head);
+ mutex_unlock(&robj->lock);
+ kfree(sobj);
+ dma_buf_put(dmabuf);
+ return -EAGAIN;
+ }
+
+ mutex_unlock(&robj->lock);
+
+ /* Unlocked by dmabuf_sync_single_unlock or dmabuf_sync_unlock. */
+ mutex_lock(&robj->sync_lock.base);
+
+ mutex_lock(&robj->lock);
+ robj->locked = true;
+ mutex_unlock(&robj->lock);
+
+ /*
+ * Check if there is a sync object added to reservation object of
+ * dmabuf before current task takes a lock to the dmabuf, and wait
+ * for the for the wake up event from other task if exists.
+ */
+ dmabuf_sync_wait_prev_objs(sobj, robj, NULL);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dmabuf_sync_single_lock);
+
+/**
+ * dmabuf_sync_single_unlock - unlock a dma buf.
+ *
+ * @dmabuf: A dma buf object that tries to unlock.
+ *
+ * The caller should call this function after CPU or DMA access to
+ * the dmabuf is completed so that others can access the dmabuf.
+ */
+void dmabuf_sync_single_unlock(struct dma_buf *dmabuf)
+{
+ struct dmabuf_sync_reservation *robj;
+ struct dmabuf_sync_object *sobj, *next;
+
+ if (!dmabuf->sync) {
+ WARN_ON(1);
+ return;
+ }
+
+ robj = dmabuf->sync;
+
+ mutex_lock(&robj->lock);
+
+ if (robj->polled) {
+ robj->poll_event = true;
+ robj->polled = false;
+ wake_up_interruptible(&robj->poll_wait);
+ }
+
+ /*
+ * Wake up a blocked task/tasks by dmabuf_sync_wait_prev_objs()
+ * with two steps.
+ *
+ * 1. Wake up a task waiting for the wake up event to a sync object
+ * of same task, and remove the sync object from reservation
+ * object of dmabuf, and then go to out: requested by same task.
+ * 2. Wait up a task waiting for the wake up event to a sync object
+ * of other task, and remove the sync object if not existed
+ * at step 1: requested by other task.
+ *
+ * The reason, we have to handle it with the above two steps,
+ * is that fcntl system call is called with a file descriptor so
+ * kernel side cannot be aware of which sync object of robj->syncs
+ * should be waked up and deleted at this function.
+ * So for this, we use the above two steps to find a sync object
+ * to be waked up.
+ */
+ list_for_each_entry_safe(sobj, next, &robj->syncs, r_head) {
+ if (sobj->task = (unsigned long)current) {
+ /*
+ * Wake up a task blocked by
+ * dmabuf_sync_wait_prev_objs().
+ */
+ WAKE_UP_SYNC_OBJ(sobj);
+
+ list_del_init(&sobj->r_head);
+
+ if (!atomic_add_unless(&sobj->refcnt, -1, 1))
+ kfree(sobj);
+ goto out;
+ }
+ }
+
+ list_for_each_entry_safe(sobj, next, &robj->syncs, r_head) {
+ if (sobj->task) {
+ /*
+ * Wake up a task blocked by
+ * dmabuf_sync_wait_prev_objs().
+ */
+ WAKE_UP_SYNC_OBJ(sobj);
+
+ list_del_init(&sobj->r_head);
+
+ if (!atomic_add_unless(&sobj->refcnt, -1, 1))
+ kfree(sobj);
+ break;
+ }
+ }
+
+out:
+ if (atomic_add_unless(&robj->shared_cnt, -1 , 1)) {
+ mutex_unlock(&robj->lock);
+ dma_buf_put(dmabuf);
+ return;
+ }
+
+ mutex_unlock(&robj->lock);
+
+ mutex_unlock(&robj->sync_lock.base);
+
+ mutex_lock(&robj->lock);
+ robj->locked = false;
+ mutex_unlock(&robj->lock);
+
+ dma_buf_put(dmabuf);
+
+ return;
+}
+EXPORT_SYMBOL_GPL(dmabuf_sync_single_unlock);
+
+/**
+ * dmabuf_sync_get - Get dmabuf sync object.
+ *
+ * @sync: An object to dmabuf_sync structure.
+ * @sync_buf: A dmabuf object to be synchronized with others.
+ * @type: A access type to a dma buf.
+ * The DMA_BUF_ACCESS_R means that this dmabuf could be accessed by
+ * others for read access. On the other hand, the DMA_BUF_ACCESS_W
+ * means that this dmabuf couldn't be accessed by others but would be
+ * accessed by caller's dma exclusively. And the DMA_BUF_ACCESS_DMA can
+ * be combined with other.
+ *
+ * This function should be called after dmabuf_sync_init function is called.
+ * The caller can tie up multiple dmabufs into one sync object by calling this
+ * function several times. Internally, this function allocates
+ * a dmabuf_sync_object and adds a given dmabuf to it, and also takes
+ * a reference to a dmabuf.
+ */
+int dmabuf_sync_get(struct dmabuf_sync *sync, void *sync_buf, unsigned int type)
+{
+ int ret;
+
+ if (!sync || !sync_buf)
+ return -EFAULT;
+
+ ret = dmabuf_sync_get_obj(sync, sync_buf, type);
+ if (ret < 0)
+ return ret;
+
+ sync->status = DMABUF_SYNC_GOT;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dmabuf_sync_get);
+
+/**
+ * dmabuf_sync_put - Put dmabuf sync object to a given dmabuf.
+ *
+ * @sync: An object to dmabuf_sync structure.
+ * @dmabuf: An dmabuf object.
+ *
+ * This function should be called if some operation is failed after
+ * dmabuf_sync_get function is called to release the dmabuf, or
+ * dmabuf_sync_unlock function is called. Internally, this function
+ * removes a given dmabuf from a sync object and remove the sync object.
+ * At this time, the dmabuf is putted.
+ */
+void dmabuf_sync_put(struct dmabuf_sync *sync, struct dma_buf *dmabuf)
+{
+ if (!sync || !dmabuf) {
+ WARN_ON(1);
+ return;
+ }
+
+ if (list_empty(&sync->syncs))
+ return;
+
+ dmabuf_sync_put_obj(sync, dmabuf);
+}
+EXPORT_SYMBOL_GPL(dmabuf_sync_put);
+
+/**
+ * dmabuf_sync_put_all - Put dmabuf sync object to dmabufs.
+ *
+ * @sync: An object to dmabuf_sync structure.
+ *
+ * This function should be called if some operation is failed after
+ * dmabuf_sync_get function is called to release all sync objects, or
+ * dmabuf_sync_unlock function is called. Internally, this function
+ * removes dmabufs from a sync object and remove the sync object.
+ * At this time, all dmabufs are putted.
+ */
+void dmabuf_sync_put_all(struct dmabuf_sync *sync)
+{
+ if (!sync) {
+ WARN_ON(1);
+ return;
+ }
+
+ if (list_empty(&sync->syncs))
+ return;
+
+ dmabuf_sync_put_objs(sync);
+}
+EXPORT_SYMBOL_GPL(dmabuf_sync_put_all);
diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
index dfac5ed..0109673 100644
--- a/include/linux/dma-buf.h
+++ b/include/linux/dma-buf.h
@@ -115,6 +115,7 @@ struct dma_buf_ops {
* @exp_name: name of the exporter; useful for debugging.
* @list_node: node for dma_buf accounting and debugging.
* @priv: exporter specific private data for this buffer object.
+ * @sync: sync object linked to this dma-buf
*/
struct dma_buf {
size_t size;
@@ -128,6 +129,7 @@ struct dma_buf {
const char *exp_name;
struct list_head list_node;
void *priv;
+ void *sync;
};
/**
@@ -148,6 +150,20 @@ struct dma_buf_attachment {
void *priv;
};
+#define DMA_BUF_ACCESS_R 0x1
+#define DMA_BUF_ACCESS_W 0x2
+#define DMA_BUF_ACCESS_DMA 0x4
+#define DMA_BUF_ACCESS_RW (DMA_BUF_ACCESS_R | DMA_BUF_ACCESS_W)
+#define DMA_BUF_ACCESS_DMA_R (DMA_BUF_ACCESS_R | DMA_BUF_ACCESS_DMA)
+#define DMA_BUF_ACCESS_DMA_W (DMA_BUF_ACCESS_W | DMA_BUF_ACCESS_DMA)
+#define DMA_BUF_ACCESS_DMA_RW (DMA_BUF_ACCESS_DMA_R | DMA_BUF_ACCESS_DMA_W)
+#define IS_VALID_DMA_BUF_ACCESS_TYPE(t) (t = DMA_BUF_ACCESS_R || \
+ t = DMA_BUF_ACCESS_W || \
+ t = DMA_BUF_ACCESS_DMA_R || \
+ t = DMA_BUF_ACCESS_DMA_W || \
+ t = DMA_BUF_ACCESS_RW || \
+ t = DMA_BUF_ACCESS_DMA_RW)
+
/**
* get_dma_buf - convenience wrapper for get_file.
* @dmabuf: [in] pointer to dma_buf
diff --git a/include/linux/dmabuf-sync.h b/include/linux/dmabuf-sync.h
new file mode 100644
index 0000000..fc0bb4e
--- /dev/null
+++ b/include/linux/dmabuf-sync.h
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2013 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 <linux/ww_mutex.h>
+#include <linux/sched.h>
+#include <linux/dma-buf.h>
+
+#define DMABUF_SYNC_NAME_SIZE 64
+
+/*
+ * Status to a dmabuf_sync object.
+ *
+ * @DMABUF_SYNC_GOT: Indicate that one more dmabuf objects have been added
+ * to a sync's list.
+ * @DMABUF_SYNC_LOCKED: Indicate that all dmabuf objects in a sync's list
+ * have been locked.
+ */
+enum dmabuf_sync_status {
+ DMABUF_SYNC_GOT = 1,
+ DMABUF_SYNC_LOCKED,
+};
+
+/*
+ * A structure for dmabuf_sync_reservation.
+ *
+ * @syncs: A list head to sync object and this is global to system.
+ * This contains sync objects of tasks that requested a lock
+ * to this dmabuf.
+ * @sync_lock: This provides read or write lock to a dmabuf.
+ * Except in the below cases, a task will be blocked if the task
+ * tries to lock a dmabuf for CPU or DMA access when other task
+ * already locked the dmabuf.
+ *
+ * Before After
+ * --------------------------
+ * CPU read CPU read
+ * CPU read DMA read
+ * DMA read CPU read
+ * DMA read DMA read
+ *
+ * @lock: Protecting a dmabuf_sync_reservation object.
+ * @poll_wait: A wait queue object to poll a dmabuf object.
+ * @poll_event: Indicate whether a dmabuf object - being polled -
+ * was unlocked or not. If true, a blocked task will be out
+ * of select system call.
+ * @poll: Indicate whether the polling to a dmabuf object was requested
+ * or not by userspace.
+ * @shared_cnt: Shared count to a dmabuf object.
+ * @accessed_type: Indicate how and who a dmabuf object was accessed by.
+ * One of the below types could be set.
+ * DMA_BUF_ACCESS_R -> CPU access for read.
+ * DMA_BUF_ACCRSS_W -> CPU access for write.
+ * DMA_BUF_ACCESS_R | DMA_BUF_ACCESS_DMA -> DMA access for read.
+ * DMA_BUF_ACCESS_W | DMA_BUF_ACCESS_DMA -> DMA access for write.
+ * @locked: Indicate whether a dmabuf object has been locked or not.
+ *
+ */
+struct dmabuf_sync_reservation {
+ struct list_head syncs;
+ struct ww_mutex sync_lock;
+ struct mutex lock;
+ wait_queue_head_t poll_wait;
+ unsigned int poll_event;
+ unsigned int polled;
+ atomic_t shared_cnt;
+ unsigned int accessed_type;
+ unsigned int locked;
+};
+
+/*
+ * A structure for dmabuf_sync_object.
+ *
+ * @head: A list head to be added to dmabuf_sync's syncs.
+ * @r_head: A list head to be added to dmabuf_sync_reservation's syncs.
+ * @robj: A reservation_object object.
+ * @dma_buf: A dma_buf object.
+ * @task: An address value to current task.
+ * This is used to indicate who is a owner of a sync object.
+ * @wq: A wait queue head.
+ * This is used to guarantee that a task can take a lock to a dmabuf
+ * if the task requested a lock to the dmabuf prior to other task.
+ * For more details, see dmabuf_sync_wait_prev_objs function.
+ * @refcnt: A reference count to a sync object.
+ * @access_type: Indicate how a current task tries to access
+ * a given buffer, and one of the below types could be set.
+ * DMA_BUF_ACCESS_R -> CPU access for read.
+ * DMA_BUF_ACCRSS_W -> CPU access for write.
+ * DMA_BUF_ACCESS_R | DMA_BUF_ACCESS_DMA -> DMA access for read.
+ * DMA_BUF_ACCESS_W | DMA_BUF_ACCESS_DMA -> DMA access for write.
+ * @waiting: Indicate whether current task is waiting for the wake up event
+ * from other task or not.
+ */
+struct dmabuf_sync_object {
+ struct list_head head;
+ struct list_head r_head;
+ struct dmabuf_sync_reservation *robj;
+ struct dma_buf *dmabuf;
+ unsigned long task;
+ wait_queue_head_t wq;
+ atomic_t refcnt;
+ unsigned int access_type;
+ unsigned int waiting;
+};
+
+struct dmabuf_sync_priv_ops {
+ void (*free)(void *priv);
+};
+
+/*
+ * A structure for dmabuf_sync.
+ *
+ * @syncs: A list head to sync object and this is global to system.
+ * This contains sync objects of dmabuf_sync owner.
+ * @list: A list entry used as committed list node
+ * @lock: Protecting a dmabuf_sync object.
+ * @ctx: A current context for ww mutex.
+ * @work: A work struct to release resources at timeout.
+ * @priv: A private data.
+ * @name: A string to dmabuf sync owner.
+ * @timer: A timer list to avoid lockup and release resources.
+ * @status: Indicate current status (DMABUF_SYNC_GOT or DMABUF_SYNC_LOCKED).
+ */
+struct dmabuf_sync {
+ struct list_head syncs;
+ struct list_head list;
+ struct mutex lock;
+ struct ww_acquire_ctx ctx;
+ struct work_struct work;
+ void *priv;
+ struct dmabuf_sync_priv_ops *ops;
+ char name[DMABUF_SYNC_NAME_SIZE];
+ struct timer_list timer;
+ unsigned int status;
+};
+
+#ifdef CONFIG_DMABUF_SYNC
+
+extern struct ww_class dmabuf_sync_ww_class;
+
+static inline void dmabuf_sync_reservation_init(struct dma_buf *dmabuf)
+{
+ struct dmabuf_sync_reservation *obj;
+
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+ if (!obj)
+ return;
+
+ dmabuf->sync = obj;
+
+ ww_mutex_init(&obj->sync_lock, &dmabuf_sync_ww_class);
+
+ mutex_init(&obj->lock);
+ atomic_set(&obj->shared_cnt, 1);
+ INIT_LIST_HEAD(&obj->syncs);
+
+ init_waitqueue_head(&obj->poll_wait);
+}
+
+static inline void dmabuf_sync_reservation_fini(struct dma_buf *dmabuf)
+{
+ struct dmabuf_sync_reservation *obj;
+
+ if (!dmabuf->sync)
+ return;
+
+ obj = dmabuf->sync;
+
+ ww_mutex_destroy(&obj->sync_lock);
+
+ kfree(obj);
+}
+
+bool dmabuf_sync_is_supported(void);
+
+struct dmabuf_sync *dmabuf_sync_init(const char *name,
+ struct dmabuf_sync_priv_ops *ops,
+ void *priv);
+
+void dmabuf_sync_fini(struct dmabuf_sync *sync);
+
+int dmabuf_sync_lock(struct dmabuf_sync *sync);
+
+int dmabuf_sync_unlock(struct dmabuf_sync *sync);
+
+int dmabuf_sync_single_lock(struct dma_buf *dmabuf, unsigned int type,
+ bool wait);
+
+void dmabuf_sync_single_unlock(struct dma_buf *dmabuf);
+
+int dmabuf_sync_get(struct dmabuf_sync *sync, void *sync_buf,
+ unsigned int type);
+
+void dmabuf_sync_put(struct dmabuf_sync *sync, struct dma_buf *dmabuf);
+
+void dmabuf_sync_put_all(struct dmabuf_sync *sync);
+
+#else
+
+static inline void dmabuf_sync_reservation_init(struct dma_buf *dmabuf) { }
+
+static inline void dmabuf_sync_reservation_fini(struct dma_buf *dmabuf) { }
+
+static inline bool dmabuf_sync_is_supported(void) { return false; }
+
+static inline struct dmabuf_sync *dmabuf_sync_init(const char *name,
+ struct dmabuf_sync_priv_ops *ops,
+ void *priv)
+{
+ return ERR_PTR(0);
+}
+
+static inline void dmabuf_sync_fini(struct dmabuf_sync *sync) { }
+
+static inline int dmabuf_sync_lock(struct dmabuf_sync *sync)
+{
+ return 0;
+}
+
+static inline int dmabuf_sync_unlock(struct dmabuf_sync *sync)
+{
+ return 0;
+}
+
+static inline int dmabuf_sync_single_lock(struct dma_buf *dmabuf,
+ unsigned int type,
+ bool wait)
+{
+ return 0;
+}
+
+static inline void dmabuf_sync_single_unlock(struct dma_buf *dmabuf)
+{
+ return;
+}
+
+static inline int dmabuf_sync_get(struct dmabuf_sync *sync,
+ void *sync_buf,
+ unsigned int type)
+{
+ return 0;
+}
+
+static inline void dmabuf_sync_put(struct dmabuf_sync *sync,
+ struct dma_buf *dmabuf) { }
+
+static inline void dmabuf_sync_put_all(struct dmabuf_sync *sync) { }
+
+#endif
--
1.7.9.5
^ permalink raw reply related
* [PATCH v9 0/2] Introduce buffer synchronization framework
From: Inki Dae @ 2013-09-17 12:23 UTC (permalink / raw)
To: dri-devel, linux-fbdev, linux-arm-kernel, linux-media,
linaro-kernel
Cc: Roger.Teague, jesse.barker, jesse.barker, maarten.lankhorst,
sumit.semwal, kyungmin.park, myungjoo.ham, Inki Dae
Hi all,
This patch set introduces a buffer synchronization framework based
on DMA BUF[1] and based on ww-mutexes[2] for lock mechanism, and is
rebased on linux-3.12-rc1
The purpose of this framework is to provide not only buffer access
control to CPU and CPU, and CPU and DMA, and DMA and DMA but also
easy-to-use interfaces for device drivers and user application.
In addtion, this patch set suggests a way for enhancing performance.
Changelog v9:
- Delete only one sync object matched at DEL_OBJ_FROM_RSV macro.
- Fix memory leak issue at dmabuf_sync_single_lock()
Changelog v8:
Consider the write-and-then-read ordering pointed out by David Herrmann,
- The ordering issue means that a task don't take a lock to the dmabuf
so this task would be stalled even though this task requested a lock to
the dmabuf between other task unlocked and tries to lock the dmabuf
again. For this, we have added a wait event mechanism using only generic
APIs, wait_event_timeout and wake_up functions.
The below is how to handle the ordering issue using this mechanism:
1. Check if there is a sync object added prior to current task's one.
2. If exists, it unlocks the dmabuf so that other task can take a lock
to the dmabuf first.
3. Wait for the wake up event from other task: current task will be
waked up when other task unlocks the dmabuf.
4. Take a lock to the dmabuf again.
- Code cleanups.
Changelog v7:
Fix things pointed out by Konrad Rzeszutek Wilk,
- Use EXPORT_SYMBOL_GPL instead of EXPORT_SYMBOL.
- Make sure to unlock and unreference all dmabuf objects
when dmabuf_sync_fini() is called.
- Add more comments.
- Code cleanups.
Changelog v6:
- Fix sync lock to multiple reads.
- Add select system call support.
. Wake up poll_wait when a dmabuf is unlocked.
- Remove unnecessary the use of mutex lock.
- Add private backend ops callbacks.
. This ops has one callback for device drivers to clean up their
sync object resource when the sync object is freed. For this,
device drivers should implement the free callback properly.
- Update document file.
Changelog v5:
- Rmove a dependence on reservation_object: the reservation_object is used
to hook up to ttm and dma-buf for easy sharing of reservations across
devices. However, the dmabuf sync can be used for all dma devices; v4l2
and drm based drivers, so doesn't need the reservation_object anymore.
With regared to this, it adds 'void *sync' to dma_buf structure.
- All patches are rebased on mainline, Linux v3.10.
Changelog v4:
- Add user side interface for buffer synchronization mechanism and update
descriptions related to the user side interface.
Changelog v3:
- remove cache operation relevant codes and update document file.
Changelog v2:
- use atomic_add_unless to avoid potential bug.
- add a macro for checking valid access type.
- code clean.
For generic user mode interface, we have used fcntl and select system
call[3]. As you know, user application sees a buffer object as a dma-buf
file descriptor. So fcntl() call with the file descriptor means to lock
some buffer region being managed by the dma-buf object. And select() call
means to wait for the completion of CPU or DMA access to the dma-buf
without locking. For more detail, you can refer to the dma-buf-sync.txt
in Documentation/
There are some cases user-space process needs this buffer synchronization
framework. One of which is to primarily enhance GPU rendering performance
in case that 3D app draws somthing in a buffer using CPU, and other process
composes the buffer with its own backbuffer using GPU.
In case of 3D app, the app calls glFlush to submit 3d commands to GPU driver
instead of glFinish for more performance. The reason, we call glFlush, is
that glFinish blocks caller's task until the execution of the 3d commands is
completed. So that makes GPU and CPU more idle. As a result, 3d rendering
performance with glFinish is quite lower than glFlush.
However, the use of glFlush has one issue that the the buffer shared with
GPU could be broken when CPU accesses the buffer just after glFlush because
CPU cannot be aware of the completion of GPU access to the buffer.
Of course, the app can be aware of that time using eglWaitGL but this function
is valid only in case of the same context.
The below summarizes how app's window is displayed on Tizen[4] platform:
1. X client requests a window buffer to Xorg.
2. X client draws something in the window buffer using CPU.
3. X client requests SWAP to Xorg.
4. Xorg notifies a damage event to Composite Manager.
5. Composite Manager gets the window buffer (front buffer) through
DRI2GetBuffers.
6. Composite Manager composes the window buffer and its own back buffer
using GPU. At this time, eglSwapBuffers is called: internally, 3d
commands are flushed to gpu driver.
7. Composite Manager requests SWAP to Xorg.
8. Xorg performs drm page flip. At this time, the window buffer is
displayed on screen.
Web app based on HTML5 also has the same issue. Web browser and Web app
are different process. The Web app can draw something in its own buffer using
CPU, and then the Web Browser can compose the buffer with its own back buffer.
Thus, in such cases, a shared buffer could be broken as one process draws
something in a buffer using CPU, when other process composes the buffer with
its own buffer using GPU without any locking mechanism. That is why we need
user land locking interface, fcntl system call.
And last one is a deferred page flip issue. This issue is that a window
buffer rendered can be displayed on screen in about 32ms in worst case:
assume that the gpu rendering is completed within 16ms.
That can be incurred when compositing a pixmap buffer with a window buffer
using GPU and when vsync is just started. At this time, Xorg waits for
a vblank event to get a window buffer so 3d rendering will be delayed
up to about 16ms. As a result, the window buffer would be displayed in
about two vsyncs (about 32ms) and in turn, that would show slow
responsiveness.
For this, we could enhance the responsiveness with locking mechanism: skipping
one vblank wait. I guess Android, Chrome OS, and other platforms are using
their own locking mechanisms with similar reason; Android sync driver, KDS, and
DMA fence.
The below shows the deferred page flip issue in worst case:
|------------ <- vsync signal
|<------ DRI2GetBuffers
|
|
|
|------------ <- vsync signal
|<------ Request gpu rendering
time |
|
|<------ Request page flip (deferred)
|------------ <- vsync signal
|<------ Displayed on screen
|
|
|
|------------ <- vsync signal
Thanks,
Inki Dae
References:
[1] http://lwn.net/Articles/470339/
[2] https://patchwork.kernel.org/patch/2625361/
[3] http://linux.die.net/man/2/fcntl
[4] https://www.tizen.org/
Inki Dae (2):
dmabuf-sync: Add a buffer synchronization framework
dma-buf: Add user interfaces for dmabuf sync support
Documentation/dma-buf-sync.txt | 286 ++++++++++++
drivers/base/Kconfig | 7 +
drivers/base/Makefile | 1 +
drivers/base/dma-buf.c | 86 ++++
drivers/base/dmabuf-sync.c | 951 ++++++++++++++++++++++++++++++++++++++++
include/linux/dma-buf.h | 16 +
include/linux/dmabuf-sync.h | 257 +++++++++++
7 files changed, 1604 insertions(+)
create mode 100644 Documentation/dma-buf-sync.txt
create mode 100644 drivers/base/dmabuf-sync.c
create mode 100644 include/linux/dmabuf-sync.h
--
1.7.9.5
^ permalink raw reply
* Re: [PATCH 02/11] omapdss: HDMI: create a HDMI PLL library
From: Tomi Valkeinen @ 2013-09-17 10:02 UTC (permalink / raw)
To: Mike Turquette; +Cc: Archit Taneja, linux-omap, linux-fbdev
In-Reply-To: <20130917093813.27384.67975@quantum>
[-- Attachment #1: Type: text/plain, Size: 1656 bytes --]
On 17/09/13 12:38, Mike Turquette wrote:
> Quoting Archit Taneja (2013-09-17 00:06:28)
>> HDMI PLL is a block common to DSS in OMAP4, OMAP5 and DRA7x. Move the
>> existing PLL functions from ti_hdmi_4xxx_ip.c and hdmi.c to a separate file.
>> These funcs are called directly from the hdmi driver rather than hdmi_ip_ops
>> function pointer calls.
>>
>> Add the PLL library function declarations to ti_hdmi.h. These will be shared
>> amongst the omap4/5 hdmi platform drivers. Remove the PLL function pointer ops
>> from the ti_hdmi_ip_ops struct. These will be shared amongst the omap4/5 hdmi
>> platform drivers and other libraries.
>>
>> Signed-off-by: Archit Taneja <archit@ti.com>
>
> Would be cool to see this convert to the common clock framework
> implementation (include/linux/clk-provider.h). It appears that this PLL
> only needs to support .enable, .disable and .recalc_rate callbacks at
> first glance.
We've got a (very) long term plan to use CCF for DSS. And, if I'm not
mistaken, the PLL used for HDMI and DSI is the same as used elsewhere in
OMAP (I don't remember the PLL type name).
I think the main issue with DSS clocks is that we require as exact
clocks as possible, and there are multiple dividers/multipliers on the
clock path. So we iterate over the divider/multiplier ranges, trying to
find as good freq match as possible.
So if the clock path is composed from "black boxes", and we can only ask
for a certain frequency, and get back probably some other frequency, it
gets quite difficult to find good clock matches. Well, at least I
imagine so, I have not tried to implement that.
Tomi
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 901 bytes --]
^ permalink raw reply
* Re: [PATCH 02/11] omapdss: HDMI: create a HDMI PLL library
From: Mike Turquette @ 2013-09-17 9:38 UTC (permalink / raw)
To: tomi.valkeinen; +Cc: linux-omap, linux-fbdev, Archit Taneja
In-Reply-To: <1379401597-27222-3-git-send-email-archit@ti.com>
Quoting Archit Taneja (2013-09-17 00:06:28)
> HDMI PLL is a block common to DSS in OMAP4, OMAP5 and DRA7x. Move the
> existing PLL functions from ti_hdmi_4xxx_ip.c and hdmi.c to a separate file.
> These funcs are called directly from the hdmi driver rather than hdmi_ip_ops
> function pointer calls.
>
> Add the PLL library function declarations to ti_hdmi.h. These will be shared
> amongst the omap4/5 hdmi platform drivers. Remove the PLL function pointer ops
> from the ti_hdmi_ip_ops struct. These will be shared amongst the omap4/5 hdmi
> platform drivers and other libraries.
>
> Signed-off-by: Archit Taneja <archit@ti.com>
Would be cool to see this convert to the common clock framework
implementation (include/linux/clk-provider.h). It appears that this PLL
only needs to support .enable, .disable and .recalc_rate callbacks at
first glance.
Regards,
Mike
> ---
> drivers/video/omap2/dss/Makefile | 2 +-
> drivers/video/omap2/dss/dss_features.c | 3 -
> drivers/video/omap2/dss/hdmi.c | 65 ++------
> drivers/video/omap2/dss/hdmi_pll.c | 243 ++++++++++++++++++++++++++++++
> drivers/video/omap2/dss/ti_hdmi.h | 25 +--
> drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 132 ----------------
> 6 files changed, 267 insertions(+), 203 deletions(-)
> create mode 100644 drivers/video/omap2/dss/hdmi_pll.c
>
> diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile
> index 56ce6bd..5ea65d3 100644
> --- a/drivers/video/omap2/dss/Makefile
> +++ b/drivers/video/omap2/dss/Makefile
> @@ -10,5 +10,5 @@ omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o
> omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o
> omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o
> omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o
> -omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o hdmi_wp.o ti_hdmi_4xxx_ip.o
> +omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o hdmi_wp.o hdmi_pll.o ti_hdmi_4xxx_ip.o
> ccflags-$(CONFIG_OMAP2_DSS_DEBUG) += -DDEBUG
> diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c
> index db359e8..9ee92e9 100644
> --- a/drivers/video/omap2/dss/dss_features.c
> +++ b/drivers/video/omap2/dss/dss_features.c
> @@ -797,10 +797,7 @@ static const struct ti_hdmi_ip_ops omap4_hdmi_functions = {
> .phy_enable = ti_hdmi_4xxx_phy_enable,
> .phy_disable = ti_hdmi_4xxx_phy_disable,
> .read_edid = ti_hdmi_4xxx_read_edid,
> - .pll_enable = ti_hdmi_4xxx_pll_enable,
> - .pll_disable = ti_hdmi_4xxx_pll_disable,
> .dump_core = ti_hdmi_4xxx_core_dump,
> - .dump_pll = ti_hdmi_4xxx_pll_dump,
> .dump_phy = ti_hdmi_4xxx_phy_dump,
> #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
> .audio_start = ti_hdmi_4xxx_audio_start,
> diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
> index f2475fc..f6a2eba 100644
> --- a/drivers/video/omap2/dss/hdmi.c
> +++ b/drivers/video/omap2/dss/hdmi.c
> @@ -42,7 +42,6 @@
>
> #define HDMI_CORE_SYS 0x400
> #define HDMI_CORE_AV 0x900
> -#define HDMI_PLLCTRL 0x200
> #define HDMI_PHY 0x300
>
> /* HDMI EDID Length move this */
> @@ -53,9 +52,6 @@
> #define EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR 4
> #define EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR 4
>
> -#define HDMI_DEFAULT_REGN 16
> -#define HDMI_DEFAULT_REGM2 1
> -
> static struct {
> struct mutex lock;
> struct platform_device *pdev;
> @@ -428,52 +424,6 @@ end: return cm;
>
> }
>
> -static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy,
> - struct hdmi_pll_info *pi)
> -{
> - unsigned long clkin, refclk;
> - u32 mf;
> -
> - clkin = clk_get_rate(hdmi.sys_clk) / 10000;
> - /*
> - * Input clock is predivided by N + 1
> - * out put of which is reference clk
> - */
> -
> - pi->regn = HDMI_DEFAULT_REGN;
> -
> - refclk = clkin / pi->regn;
> -
> - pi->regm2 = HDMI_DEFAULT_REGM2;
> -
> - /*
> - * multiplier is pixel_clk/ref_clk
> - * Multiplying by 100 to avoid fractional part removal
> - */
> - pi->regm = phy * pi->regm2 / refclk;
> -
> - /*
> - * fractional multiplier is remainder of the difference between
> - * multiplier and actual phy(required pixel clock thus should be
> - * multiplied by 2^18(262144) divided by the reference clock
> - */
> - mf = (phy - pi->regm / pi->regm2 * refclk) * 262144;
> - pi->regmf = pi->regm2 * mf / refclk;
> -
> - /*
> - * Dcofreq should be set to 1 if required pixel clock
> - * is greater than 1000MHz
> - */
> - pi->dcofreq = phy > 1000 * 100;
> - pi->regsd = ((pi->regm * clkin / 10) / (pi->regn * 250) + 5) / 10;
> -
> - /* Set the reference clock to sysclk reference */
> - pi->refsel = HDMI_REFSEL_SYSCLK;
> -
> - DSSDBG("M = %d Mf = %d\n", pi->regm, pi->regmf);
> - DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd);
> -}
> -
> static int hdmi_power_on_core(struct omap_dss_device *dssdev)
> {
> int r;
> @@ -526,12 +476,12 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
>
> phy = p->pixel_clock;
>
> - hdmi_compute_pll(dssdev, phy, &hdmi.ip_data.pll_data);
> + hdmi_pll_compute(&hdmi.ip_data.pll, clk_get_rate(hdmi.sys_clk), phy);
>
> hdmi_wp_video_stop(&hdmi.ip_data.wp);
>
> /* config the PLL and PHY hdmi_set_pll_pwrfirst */
> - r = hdmi.ip_data.ops->pll_enable(&hdmi.ip_data);
> + r = hdmi_pll_enable(&hdmi.ip_data.pll, &hdmi.ip_data.wp);
> if (r) {
> DSSDBG("Failed to lock PLL\n");
> goto err_pll_enable;
> @@ -566,7 +516,7 @@ err_mgr_enable:
> err_vid_enable:
> hdmi.ip_data.ops->phy_disable(&hdmi.ip_data);
> err_phy_enable:
> - hdmi.ip_data.ops->pll_disable(&hdmi.ip_data);
> + hdmi_pll_disable(&hdmi.ip_data.pll, &hdmi.ip_data.wp);
> err_pll_enable:
> hdmi_power_off_core(dssdev);
> return -EIO;
> @@ -580,7 +530,7 @@ static void hdmi_power_off_full(struct omap_dss_device *dssdev)
>
> hdmi_wp_video_stop(&hdmi.ip_data.wp);
> hdmi.ip_data.ops->phy_disable(&hdmi.ip_data);
> - hdmi.ip_data.ops->pll_disable(&hdmi.ip_data);
> + hdmi_pll_disable(&hdmi.ip_data.pll, &hdmi.ip_data.wp);
>
> hdmi_power_off_core(dssdev);
> }
> @@ -642,7 +592,7 @@ static void hdmi_dump_regs(struct seq_file *s)
> }
>
> hdmi_wp_dump(&hdmi.ip_data.wp, s);
> - hdmi.ip_data.ops->dump_pll(&hdmi.ip_data, s);
> + hdmi_pll_dump(&hdmi.ip_data.pll, s);
> hdmi.ip_data.ops->dump_phy(&hdmi.ip_data, s);
> hdmi.ip_data.ops->dump_core(&hdmi.ip_data, s);
>
> @@ -1095,6 +1045,10 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
> if (r)
> return r;
>
> + r = hdmi_pll_init(pdev, &hdmi.ip_data.pll);
> + if (r)
> + return r;
> +
> hdmi.ip_data.irq = platform_get_irq(pdev, 0);
> if (hdmi.ip_data.irq < 0) {
> DSSERR("platform_get_irq failed\n");
> @@ -1111,7 +1065,6 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
>
> hdmi.ip_data.core_sys_offset = HDMI_CORE_SYS;
> hdmi.ip_data.core_av_offset = HDMI_CORE_AV;
> - hdmi.ip_data.pll_offset = HDMI_PLLCTRL;
> hdmi.ip_data.phy_offset = HDMI_PHY;
>
> hdmi_init_output(pdev);
> diff --git a/drivers/video/omap2/dss/hdmi_pll.c b/drivers/video/omap2/dss/hdmi_pll.c
> new file mode 100644
> index 0000000..d53b8e2
> --- /dev/null
> +++ b/drivers/video/omap2/dss/hdmi_pll.c
> @@ -0,0 +1,243 @@
> +/*
> + * HDMI PLL
> + *
> + * Copyright (C) 2013 Texas Instruments Incorporated
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/platform_device.h>
> +#include <video/omapdss.h>
> +
> +#include "dss.h"
> +#include "ti_hdmi.h"
> +#include "ti_hdmi_4xxx_ip.h"
> +
> +#define HDMI_DEFAULT_REGN 16
> +#define HDMI_DEFAULT_REGM2 1
> +
> +static inline void hdmi_write_reg(void __iomem *base_addr, const u16 idx,
> + u32 val)
> +{
> + __raw_writel(val, base_addr + idx);
> +}
> +
> +static inline u32 hdmi_read_reg(void __iomem *base_addr, const u16 idx)
> +{
> + return __raw_readl(base_addr + idx);
> +}
> +
> +#define REG_FLD_MOD(base, idx, val, start, end) \
> + hdmi_write_reg(base, idx, FLD_MOD(hdmi_read_reg(base, idx),\
> + val, start, end))
> +#define REG_GET(base, idx, start, end) \
> + FLD_GET(hdmi_read_reg(base, idx), start, end)
> +
> +static inline int hdmi_wait_for_bit_change(void __iomem *base_addr,
> + const u16 idx, int b2, int b1, u32 val)
> +{
> + u32 t = 0;
> + while (val != REG_GET(base_addr, idx, b2, b1)) {
> + udelay(1);
> + if (t++ > 10000)
> + return !val;
> + }
> + return val;
> +}
> +
> +void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s)
> +{
> +#define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\
> + hdmi_read_reg(pll->base, r))
> +
> + DUMPPLL(PLLCTRL_PLL_CONTROL);
> + DUMPPLL(PLLCTRL_PLL_STATUS);
> + DUMPPLL(PLLCTRL_PLL_GO);
> + DUMPPLL(PLLCTRL_CFG1);
> + DUMPPLL(PLLCTRL_CFG2);
> + DUMPPLL(PLLCTRL_CFG3);
> + DUMPPLL(PLLCTRL_SSC_CFG1);
> + DUMPPLL(PLLCTRL_SSC_CFG2);
> + DUMPPLL(PLLCTRL_CFG4);
> +}
> +
> +void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy)
> +{
> + struct hdmi_pll_info *pi = &pll->info;
> + unsigned long refclk;
> + u32 mf;
> +
> + /* use our funky units */
> + clkin /= 10000;
> +
> + /*
> + * Input clock is predivided by N + 1
> + * out put of which is reference clk
> + */
> +
> + pi->regn = HDMI_DEFAULT_REGN;
> +
> + refclk = clkin / pi->regn;
> +
> + pi->regm2 = HDMI_DEFAULT_REGM2;
> +
> + /*
> + * multiplier is pixel_clk/ref_clk
> + * Multiplying by 100 to avoid fractional part removal
> + */
> + pi->regm = phy * pi->regm2 / refclk;
> +
> + /*
> + * fractional multiplier is remainder of the difference between
> + * multiplier and actual phy(required pixel clock thus should be
> + * multiplied by 2^18(262144) divided by the reference clock
> + */
> + mf = (phy - pi->regm / pi->regm2 * refclk) * 262144;
> + pi->regmf = pi->regm2 * mf / refclk;
> +
> + /*
> + * Dcofreq should be set to 1 if required pixel clock
> + * is greater than 1000MHz
> + */
> + pi->dcofreq = phy > 1000 * 100;
> + pi->regsd = ((pi->regm * clkin / 10) / (pi->regn * 250) + 5) / 10;
> +
> + /* Set the reference clock to sysclk reference */
> + pi->refsel = HDMI_REFSEL_SYSCLK;
> +
> + DSSDBG("M = %d Mf = %d\n", pi->regm, pi->regmf);
> + DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd);
> +}
> +
> +
> +static int hdmi_pll_config(struct hdmi_pll_data *pll)
> +{
> + u32 r;
> + struct hdmi_pll_info *fmt = &pll->info;
> +
> + /* PLL start always use manual mode */
> + REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, 0x0, 0, 0);
> +
> + r = hdmi_read_reg(pll->base, PLLCTRL_CFG1);
> + r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1_PLL_REGM */
> + r = FLD_MOD(r, fmt->regn - 1, 8, 1); /* CFG1_PLL_REGN */
> + hdmi_write_reg(pll->base, PLLCTRL_CFG1, r);
> +
> + r = hdmi_read_reg(pll->base, PLLCTRL_CFG2);
> +
> + r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */
> + r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */
> + r = FLD_MOD(r, 0x0, 14, 14); /* PHY_CLKINEN de-assert during locking */
> + r = FLD_MOD(r, fmt->refsel, 22, 21); /* REFSEL */
> +
> + if (fmt->dcofreq) {
> + /* divider programming for frequency beyond 1000Mhz */
> + REG_FLD_MOD(pll->base, PLLCTRL_CFG3, fmt->regsd, 17, 10);
> + r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */
> + } else {
> + r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */
> + }
> +
> + hdmi_write_reg(pll->base, PLLCTRL_CFG2, r);
> +
> + r = hdmi_read_reg(pll->base, PLLCTRL_CFG4);
> + r = FLD_MOD(r, fmt->regm2, 24, 18);
> + r = FLD_MOD(r, fmt->regmf, 17, 0);
> + hdmi_write_reg(pll->base, PLLCTRL_CFG4, r);
> +
> + /* go now */
> + REG_FLD_MOD(pll->base, PLLCTRL_PLL_GO, 0x1, 0, 0);
> +
> + /* wait for bit change */
> + if (hdmi_wait_for_bit_change(pll->base, PLLCTRL_PLL_GO,
> + 0, 0, 1) != 1) {
> + pr_err("PLL GO bit not set\n");
> + return -ETIMEDOUT;
> + }
> +
> + /* Wait till the lock bit is set in PLL status */
> + if (hdmi_wait_for_bit_change(pll->base,
> + PLLCTRL_PLL_STATUS, 1, 1, 1) != 1) {
> + pr_err("cannot lock PLL\n");
> + pr_err("CFG1 0x%x\n",
> + hdmi_read_reg(pll->base, PLLCTRL_CFG1));
> + pr_err("CFG2 0x%x\n",
> + hdmi_read_reg(pll->base, PLLCTRL_CFG2));
> + pr_err("CFG4 0x%x\n",
> + hdmi_read_reg(pll->base, PLLCTRL_CFG4));
> + return -ETIMEDOUT;
> + }
> +
> + pr_debug("PLL locked!\n");
> +
> + return 0;
> +}
> +
> +static int hdmi_pll_reset(struct hdmi_pll_data *pll)
> +{
> + /* SYSRESET controlled by power FSM */
> + REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, 0x0, 3, 3);
> +
> + /* READ 0x0 reset is in progress */
> + if (hdmi_wait_for_bit_change(pll->base, PLLCTRL_PLL_STATUS, 0, 0, 1)
> + != 1) {
> + pr_err("Failed to sysreset PLL\n");
> + return -ETIMEDOUT;
> + }
> +
> + return 0;
> +}
> +
> +int hdmi_pll_enable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp)
> +{
> + u16 r = 0;
> +
> + r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF);
> + if (r)
> + return r;
> +
> + r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS);
> + if (r)
> + return r;
> +
> + r = hdmi_pll_reset(pll);
> + if (r)
> + return r;
> +
> + r = hdmi_pll_config(pll);
> + if (r)
> + return r;
> +
> + return 0;
> +}
> +
> +void hdmi_pll_disable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp)
> +{
> + hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF);
> +}
> +
> +int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll)
> +{
> + struct resource *res;
> +
> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi_pllctrl");
> + if (!res) {
> + DSSERR("can't get PLL CTRL IORESOURCE_MEM HDMI\n");
> + return -EINVAL;
> + }
> +
> + pll->base = devm_request_and_ioremap(&pdev->dev, res);
> + if (!pll->base) {
> + DSSERR("can't ioremap PLL ctrl\n");
> + return -ENOMEM;
> + }
> +
> + return 0;
> +}
> diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h
> index d16f28d..62a83c7 100644
> --- a/drivers/video/omap2/dss/ti_hdmi.h
> +++ b/drivers/video/omap2/dss/ti_hdmi.h
> @@ -155,14 +155,8 @@ struct ti_hdmi_ip_ops {
>
> int (*read_edid)(struct hdmi_ip_data *ip_data, u8 *edid, int len);
>
> - int (*pll_enable)(struct hdmi_ip_data *ip_data);
> -
> - void (*pll_disable)(struct hdmi_ip_data *ip_data);
> -
> void (*dump_core)(struct hdmi_ip_data *ip_data, struct seq_file *s);
>
> - void (*dump_pll)(struct hdmi_ip_data *ip_data, struct seq_file *s);
> -
> void (*dump_phy)(struct hdmi_ip_data *ip_data, struct seq_file *s);
>
> #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
> @@ -223,17 +217,22 @@ struct hdmi_wp_data {
> void __iomem *base;
> };
>
> +struct hdmi_pll_data {
> + void __iomem *base;
> +
> + struct hdmi_pll_info info;
> +};
> +
> struct hdmi_ip_data {
> struct hdmi_wp_data wp;
> + struct hdmi_pll_data pll;
>
> unsigned long core_sys_offset;
> unsigned long core_av_offset;
> - unsigned long pll_offset;
> unsigned long phy_offset;
> int irq;
> const struct ti_hdmi_ip_ops *ops;
> struct hdmi_config cfg;
> - struct hdmi_pll_info pll_data;
> struct hdmi_core_infoframe_avi avi_cfg;
>
> /* ti_hdmi_4xxx_ip private data. These should be in a separate struct */
> @@ -260,13 +259,17 @@ void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt,
> struct omap_video_timings *timings, struct hdmi_config *param);
> int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp);
>
> +/* HDMI PLL funcs */
> +int hdmi_pll_enable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp);
> +void hdmi_pll_disable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp);
> +void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s);
> +void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy);
> +int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll);
> +
> int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data);
> void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data);
> int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data, u8 *edid, int len);
> -int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data);
> -void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data);
> void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data);
> -void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
> void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
> void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
> #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
> diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
> index d4b8883..8cfb54b 100644
> --- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
> +++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
> @@ -75,11 +75,6 @@ static inline void __iomem *hdmi_phy_base(struct hdmi_ip_data *ip_data)
> return ip_data->wp.base + ip_data->phy_offset;
> }
>
> -static inline void __iomem *hdmi_pll_base(struct hdmi_ip_data *ip_data)
> -{
> - return ip_data->wp.base + ip_data->pll_offset;
> -}
> -
> static inline void __iomem *hdmi_av_base(struct hdmi_ip_data *ip_data)
> {
> return ip_data->wp.base + ip_data->core_av_offset;
> @@ -90,117 +85,6 @@ static inline void __iomem *hdmi_core_sys_base(struct hdmi_ip_data *ip_data)
> return ip_data->wp.base + ip_data->core_sys_offset;
> }
>
> -
> -static int hdmi_pll_init(struct hdmi_ip_data *ip_data)
> -{
> - u32 r;
> - void __iomem *pll_base = hdmi_pll_base(ip_data);
> - struct hdmi_pll_info *fmt = &ip_data->pll_data;
> -
> - /* PLL start always use manual mode */
> - REG_FLD_MOD(pll_base, PLLCTRL_PLL_CONTROL, 0x0, 0, 0);
> -
> - r = hdmi_read_reg(pll_base, PLLCTRL_CFG1);
> - r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1_PLL_REGM */
> - r = FLD_MOD(r, fmt->regn - 1, 8, 1); /* CFG1_PLL_REGN */
> -
> - hdmi_write_reg(pll_base, PLLCTRL_CFG1, r);
> -
> - r = hdmi_read_reg(pll_base, PLLCTRL_CFG2);
> -
> - r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */
> - r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */
> - r = FLD_MOD(r, 0x0, 14, 14); /* PHY_CLKINEN de-assert during locking */
> - r = FLD_MOD(r, fmt->refsel, 22, 21); /* REFSEL */
> -
> - if (fmt->dcofreq) {
> - /* divider programming for frequency beyond 1000Mhz */
> - REG_FLD_MOD(pll_base, PLLCTRL_CFG3, fmt->regsd, 17, 10);
> - r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */
> - } else {
> - r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */
> - }
> -
> - hdmi_write_reg(pll_base, PLLCTRL_CFG2, r);
> -
> - r = hdmi_read_reg(pll_base, PLLCTRL_CFG4);
> - r = FLD_MOD(r, fmt->regm2, 24, 18);
> - r = FLD_MOD(r, fmt->regmf, 17, 0);
> -
> - hdmi_write_reg(pll_base, PLLCTRL_CFG4, r);
> -
> - /* go now */
> - REG_FLD_MOD(pll_base, PLLCTRL_PLL_GO, 0x1, 0, 0);
> -
> - /* wait for bit change */
> - if (hdmi_wait_for_bit_change(pll_base, PLLCTRL_PLL_GO,
> - 0, 0, 1) != 1) {
> - pr_err("PLL GO bit not set\n");
> - return -ETIMEDOUT;
> - }
> -
> - /* Wait till the lock bit is set in PLL status */
> - if (hdmi_wait_for_bit_change(pll_base,
> - PLLCTRL_PLL_STATUS, 1, 1, 1) != 1) {
> - pr_err("cannot lock PLL\n");
> - pr_err("CFG1 0x%x\n",
> - hdmi_read_reg(pll_base, PLLCTRL_CFG1));
> - pr_err("CFG2 0x%x\n",
> - hdmi_read_reg(pll_base, PLLCTRL_CFG2));
> - pr_err("CFG4 0x%x\n",
> - hdmi_read_reg(pll_base, PLLCTRL_CFG4));
> - return -ETIMEDOUT;
> - }
> -
> - pr_debug("PLL locked!\n");
> -
> - return 0;
> -}
> -
> -
> -static int hdmi_pll_reset(struct hdmi_ip_data *ip_data)
> -{
> - /* SYSRESET controlled by power FSM */
> - REG_FLD_MOD(hdmi_pll_base(ip_data), PLLCTRL_PLL_CONTROL, 0x0, 3, 3);
> -
> - /* READ 0x0 reset is in progress */
> - if (hdmi_wait_for_bit_change(hdmi_pll_base(ip_data),
> - PLLCTRL_PLL_STATUS, 0, 0, 1) != 1) {
> - pr_err("Failed to sysreset PLL\n");
> - return -ETIMEDOUT;
> - }
> -
> - return 0;
> -}
> -
> -int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data)
> -{
> - u16 r = 0;
> -
> - r = hdmi_wp_set_pll_pwr(&ip_data->wp, HDMI_PLLPWRCMD_ALLOFF);
> - if (r)
> - return r;
> -
> - r = hdmi_wp_set_pll_pwr(&ip_data->wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS);
> - if (r)
> - return r;
> -
> - r = hdmi_pll_reset(ip_data);
> - if (r)
> - return r;
> -
> - r = hdmi_pll_init(ip_data);
> - if (r)
> - return r;
> -
> - return 0;
> -}
> -
> -void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data)
> -{
> - hdmi_wp_set_pll_pwr(&ip_data->wp, HDMI_PLLPWRCMD_ALLOFF);
> -}
> -
> static irqreturn_t hdmi_irq_handler(int irq, void *data)
> {
> struct hdmi_ip_data *ip_data = data;
> @@ -717,22 +601,6 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data)
> hdmi_core_av_packet_config(ip_data, repeat_cfg);
> }
>
> -void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
> -{
> -#define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\
> - hdmi_read_reg(hdmi_pll_base(ip_data), r))
> -
> - DUMPPLL(PLLCTRL_PLL_CONTROL);
> - DUMPPLL(PLLCTRL_PLL_STATUS);
> - DUMPPLL(PLLCTRL_PLL_GO);
> - DUMPPLL(PLLCTRL_CFG1);
> - DUMPPLL(PLLCTRL_CFG2);
> - DUMPPLL(PLLCTRL_CFG3);
> - DUMPPLL(PLLCTRL_SSC_CFG1);
> - DUMPPLL(PLLCTRL_SSC_CFG2);
> - DUMPPLL(PLLCTRL_CFG4);
> -}
> -
> void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
> {
> int i;
> --
> 1.8.1.2
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] pwm-backlight: allow for non-increasing brightness levels
From: Sascha Hauer @ 2013-09-17 9:36 UTC (permalink / raw)
To: Mike Dunn
Cc: thierry.reding-Re5JQEeQqe8AvxtiuMwx3w, Richard Purdie, Jingoo Han,
Jean-Christophe Plagniol-Villard, Tomi Valkeinen, Grant Likely,
Rob Herring, linux-pwm-u79uwXL29TY76Z2rM5mHXA,
linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Robert Jarzmik, Marek Vasut
In-Reply-To: <1379010952-8928-1-git-send-email-mikedunn-kFrNdAxtuftBDgjK7y7TUQ@public.gmane.org>
Hi Mike,
On Thu, Sep 12, 2013 at 11:35:52AM -0700, Mike Dunn wrote:
> Currently the driver assumes that the values specified in the brightness-levels
> device tree property increase as they are parsed from left to right. But boards
> that invert the signal between the PWM output and the backlight will need to
> specify decreasing brightness-levels. This patch removes the assumption that
> the last element of the array is the max value, and instead searches the array
> for the max value and uses that as the normalizing value when determining the
> duty cycle.
Note there's also support for inverted PWMs in the PWM framework
provided your hardware supports this.
Sascha
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply
* [PATCH 11/11] [experimental] arm: omap: omap4 hwmod data: Split hdmi address space
From: Archit Taneja @ 2013-09-17 7:18 UTC (permalink / raw)
To: tomi.valkeinen; +Cc: linux-omap, linux-fbdev, Archit Taneja
In-Reply-To: <1379401597-27222-1-git-send-email-archit@ti.com>
Split hdmi address space in order to get base addresses of hdmi submodules by
name. This is just a demonstration patch for the hdmi refactoring to work. The
address data should belong to the hdmi DT node.
Signed-off-by: Archit Taneja <archit@ti.com>
---
arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
index 9c3b504..3b12339 100644
--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -3843,7 +3843,26 @@ static struct omap_hwmod_ocp_if omap44xx_l3_main_2__dss_hdmi = {
static struct omap_hwmod_addr_space omap44xx_dss_hdmi_addrs[] = {
{
+ .name = "hdmi_wp",
.pa_start = 0x48046000,
+ .pa_end = 0x480461ff,
+ .flags = ADDR_TYPE_RT
+ },
+ {
+ .name = "hdmi_pllctrl",
+ .pa_start = 0x48046200,
+ .pa_end = 0x480462ff,
+ .flags = ADDR_TYPE_RT
+ },
+ {
+ .name = "hdmi_txphy",
+ .pa_start = 0x48046300,
+ .pa_end = 0x480463ff,
+ .flags = ADDR_TYPE_RT
+ },
+ {
+ .name = "hdmi_core",
+ .pa_start = 0x48046400,
.pa_end = 0x48046fff,
.flags = ADDR_TYPE_RT
},
--
1.8.1.2
^ permalink raw reply related
* [PATCH 10/11] omapdss: HDMI: move common functions to a separate file
From: Archit Taneja @ 2013-09-17 7:18 UTC (permalink / raw)
To: tomi.valkeinen; +Cc: linux-omap, linux-fbdev, Archit Taneja
In-Reply-To: <1379401597-27222-1-git-send-email-archit@ti.com>
The OMAP4 HDMI encoder driver(hdmi4.c) contains timings tables, and helper
functions which can be used as is by the OMAP5/DRA7x encoder driver. Move these
to hdmi_common.c so that it's not replicated in the future.
Signed-off-by: Archit Taneja <archit@ti.com>
---
drivers/video/omap2/dss/Makefile | 4 +-
drivers/video/omap2/dss/hdmi4.c | 438 +---------------------------------
drivers/video/omap2/dss/hdmi4_core.c | 4 +-
drivers/video/omap2/dss/hdmi4_core.h | 2 +-
drivers/video/omap2/dss/hdmi_common.c | 423 ++++++++++++++++++++++++++++++++
drivers/video/omap2/dss/ti_hdmi.h | 11 +-
6 files changed, 447 insertions(+), 435 deletions(-)
create mode 100644 drivers/video/omap2/dss/hdmi_common.c
diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile
index f87ca32..d3aa91b 100644
--- a/drivers/video/omap2/dss/Makefile
+++ b/drivers/video/omap2/dss/Makefile
@@ -10,6 +10,6 @@ omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o
omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o
omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o
omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o
-omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi4.o hdmi_wp.o hdmi_pll.o hdmi_phy.o \
- hdmi4_core.o
+omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi4.o hdmi_common.o hdmi_wp.o hdmi_pll.o \
+ hdmi_phy.o hdmi4_core.o
ccflags-$(CONFIG_OMAP2_DSS_DEBUG) += -DDEBUG
diff --git a/drivers/video/omap2/dss/hdmi4.c b/drivers/video/omap2/dss/hdmi4.c
index ab43069..e140096 100644
--- a/drivers/video/omap2/dss/hdmi4.c
+++ b/drivers/video/omap2/dss/hdmi4.c
@@ -57,237 +57,6 @@ static struct {
struct omap_dss_device output;
} hdmi;
-/*
- * Logic for the below structure :
- * user enters the CEA or VESA timings by specifying the HDMI/DVI code.
- * There is a correspondence between CEA/VESA timing and code, please
- * refer to section 6.3 in HDMI 1.3 specification for timing code.
- *
- * In the below structure, cea_vesa_timings corresponds to all OMAP4
- * supported CEA and VESA timing values.code_cea corresponds to the CEA
- * code, It is used to get the timing from cea_vesa_timing array.Similarly
- * with code_vesa. Code_index is used for back mapping, that is once EDID
- * is read from the TV, EDID is parsed to find the timing values and then
- * map it to corresponding CEA or VESA index.
- */
-
-static const struct hdmi_config cea_timings[] = {
- {
- { 640, 480, 25200, 96, 16, 48, 2, 10, 33,
- OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
- false, },
- { 1, HDMI_HDMI },
- },
- {
- { 720, 480, 27027, 62, 16, 60, 6, 9, 30,
- OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
- false, },
- { 2, HDMI_HDMI },
- },
- {
- { 1280, 720, 74250, 40, 110, 220, 5, 5, 20,
- OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
- false, },
- { 4, HDMI_HDMI },
- },
- {
- { 1920, 540, 74250, 44, 88, 148, 5, 2, 15,
- OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
- true, },
- { 5, HDMI_HDMI },
- },
- {
- { 1440, 240, 27027, 124, 38, 114, 3, 4, 15,
- OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
- true, },
- { 6, HDMI_HDMI },
- },
- {
- { 1920, 1080, 148500, 44, 88, 148, 5, 4, 36,
- OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
- false, },
- { 16, HDMI_HDMI },
- },
- {
- { 720, 576, 27000, 64, 12, 68, 5, 5, 39,
- OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
- false, },
- { 17, HDMI_HDMI },
- },
- {
- { 1280, 720, 74250, 40, 440, 220, 5, 5, 20,
- OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
- false, },
- { 19, HDMI_HDMI },
- },
- {
- { 1920, 540, 74250, 44, 528, 148, 5, 2, 15,
- OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
- true, },
- { 20, HDMI_HDMI },
- },
- {
- { 1440, 288, 27000, 126, 24, 138, 3, 2, 19,
- OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
- true, },
- { 21, HDMI_HDMI },
- },
- {
- { 1440, 576, 54000, 128, 24, 136, 5, 5, 39,
- OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
- false, },
- { 29, HDMI_HDMI },
- },
- {
- { 1920, 1080, 148500, 44, 528, 148, 5, 4, 36,
- OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
- false, },
- { 31, HDMI_HDMI },
- },
- {
- { 1920, 1080, 74250, 44, 638, 148, 5, 4, 36,
- OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
- false, },
- { 32, HDMI_HDMI },
- },
- {
- { 2880, 480, 108108, 248, 64, 240, 6, 9, 30,
- OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
- false, },
- { 35, HDMI_HDMI },
- },
- {
- { 2880, 576, 108000, 256, 48, 272, 5, 5, 39,
- OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
- false, },
- { 37, HDMI_HDMI },
- },
-};
-
-static const struct hdmi_config vesa_timings[] = {
-/* VESA From Here */
- {
- { 640, 480, 25175, 96, 16, 48, 2, 11, 31,
- OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
- false, },
- { 4, HDMI_DVI },
- },
- {
- { 800, 600, 40000, 128, 40, 88, 4, 1, 23,
- OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
- false, },
- { 9, HDMI_DVI },
- },
- {
- { 848, 480, 33750, 112, 16, 112, 8, 6, 23,
- OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
- false, },
- { 0xE, HDMI_DVI },
- },
- {
- { 1280, 768, 79500, 128, 64, 192, 7, 3, 20,
- OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
- false, },
- { 0x17, HDMI_DVI },
- },
- {
- { 1280, 800, 83500, 128, 72, 200, 6, 3, 22,
- OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
- false, },
- { 0x1C, HDMI_DVI },
- },
- {
- { 1360, 768, 85500, 112, 64, 256, 6, 3, 18,
- OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
- false, },
- { 0x27, HDMI_DVI },
- },
- {
- { 1280, 960, 108000, 112, 96, 312, 3, 1, 36,
- OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
- false, },
- { 0x20, HDMI_DVI },
- },
- {
- { 1280, 1024, 108000, 112, 48, 248, 3, 1, 38,
- OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
- false, },
- { 0x23, HDMI_DVI },
- },
- {
- { 1024, 768, 65000, 136, 24, 160, 6, 3, 29,
- OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
- false, },
- { 0x10, HDMI_DVI },
- },
- {
- { 1400, 1050, 121750, 144, 88, 232, 4, 3, 32,
- OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
- false, },
- { 0x2A, HDMI_DVI },
- },
- {
- { 1440, 900, 106500, 152, 80, 232, 6, 3, 25,
- OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
- false, },
- { 0x2F, HDMI_DVI },
- },
- {
- { 1680, 1050, 146250, 176 , 104, 280, 6, 3, 30,
- OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
- false, },
- { 0x3A, HDMI_DVI },
- },
- {
- { 1366, 768, 85500, 143, 70, 213, 3, 3, 24,
- OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
- false, },
- { 0x51, HDMI_DVI },
- },
- {
- { 1920, 1080, 148500, 44, 148, 80, 5, 4, 36,
- OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
- false, },
- { 0x52, HDMI_DVI },
- },
- {
- { 1280, 768, 68250, 32, 48, 80, 7, 3, 12,
- OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
- false, },
- { 0x16, HDMI_DVI },
- },
- {
- { 1400, 1050, 101000, 32, 48, 80, 4, 3, 23,
- OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
- false, },
- { 0x29, HDMI_DVI },
- },
- {
- { 1680, 1050, 119000, 32, 48, 80, 6, 3, 21,
- OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
- false, },
- { 0x39, HDMI_DVI },
- },
- {
- { 1280, 800, 79500, 32, 48, 80, 6, 3, 14,
- OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
- false, },
- { 0x1B, HDMI_DVI },
- },
- {
- { 1280, 720, 74250, 40, 110, 220, 5, 5, 20,
- OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
- false, },
- { 0x55, HDMI_DVI },
- },
- {
- { 1920, 1200, 154000, 32, 48, 80, 6, 3, 26,
- OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
- false, },
- { 0x44, HDMI_DVI },
- },
-};
-
static int hdmi_runtime_get(void)
{
int r;
@@ -335,86 +104,6 @@ static int hdmi_init_regulator(void)
return 0;
}
-static const struct hdmi_config *hdmi_find_timing(
- const struct hdmi_config *timings_arr,
- int len)
-{
- int i;
-
- for (i = 0; i < len; i++) {
- if (timings_arr[i].cm.code = hdmi.cfg.cm.code)
- return &timings_arr[i];
- }
- return NULL;
-}
-
-static const struct hdmi_config *hdmi_get_timings(void)
-{
- const struct hdmi_config *arr;
- int len;
-
- if (hdmi.cfg.cm.mode = HDMI_DVI) {
- arr = vesa_timings;
- len = ARRAY_SIZE(vesa_timings);
- } else {
- arr = cea_timings;
- len = ARRAY_SIZE(cea_timings);
- }
-
- return hdmi_find_timing(arr, len);
-}
-
-static bool hdmi_timings_compare(struct omap_video_timings *timing1,
- const struct omap_video_timings *timing2)
-{
- int timing1_vsync, timing1_hsync, timing2_vsync, timing2_hsync;
-
- if ((DIV_ROUND_CLOSEST(timing2->pixel_clock, 1000) =
- DIV_ROUND_CLOSEST(timing1->pixel_clock, 1000)) &&
- (timing2->x_res = timing1->x_res) &&
- (timing2->y_res = timing1->y_res)) {
-
- timing2_hsync = timing2->hfp + timing2->hsw + timing2->hbp;
- timing1_hsync = timing1->hfp + timing1->hsw + timing1->hbp;
- timing2_vsync = timing2->vfp + timing2->vsw + timing2->vbp;
- timing1_vsync = timing2->vfp + timing2->vsw + timing2->vbp;
-
- DSSDBG("timing1_hsync = %d timing1_vsync = %d"\
- "timing2_hsync = %d timing2_vsync = %d\n",
- timing1_hsync, timing1_vsync,
- timing2_hsync, timing2_vsync);
-
- if ((timing1_hsync = timing2_hsync) &&
- (timing1_vsync = timing2_vsync)) {
- return true;
- }
- }
- return false;
-}
-
-static struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing)
-{
- int i;
- struct hdmi_cm cm = {-1};
- DSSDBG("hdmi_get_code\n");
-
- for (i = 0; i < ARRAY_SIZE(cea_timings); i++) {
- if (hdmi_timings_compare(timing, &cea_timings[i].timings)) {
- cm = cea_timings[i].cm;
- goto end;
- }
- }
- for (i = 0; i < ARRAY_SIZE(vesa_timings); i++) {
- if (hdmi_timings_compare(timing, &vesa_timings[i].timings)) {
- cm = vesa_timings[i].cm;
- goto end;
- }
- }
-
-end: return cm;
-
-}
-
static int hdmi_power_on_core(struct omap_dss_device *dssdev)
{
int r;
@@ -550,7 +239,7 @@ static void hdmi_display_set_timing(struct omap_dss_device *dssdev,
cm = hdmi_get_code(timings);
hdmi.cfg.cm = cm;
- t = hdmi_get_timings();
+ t = hdmi_get_timings(cm.mode, cm.code);
if (t != NULL) {
hdmi.cfg = *t;
@@ -564,10 +253,11 @@ static void hdmi_display_get_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
const struct hdmi_config *cfg;
+ struct hdmi_cm cm = hdmi.cfg.cm;
- cfg = hdmi_get_timings();
+ cfg = hdmi_get_timings(cm.mode, cm.code);
if (cfg = NULL)
- cfg = &vesa_timings[0];
+ cfg = hdmi_default_timing();
memcpy(timings, &cfg->timings, sizeof(cfg->timings));
}
@@ -695,117 +385,6 @@ static int hdmi_get_clocks(struct platform_device *pdev)
return 0;
}
-#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
-int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts)
-{
- u32 deep_color;
- bool deep_color_correct = false;
- u32 pclk = hdmi.cfg.timings.pixel_clock;
-
- if (n = NULL || cts = NULL)
- return -EINVAL;
-
- /* TODO: When implemented, query deep color mode here. */
- deep_color = 100;
-
- /*
- * When using deep color, the default N value (as in the HDMI
- * specification) yields to an non-integer CTS. Hence, we
- * modify it while keeping the restrictions described in
- * section 7.2.1 of the HDMI 1.4a specification.
- */
- switch (sample_freq) {
- case 32000:
- case 48000:
- case 96000:
- case 192000:
- if (deep_color = 125)
- if (pclk = 27027 || pclk = 74250)
- deep_color_correct = true;
- if (deep_color = 150)
- if (pclk = 27027)
- deep_color_correct = true;
- break;
- case 44100:
- case 88200:
- case 176400:
- if (deep_color = 125)
- if (pclk = 27027)
- deep_color_correct = true;
- break;
- default:
- return -EINVAL;
- }
-
- if (deep_color_correct) {
- switch (sample_freq) {
- case 32000:
- *n = 8192;
- break;
- case 44100:
- *n = 12544;
- break;
- case 48000:
- *n = 8192;
- break;
- case 88200:
- *n = 25088;
- break;
- case 96000:
- *n = 16384;
- break;
- case 176400:
- *n = 50176;
- break;
- case 192000:
- *n = 32768;
- break;
- default:
- return -EINVAL;
- }
- } else {
- switch (sample_freq) {
- case 32000:
- *n = 4096;
- break;
- case 44100:
- *n = 6272;
- break;
- case 48000:
- *n = 6144;
- break;
- case 88200:
- *n = 12544;
- break;
- case 96000:
- *n = 12288;
- break;
- case 176400:
- *n = 25088;
- break;
- case 192000:
- *n = 24576;
- break;
- default:
- return -EINVAL;
- }
- }
- /* Calculate CTS. See HDMI 1.3a or 1.4a specifications */
- *cts = pclk * (*n / 128) * deep_color / (sample_freq / 10);
-
- return 0;
-}
-
-static bool hdmi_mode_has_audio(void)
-{
- if (hdmi.cfg.cm.mode = HDMI_HDMI)
- return true;
- else
- return false;
-}
-
-#endif
-
static int hdmi_connect(struct omap_dss_device *dssdev,
struct omap_dss_device *dst)
{
@@ -878,7 +457,7 @@ static int hdmi_audio_enable(struct omap_dss_device *dssdev)
mutex_lock(&hdmi.lock);
- if (!hdmi_mode_has_audio()) {
+ if (!hdmi_mode_has_audio(hdmi.cfg.cm.mode)) {
r = -EPERM;
goto err;
}
@@ -916,7 +495,7 @@ static bool hdmi_audio_supported(struct omap_dss_device *dssdev)
mutex_lock(&hdmi.lock);
- r = hdmi_mode_has_audio();
+ r = hdmi_mode_has_audio(hdmi.cfg.cm.mode);
mutex_unlock(&hdmi.lock);
return r;
@@ -926,15 +505,16 @@ static int hdmi_audio_config(struct omap_dss_device *dssdev,
struct omap_dss_audio *audio)
{
int r;
+ u32 pclk = hdmi.cfg.timings.pixel_clock;
mutex_lock(&hdmi.lock);
- if (!hdmi_mode_has_audio()) {
+ if (!hdmi_mode_has_audio(hdmi.cfg.cm.mode)) {
r = -EPERM;
goto err;
}
- r = hdmi4_audio_config(&hdmi.core, &hdmi.wp, audio);
+ r = hdmi4_audio_config(&hdmi.core, &hdmi.wp, audio, pclk);
if (r)
goto err;
diff --git a/drivers/video/omap2/dss/hdmi4_core.c b/drivers/video/omap2/dss/hdmi4_core.c
index 448e632..c7f5ec4 100644
--- a/drivers/video/omap2/dss/hdmi4_core.c
+++ b/drivers/video/omap2/dss/hdmi4_core.c
@@ -789,7 +789,7 @@ static void hdmi_core_audio_infoframe_cfg(struct hdmi_core_data *core,
}
int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
- struct omap_dss_audio *audio)
+ struct omap_dss_audio *audio, u32 pclk)
{
struct hdmi_audio_format audio_format;
struct hdmi_audio_dma audio_dma;
@@ -856,7 +856,7 @@ int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
return -EINVAL;
}
- err = hdmi_compute_acr(fs_nr, &n, &cts);
+ err = hdmi_compute_acr(pclk, fs_nr, &n, &cts);
/* Audio clock regeneration settings */
acore.n = n;
diff --git a/drivers/video/omap2/dss/hdmi4_core.h b/drivers/video/omap2/dss/hdmi4_core.h
index 78319ff..52d9c95 100644
--- a/drivers/video/omap2/dss/hdmi4_core.h
+++ b/drivers/video/omap2/dss/hdmi4_core.h
@@ -271,7 +271,7 @@ int hdmi4_core_init(struct platform_device *pdev, struct hdmi_core_data *core);
int hdmi4_audio_start(struct hdmi_core_data *core, struct hdmi_wp_data *wp);
void hdmi4_audio_stop(struct hdmi_core_data *core, struct hdmi_wp_data *wp);
int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
- struct omap_dss_audio *audio);
+ struct omap_dss_audio *audio, u32 pclk);
int hdmi4_audio_get_dma_port(u32 *offset, u32 *size);
#endif
diff --git a/drivers/video/omap2/dss/hdmi_common.c b/drivers/video/omap2/dss/hdmi_common.c
new file mode 100644
index 0000000..86f0505
--- /dev/null
+++ b/drivers/video/omap2/dss/hdmi_common.c
@@ -0,0 +1,423 @@
+
+/*
+ * Logic for the below structure :
+ * user enters the CEA or VESA timings by specifying the HDMI/DVI code.
+ * There is a correspondence between CEA/VESA timing and code, please
+ * refer to section 6.3 in HDMI 1.3 specification for timing code.
+ *
+ * In the below structure, cea_vesa_timings corresponds to all OMAP4
+ * supported CEA and VESA timing values.code_cea corresponds to the CEA
+ * code, It is used to get the timing from cea_vesa_timing array.Similarly
+ * with code_vesa. Code_index is used for back mapping, that is once EDID
+ * is read from the TV, EDID is parsed to find the timing values and then
+ * map it to corresponding CEA or VESA index.
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <video/omapdss.h>
+
+#include "ti_hdmi.h"
+
+static const struct hdmi_config cea_timings[] = {
+ {
+ { 640, 480, 25200, 96, 16, 48, 2, 10, 33,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 1, HDMI_HDMI },
+ },
+ {
+ { 720, 480, 27027, 62, 16, 60, 6, 9, 30,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 2, HDMI_HDMI },
+ },
+ {
+ { 1280, 720, 74250, 40, 110, 220, 5, 5, 20,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 4, HDMI_HDMI },
+ },
+ {
+ { 1920, 540, 74250, 44, 88, 148, 5, 2, 15,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ true, },
+ { 5, HDMI_HDMI },
+ },
+ {
+ { 1440, 240, 27027, 124, 38, 114, 3, 4, 15,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ true, },
+ { 6, HDMI_HDMI },
+ },
+ {
+ { 1920, 1080, 148500, 44, 88, 148, 5, 4, 36,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 16, HDMI_HDMI },
+ },
+ {
+ { 720, 576, 27000, 64, 12, 68, 5, 5, 39,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 17, HDMI_HDMI },
+ },
+ {
+ { 1280, 720, 74250, 40, 440, 220, 5, 5, 20,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 19, HDMI_HDMI },
+ },
+ {
+ { 1920, 540, 74250, 44, 528, 148, 5, 2, 15,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ true, },
+ { 20, HDMI_HDMI },
+ },
+ {
+ { 1440, 288, 27000, 126, 24, 138, 3, 2, 19,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ true, },
+ { 21, HDMI_HDMI },
+ },
+ {
+ { 1440, 576, 54000, 128, 24, 136, 5, 5, 39,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 29, HDMI_HDMI },
+ },
+ {
+ { 1920, 1080, 148500, 44, 528, 148, 5, 4, 36,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 31, HDMI_HDMI },
+ },
+ {
+ { 1920, 1080, 74250, 44, 638, 148, 5, 4, 36,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 32, HDMI_HDMI },
+ },
+ {
+ { 2880, 480, 108108, 248, 64, 240, 6, 9, 30,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 35, HDMI_HDMI },
+ },
+ {
+ { 2880, 576, 108000, 256, 48, 272, 5, 5, 39,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 37, HDMI_HDMI },
+ },
+};
+
+static const struct hdmi_config vesa_timings[] = {
+/* VESA From Here */
+ {
+ { 640, 480, 25175, 96, 16, 48, 2, 11, 31,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 4, HDMI_DVI },
+ },
+ {
+ { 800, 600, 40000, 128, 40, 88, 4, 1, 23,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 9, HDMI_DVI },
+ },
+ {
+ { 848, 480, 33750, 112, 16, 112, 8, 6, 23,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0xE, HDMI_DVI },
+ },
+ {
+ { 1280, 768, 79500, 128, 64, 192, 7, 3, 20,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 0x17, HDMI_DVI },
+ },
+ {
+ { 1280, 800, 83500, 128, 72, 200, 6, 3, 22,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 0x1C, HDMI_DVI },
+ },
+ {
+ { 1360, 768, 85500, 112, 64, 256, 6, 3, 18,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x27, HDMI_DVI },
+ },
+ {
+ { 1280, 960, 108000, 112, 96, 312, 3, 1, 36,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x20, HDMI_DVI },
+ },
+ {
+ { 1280, 1024, 108000, 112, 48, 248, 3, 1, 38,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x23, HDMI_DVI },
+ },
+ {
+ { 1024, 768, 65000, 136, 24, 160, 6, 3, 29,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 0x10, HDMI_DVI },
+ },
+ {
+ { 1400, 1050, 121750, 144, 88, 232, 4, 3, 32,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 0x2A, HDMI_DVI },
+ },
+ {
+ { 1440, 900, 106500, 152, 80, 232, 6, 3, 25,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 0x2F, HDMI_DVI },
+ },
+ {
+ { 1680, 1050, 146250, 176 , 104, 280, 6, 3, 30,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 0x3A, HDMI_DVI },
+ },
+ {
+ { 1366, 768, 85500, 143, 70, 213, 3, 3, 24,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x51, HDMI_DVI },
+ },
+ {
+ { 1920, 1080, 148500, 44, 148, 80, 5, 4, 36,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x52, HDMI_DVI },
+ },
+ {
+ { 1280, 768, 68250, 32, 48, 80, 7, 3, 12,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x16, HDMI_DVI },
+ },
+ {
+ { 1400, 1050, 101000, 32, 48, 80, 4, 3, 23,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x29, HDMI_DVI },
+ },
+ {
+ { 1680, 1050, 119000, 32, 48, 80, 6, 3, 21,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x39, HDMI_DVI },
+ },
+ {
+ { 1280, 800, 79500, 32, 48, 80, 6, 3, 14,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x1B, HDMI_DVI },
+ },
+ {
+ { 1280, 720, 74250, 40, 110, 220, 5, 5, 20,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x55, HDMI_DVI },
+ },
+ {
+ { 1920, 1200, 154000, 32, 48, 80, 6, 3, 26,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x44, HDMI_DVI },
+ },
+};
+
+const struct hdmi_config *hdmi_default_timing(void)
+{
+ return &vesa_timings[0];
+}
+
+static const struct hdmi_config *hdmi_find_timing(int code,
+ const struct hdmi_config *timings_arr, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (timings_arr[i].cm.code = code)
+ return &timings_arr[i];
+ }
+
+ return NULL;
+}
+
+const struct hdmi_config *hdmi_get_timings(int mode, int code)
+{
+ const struct hdmi_config *arr;
+ int len;
+
+ if (mode = HDMI_DVI) {
+ arr = vesa_timings;
+ len = ARRAY_SIZE(vesa_timings);
+ } else {
+ arr = cea_timings;
+ len = ARRAY_SIZE(cea_timings);
+ }
+
+ return hdmi_find_timing(code, arr, len);
+}
+
+static bool hdmi_timings_compare(struct omap_video_timings *timing1,
+ const struct omap_video_timings *timing2)
+{
+ int timing1_vsync, timing1_hsync, timing2_vsync, timing2_hsync;
+
+ if ((DIV_ROUND_CLOSEST(timing2->pixel_clock, 1000) =
+ DIV_ROUND_CLOSEST(timing1->pixel_clock, 1000)) &&
+ (timing2->x_res = timing1->x_res) &&
+ (timing2->y_res = timing1->y_res)) {
+
+ timing2_hsync = timing2->hfp + timing2->hsw + timing2->hbp;
+ timing1_hsync = timing1->hfp + timing1->hsw + timing1->hbp;
+ timing2_vsync = timing2->vfp + timing2->vsw + timing2->vbp;
+ timing1_vsync = timing2->vfp + timing2->vsw + timing2->vbp;
+
+ DSSDBG("timing1_hsync = %d timing1_vsync = %d"\
+ "timing2_hsync = %d timing2_vsync = %d\n",
+ timing1_hsync, timing1_vsync,
+ timing2_hsync, timing2_vsync);
+
+ if ((timing1_hsync = timing2_hsync) &&
+ (timing1_vsync = timing2_vsync)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing)
+{
+ int i;
+ struct hdmi_cm cm = {-1};
+ DSSDBG("hdmi_get_code\n");
+
+ for (i = 0; i < ARRAY_SIZE(cea_timings); i++) {
+ if (hdmi_timings_compare(timing, &cea_timings[i].timings)) {
+ cm = cea_timings[i].cm;
+ goto end;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(vesa_timings); i++) {
+ if (hdmi_timings_compare(timing, &vesa_timings[i].timings)) {
+ cm = vesa_timings[i].cm;
+ goto end;
+ }
+ }
+
+end:
+ return cm;
+}
+
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
+int hdmi_compute_acr(u32 pclk, u32 sample_freq, u32 *n, u32 *cts)
+{
+ u32 deep_color;
+ bool deep_color_correct = false;
+
+ if (n = NULL || cts = NULL)
+ return -EINVAL;
+
+ /* TODO: When implemented, query deep color mode here. */
+ deep_color = 100;
+
+ /*
+ * When using deep color, the default N value (as in the HDMI
+ * specification) yields to an non-integer CTS. Hence, we
+ * modify it while keeping the restrictions described in
+ * section 7.2.1 of the HDMI 1.4a specification.
+ */
+ switch (sample_freq) {
+ case 32000:
+ case 48000:
+ case 96000:
+ case 192000:
+ if (deep_color = 125)
+ if (pclk = 27027 || pclk = 74250)
+ deep_color_correct = true;
+ if (deep_color = 150)
+ if (pclk = 27027)
+ deep_color_correct = true;
+ break;
+ case 44100:
+ case 88200:
+ case 176400:
+ if (deep_color = 125)
+ if (pclk = 27027)
+ deep_color_correct = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (deep_color_correct) {
+ switch (sample_freq) {
+ case 32000:
+ *n = 8192;
+ break;
+ case 44100:
+ *n = 12544;
+ break;
+ case 48000:
+ *n = 8192;
+ break;
+ case 88200:
+ *n = 25088;
+ break;
+ case 96000:
+ *n = 16384;
+ break;
+ case 176400:
+ *n = 50176;
+ break;
+ case 192000:
+ *n = 32768;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ switch (sample_freq) {
+ case 32000:
+ *n = 4096;
+ break;
+ case 44100:
+ *n = 6272;
+ break;
+ case 48000:
+ *n = 6144;
+ break;
+ case 88200:
+ *n = 12544;
+ break;
+ case 96000:
+ *n = 12288;
+ break;
+ case 176400:
+ *n = 25088;
+ break;
+ case 192000:
+ *n = 24576;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ /* Calculate CTS. See HDMI 1.3a or 1.4a specifications */
+ *cts = pclk * (*n / 128) * deep_color / (sample_freq / 10);
+
+ return 0;
+}
+#endif
diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h
index 5034080..cd4237c 100644
--- a/drivers/video/omap2/dss/ti_hdmi.h
+++ b/drivers/video/omap2/dss/ti_hdmi.h
@@ -425,13 +425,22 @@ void hdmi_phy_disable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp);
void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s);
int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy);
+/* HDMI common funcs */
+const struct hdmi_config *hdmi_default_timing(void);
+const struct hdmi_config *hdmi_get_timings(int mode, int code);
+struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing);
+
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
-int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts);
+int hdmi_compute_acr(u32 pclk, u32 sample_freq, u32 *n, u32 *cts);
int hdmi_wp_audio_enable(struct hdmi_wp_data *wp, bool enable);
int hdmi_wp_audio_core_req_enable(struct hdmi_wp_data *wp, bool enable);
void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp,
struct hdmi_audio_format *aud_fmt);
void hdmi_wp_audio_config_dma(struct hdmi_wp_data *wp,
struct hdmi_audio_dma *aud_dma);
+static inline bool hdmi_mode_has_audio(int mode)
+{
+ return mode = HDMI_HDMI ? true : false;
+}
#endif
#endif
--
1.8.1.2
^ permalink raw reply related
* [PATCH 09/11] omapdss: OMAP4: HDMI: remove unnecessary edid macros
From: Archit Taneja @ 2013-09-17 7:18 UTC (permalink / raw)
To: tomi.valkeinen; +Cc: linux-omap, linux-fbdev, Archit Taneja
In-Reply-To: <1379401597-27222-1-git-send-email-archit@ti.com>
The hdmi4 driver has edid macros that aren't used at all. Remove them.
Signed-off-by: Archit Taneja <archit@ti.com>
---
drivers/video/omap2/dss/hdmi4.c | 8 --------
1 file changed, 8 deletions(-)
diff --git a/drivers/video/omap2/dss/hdmi4.c b/drivers/video/omap2/dss/hdmi4.c
index 324ecd0..ab43069 100644
--- a/drivers/video/omap2/dss/hdmi4.c
+++ b/drivers/video/omap2/dss/hdmi4.c
@@ -38,14 +38,6 @@
#include "dss.h"
#include "dss_features.h"
-/* HDMI EDID Length move this */
-#define HDMI_EDID_MAX_LENGTH 256
-#define EDID_TIMING_DESCRIPTOR_SIZE 0x12
-#define EDID_DESCRIPTOR_BLOCK0_ADDRESS 0x36
-#define EDID_DESCRIPTOR_BLOCK1_ADDRESS 0x80
-#define EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR 4
-#define EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR 4
-
static struct {
struct mutex lock;
struct platform_device *pdev;
--
1.8.1.2
^ permalink raw reply related
* [PATCH 07/11] omapdss: HDMI: add HDMI wrapper IRQ flags
From: Archit Taneja @ 2013-09-17 7:18 UTC (permalink / raw)
To: tomi.valkeinen; +Cc: linux-omap, linux-fbdev, Archit Taneja
In-Reply-To: <1379401597-27222-1-git-send-email-archit@ti.com>
Add flags for the interrupts present in HDMI wrapper block, these will be used
to configure HDMI_IRQENABLE_SET/CLEAR and HDMI_IRQSTATUS registers.
Signed-off-by: Archit Taneja <archit@ti.com>
---
drivers/video/omap2/dss/hdmi_phy.c | 3 ---
drivers/video/omap2/dss/ti_hdmi.h | 15 +++++++++++++++
2 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/drivers/video/omap2/dss/hdmi_phy.c b/drivers/video/omap2/dss/hdmi_phy.c
index b79c56b..b7d171c 100644
--- a/drivers/video/omap2/dss/hdmi_phy.c
+++ b/drivers/video/omap2/dss/hdmi_phy.c
@@ -17,9 +17,6 @@
#include "dss.h"
#include "ti_hdmi.h"
-#define HDMI_IRQ_LINK_CONNECT (1 << 25)
-#define HDMI_IRQ_LINK_DISCONNECT (1 << 26)
-
void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s)
{
#define DUMPPHY(r) seq_printf(s, "%-35s %08x\n", #r,\
diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h
index cf096fd..5034080 100644
--- a/drivers/video/omap2/dss/ti_hdmi.h
+++ b/drivers/video/omap2/dss/ti_hdmi.h
@@ -49,6 +49,21 @@
#define HDMI_WP_AUDIO_CTRL 0x88
#define HDMI_WP_AUDIO_DATA 0x8C
+/* HDMI WP IRQ flags */
+
+#define HDMI_IRQ_OCP_TIMEOUT (1 << 4)
+#define HDMI_IRQ_AUDIO_FIFO_UNDERFLOW (1 << 8)
+#define HDMI_IRQ_AUDIO_FIFO_OVERFLOW (1 << 9)
+#define HDMI_IRQ_AUDIO_FIFO_SAMPLE_REQ (1 << 10)
+#define HDMI_IRQ_VIDEO_VSYNC (1 << 16)
+#define HDMI_IRQ_VIDEO_FRAME_DONE (1 << 17)
+#define HDMI_IRQ_PHY_LINE5V_ASSERT (1 << 24)
+#define HDMI_IRQ_LINK_CONNECT (1 << 25)
+#define HDMI_IRQ_LINK_DISCONNECT (1 << 26)
+#define HDMI_IRQ_PLL_LOCK (1 << 29)
+#define HDMI_IRQ_PLL_UNLOCK (1 << 30)
+#define HDMI_IRQ_PLL_RECAL (1 << 31)
+
/* HDMI PLL */
#define PLLCTRL_PLL_CONTROL 0x0
--
1.8.1.2
^ permalink raw reply related
* [PATCH 06/11] omapdss: HDMI: Clean up the header files
From: Archit Taneja @ 2013-09-17 7:18 UTC (permalink / raw)
To: tomi.valkeinen; +Cc: linux-omap, linux-fbdev, Archit Taneja
In-Reply-To: <1379401597-27222-1-git-send-email-archit@ti.com>
Keep only OMAP4 HDMI core block related structs and enums in ti_hdmi_4xxx_ip.h,
move the rest to ti_hdmi.h. This holds all library specific data which will be
shared between OMAP4 and OMAP5/DRA7x HDMI encoder drivers.
Move the duplicate register read/write/wait_for_bit_change functions in the hdmi
library files to ti_hdmi.h
Signed-off-by: Archit Taneja <archit@ti.com>
---
drivers/video/omap2/dss/hdmi_phy.c | 31 ------
drivers/video/omap2/dss/hdmi_pll.c | 31 ------
drivers/video/omap2/dss/hdmi_wp.c | 31 ------
drivers/video/omap2/dss/ti_hdmi.h | 170 ++++++++++++++++++++++++++++++
drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 31 ------
drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h | 141 +------------------------
6 files changed, 171 insertions(+), 264 deletions(-)
diff --git a/drivers/video/omap2/dss/hdmi_phy.c b/drivers/video/omap2/dss/hdmi_phy.c
index cacd3c4..b79c56b 100644
--- a/drivers/video/omap2/dss/hdmi_phy.c
+++ b/drivers/video/omap2/dss/hdmi_phy.c
@@ -9,7 +9,6 @@
*/
#include <linux/kernel.h>
-#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/platform_device.h>
@@ -17,40 +16,10 @@
#include "dss.h"
#include "ti_hdmi.h"
-#include "ti_hdmi_4xxx_ip.h"
#define HDMI_IRQ_LINK_CONNECT (1 << 25)
#define HDMI_IRQ_LINK_DISCONNECT (1 << 26)
-static inline void hdmi_write_reg(void __iomem *base_addr, const u16 idx,
- u32 val)
-{
- __raw_writel(val, base_addr + idx);
-}
-
-static inline u32 hdmi_read_reg(void __iomem *base_addr, const u16 idx)
-{
- return __raw_readl(base_addr + idx);
-}
-
-#define REG_FLD_MOD(base, idx, val, start, end) \
- hdmi_write_reg(base, idx, FLD_MOD(hdmi_read_reg(base, idx),\
- val, start, end))
-#define REG_GET(base, idx, start, end) \
- FLD_GET(hdmi_read_reg(base, idx), start, end)
-
-static inline int hdmi_wait_for_bit_change(void __iomem *base_addr,
- const u16 idx, int b2, int b1, u32 val)
-{
- u32 t = 0;
- while (val != REG_GET(base_addr, idx, b2, b1)) {
- udelay(1);
- if (t++ > 10000)
- return !val;
- }
- return val;
-}
-
void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s)
{
#define DUMPPHY(r) seq_printf(s, "%-35s %08x\n", #r,\
diff --git a/drivers/video/omap2/dss/hdmi_pll.c b/drivers/video/omap2/dss/hdmi_pll.c
index d53b8e2..cddba76 100644
--- a/drivers/video/omap2/dss/hdmi_pll.c
+++ b/drivers/video/omap2/dss/hdmi_pll.c
@@ -10,7 +10,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/platform_device.h>
@@ -18,40 +17,10 @@
#include "dss.h"
#include "ti_hdmi.h"
-#include "ti_hdmi_4xxx_ip.h"
#define HDMI_DEFAULT_REGN 16
#define HDMI_DEFAULT_REGM2 1
-static inline void hdmi_write_reg(void __iomem *base_addr, const u16 idx,
- u32 val)
-{
- __raw_writel(val, base_addr + idx);
-}
-
-static inline u32 hdmi_read_reg(void __iomem *base_addr, const u16 idx)
-{
- return __raw_readl(base_addr + idx);
-}
-
-#define REG_FLD_MOD(base, idx, val, start, end) \
- hdmi_write_reg(base, idx, FLD_MOD(hdmi_read_reg(base, idx),\
- val, start, end))
-#define REG_GET(base, idx, start, end) \
- FLD_GET(hdmi_read_reg(base, idx), start, end)
-
-static inline int hdmi_wait_for_bit_change(void __iomem *base_addr,
- const u16 idx, int b2, int b1, u32 val)
-{
- u32 t = 0;
- while (val != REG_GET(base_addr, idx, b2, b1)) {
- udelay(1);
- if (t++ > 10000)
- return !val;
- }
- return val;
-}
-
void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s)
{
#define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\
diff --git a/drivers/video/omap2/dss/hdmi_wp.c b/drivers/video/omap2/dss/hdmi_wp.c
index 482940a..7c7f84d 100644
--- a/drivers/video/omap2/dss/hdmi_wp.c
+++ b/drivers/video/omap2/dss/hdmi_wp.c
@@ -9,7 +9,6 @@
*/
#include <linux/kernel.h>
-#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/platform_device.h>
@@ -17,36 +16,6 @@
#include "dss.h"
#include "ti_hdmi.h"
-#include "ti_hdmi_4xxx_ip.h"
-
-static inline void hdmi_write_reg(void __iomem *base_addr, const u16 idx,
- u32 val)
-{
- __raw_writel(val, base_addr + idx);
-}
-
-static inline u32 hdmi_read_reg(void __iomem *base_addr, const u16 idx)
-{
- return __raw_readl(base_addr + idx);
-}
-
-#define REG_FLD_MOD(base, idx, val, start, end) \
- hdmi_write_reg(base, idx, FLD_MOD(hdmi_read_reg(base, idx),\
- val, start, end))
-#define REG_GET(base, idx, start, end) \
- FLD_GET(hdmi_read_reg(base, idx), start, end)
-
-static inline int hdmi_wait_for_bit_change(void __iomem *base_addr,
- const u16 idx, int b2, int b1, u32 val)
-{
- u32 t = 0;
- while (val != REG_GET(base_addr, idx, b2, b1)) {
- udelay(1);
- if (t++ > 10000)
- return !val;
- }
- return val;
-}
void hdmi_wp_dump(struct hdmi_wp_data *wp, struct seq_file *s)
{
diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h
index 107a606..cf096fd 100644
--- a/drivers/video/omap2/dss/ti_hdmi.h
+++ b/drivers/video/omap2/dss/ti_hdmi.h
@@ -21,7 +21,52 @@
#ifndef _TI_HDMI_H
#define _TI_HDMI_H
+#include <linux/delay.h>
+#include <linux/io.h>
#include <linux/platform_device.h>
+#include <video/omapdss.h>
+
+#include "dss.h"
+
+/* HDMI Wrapper */
+
+#define HDMI_WP_REVISION 0x0
+#define HDMI_WP_SYSCONFIG 0x10
+#define HDMI_WP_IRQSTATUS_RAW 0x24
+#define HDMI_WP_IRQSTATUS 0x28
+#define HDMI_WP_IRQENABLE_SET 0x2C
+#define HDMI_WP_IRQENABLE_CLR 0x30
+#define HDMI_WP_IRQWAKEEN 0x34
+#define HDMI_WP_PWR_CTRL 0x40
+#define HDMI_WP_DEBOUNCE 0x44
+#define HDMI_WP_VIDEO_CFG 0x50
+#define HDMI_WP_VIDEO_SIZE 0x60
+#define HDMI_WP_VIDEO_TIMING_H 0x68
+#define HDMI_WP_VIDEO_TIMING_V 0x6C
+#define HDMI_WP_WP_CLK 0x70
+#define HDMI_WP_AUDIO_CFG 0x80
+#define HDMI_WP_AUDIO_CFG2 0x84
+#define HDMI_WP_AUDIO_CTRL 0x88
+#define HDMI_WP_AUDIO_DATA 0x8C
+
+/* HDMI PLL */
+
+#define PLLCTRL_PLL_CONTROL 0x0
+#define PLLCTRL_PLL_STATUS 0x4
+#define PLLCTRL_PLL_GO 0x8
+#define PLLCTRL_CFG1 0xC
+#define PLLCTRL_CFG2 0x10
+#define PLLCTRL_CFG3 0x14
+#define PLLCTRL_SSC_CFG1 0x18
+#define PLLCTRL_SSC_CFG2 0x1C
+#define PLLCTRL_CFG4 0x20
+
+/* HDMI PHY */
+
+#define HDMI_TXPHY_TX_CTRL 0x0
+#define HDMI_TXPHY_DIGITAL_CTRL 0x4
+#define HDMI_TXPHY_POWER_CTRL 0x8
+#define HDMI_TXPHY_PAD_CFG_CTRL 0xC
enum hdmi_pll_pwr {
HDMI_PLLPWRCMD_ALLOFF = 0,
@@ -98,6 +143,75 @@ enum hdmi_audio_blk_strt_end_sig {
HDMI_AUDIO_BLOCK_SIG_STARTEND_OFF = 1
};
+enum hdmi_core_audio_layout {
+ HDMI_AUDIO_LAYOUT_2CH = 0,
+ HDMI_AUDIO_LAYOUT_8CH = 1
+};
+
+enum hdmi_core_cts_mode {
+ HDMI_AUDIO_CTS_MODE_HW = 0,
+ HDMI_AUDIO_CTS_MODE_SW = 1
+};
+
+enum hdmi_audio_mclk_mode {
+ HDMI_AUDIO_MCLK_128FS = 0,
+ HDMI_AUDIO_MCLK_256FS = 1,
+ HDMI_AUDIO_MCLK_384FS = 2,
+ HDMI_AUDIO_MCLK_512FS = 3,
+ HDMI_AUDIO_MCLK_768FS = 4,
+ HDMI_AUDIO_MCLK_1024FS = 5,
+ HDMI_AUDIO_MCLK_1152FS = 6,
+ HDMI_AUDIO_MCLK_192FS = 7
+};
+
+/* INFOFRAME_AVI_ and INFOFRAME_AUDIO_ definitions */
+enum hdmi_core_infoframe {
+ HDMI_INFOFRAME_AVI_DB1Y_RGB = 0,
+ HDMI_INFOFRAME_AVI_DB1Y_YUV422 = 1,
+ HDMI_INFOFRAME_AVI_DB1Y_YUV444 = 2,
+ HDMI_INFOFRAME_AVI_DB1A_ACTIVE_FORMAT_OFF = 0,
+ HDMI_INFOFRAME_AVI_DB1A_ACTIVE_FORMAT_ON = 1,
+ HDMI_INFOFRAME_AVI_DB1B_NO = 0,
+ HDMI_INFOFRAME_AVI_DB1B_VERT = 1,
+ HDMI_INFOFRAME_AVI_DB1B_HORI = 2,
+ HDMI_INFOFRAME_AVI_DB1B_VERTHORI = 3,
+ HDMI_INFOFRAME_AVI_DB1S_0 = 0,
+ HDMI_INFOFRAME_AVI_DB1S_1 = 1,
+ HDMI_INFOFRAME_AVI_DB1S_2 = 2,
+ HDMI_INFOFRAME_AVI_DB2C_NO = 0,
+ HDMI_INFOFRAME_AVI_DB2C_ITU601 = 1,
+ HDMI_INFOFRAME_AVI_DB2C_ITU709 = 2,
+ HDMI_INFOFRAME_AVI_DB2C_EC_EXTENDED = 3,
+ HDMI_INFOFRAME_AVI_DB2M_NO = 0,
+ HDMI_INFOFRAME_AVI_DB2M_43 = 1,
+ HDMI_INFOFRAME_AVI_DB2M_169 = 2,
+ HDMI_INFOFRAME_AVI_DB2R_SAME = 8,
+ HDMI_INFOFRAME_AVI_DB2R_43 = 9,
+ HDMI_INFOFRAME_AVI_DB2R_169 = 10,
+ HDMI_INFOFRAME_AVI_DB2R_149 = 11,
+ HDMI_INFOFRAME_AVI_DB3ITC_NO = 0,
+ HDMI_INFOFRAME_AVI_DB3ITC_YES = 1,
+ HDMI_INFOFRAME_AVI_DB3EC_XVYUV601 = 0,
+ HDMI_INFOFRAME_AVI_DB3EC_XVYUV709 = 1,
+ HDMI_INFOFRAME_AVI_DB3Q_DEFAULT = 0,
+ HDMI_INFOFRAME_AVI_DB3Q_LR = 1,
+ HDMI_INFOFRAME_AVI_DB3Q_FR = 2,
+ HDMI_INFOFRAME_AVI_DB3SC_NO = 0,
+ HDMI_INFOFRAME_AVI_DB3SC_HORI = 1,
+ HDMI_INFOFRAME_AVI_DB3SC_VERT = 2,
+ HDMI_INFOFRAME_AVI_DB3SC_HORIVERT = 3,
+ HDMI_INFOFRAME_AVI_DB5PR_NO = 0,
+ HDMI_INFOFRAME_AVI_DB5PR_2 = 1,
+ HDMI_INFOFRAME_AVI_DB5PR_3 = 2,
+ HDMI_INFOFRAME_AVI_DB5PR_4 = 3,
+ HDMI_INFOFRAME_AVI_DB5PR_5 = 4,
+ HDMI_INFOFRAME_AVI_DB5PR_6 = 5,
+ HDMI_INFOFRAME_AVI_DB5PR_7 = 6,
+ HDMI_INFOFRAME_AVI_DB5PR_8 = 7,
+ HDMI_INFOFRAME_AVI_DB5PR_9 = 8,
+ HDMI_INFOFRAME_AVI_DB5PR_10 = 9,
+};
+
struct hdmi_cm {
int code;
int mode;
@@ -143,6 +257,33 @@ struct hdmi_audio_dma {
u16 fifo_threshold;
};
+struct hdmi_core_audio_i2s_config {
+ u8 in_length_bits;
+ u8 justification;
+ u8 sck_edge_mode;
+ u8 vbit;
+ u8 direction;
+ u8 shift;
+ u8 active_sds;
+};
+
+struct hdmi_core_audio_config {
+ struct hdmi_core_audio_i2s_config i2s_cfg;
+ struct snd_aes_iec958 *iec60958_cfg;
+ bool fs_override;
+ u32 n;
+ u32 cts;
+ u32 aud_par_busclk;
+ enum hdmi_core_audio_layout layout;
+ enum hdmi_core_cts_mode cts_mode;
+ bool use_mclk;
+ enum hdmi_audio_mclk_mode mclk_mode;
+ bool en_acr_pkt;
+ bool en_dsd_audio;
+ bool en_parallel_aud_input;
+ bool en_spdif;
+};
+
/*
* Refer to section 8.2 in HDMI 1.3 specification for
* details about infoframe databytes
@@ -206,6 +347,35 @@ struct hdmi_core_data {
struct hdmi_core_infoframe_avi avi_cfg;
};
+static inline void hdmi_write_reg(void __iomem *base_addr, const u16 idx,
+ u32 val)
+{
+ __raw_writel(val, base_addr + idx);
+}
+
+static inline u32 hdmi_read_reg(void __iomem *base_addr, const u16 idx)
+{
+ return __raw_readl(base_addr + idx);
+}
+
+#define REG_FLD_MOD(base, idx, val, start, end) \
+ hdmi_write_reg(base, idx, FLD_MOD(hdmi_read_reg(base, idx),\
+ val, start, end))
+#define REG_GET(base, idx, start, end) \
+ FLD_GET(hdmi_read_reg(base, idx), start, end)
+
+static inline int hdmi_wait_for_bit_change(void __iomem *base_addr,
+ const u16 idx, int b2, int b1, u32 val)
+{
+ u32 t = 0;
+ while (val != REG_GET(base_addr, idx, b2, b1)) {
+ udelay(1);
+ if (t++ > 10000)
+ return !val;
+ }
+ return val;
+}
+
/* HDMI wrapper funcs */
int hdmi_wp_video_start(struct hdmi_wp_data *wp);
void hdmi_wp_video_stop(struct hdmi_wp_data *wp);
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
index ca208d8..25b39ac 100644
--- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
+++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
@@ -35,41 +35,10 @@
#endif
#include "ti_hdmi_4xxx_ip.h"
-#include "dss.h"
#include "dss_features.h"
#define HDMI_CORE_AV 0x500
-static inline void hdmi_write_reg(void __iomem *base_addr,
- const u16 idx, u32 val)
-{
- __raw_writel(val, base_addr + idx);
-}
-
-static inline u32 hdmi_read_reg(void __iomem *base_addr,
- const u16 idx)
-{
- return __raw_readl(base_addr + idx);
-}
-
-#define REG_FLD_MOD(base, idx, val, start, end) \
- hdmi_write_reg(base, idx, FLD_MOD(hdmi_read_reg(base, idx),\
- val, start, end))
-#define REG_GET(base, idx, start, end) \
- FLD_GET(hdmi_read_reg(base, idx), start, end)
-
-static inline int hdmi_wait_for_bit_change(void __iomem *base_addr,
- const u16 idx, int b2, int b1, u32 val)
-{
- u32 t = 0;
- while (val != REG_GET(base_addr, idx, b2, b1)) {
- udelay(1);
- if (t++ > 10000)
- return !val;
- }
- return val;
-}
-
static inline void __iomem *hdmi_av_base(struct hdmi_core_data *core)
{
return core->base + HDMI_CORE_AV;
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h
index b9bb300..78319ff 100644
--- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h
+++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h
@@ -21,32 +21,9 @@
#ifndef _HDMI_TI_4xxx_H_
#define _HDMI_TI_4xxx_H_
-#include <linux/string.h>
-#include <video/omapdss.h>
#include "ti_hdmi.h"
-/* HDMI Wrapper */
-
-#define HDMI_WP_REVISION 0x0
-#define HDMI_WP_SYSCONFIG 0x10
-#define HDMI_WP_IRQSTATUS_RAW 0x24
-#define HDMI_WP_IRQSTATUS 0x28
-#define HDMI_WP_IRQENABLE_SET 0x2C
-#define HDMI_WP_IRQENABLE_CLR 0x30
-#define HDMI_WP_IRQWAKEEN 0x34
-#define HDMI_WP_PWR_CTRL 0x40
-#define HDMI_WP_DEBOUNCE 0x44
-#define HDMI_WP_VIDEO_CFG 0x50
-#define HDMI_WP_VIDEO_SIZE 0x60
-#define HDMI_WP_VIDEO_TIMING_H 0x68
-#define HDMI_WP_VIDEO_TIMING_V 0x6C
-#define HDMI_WP_WP_CLK 0x70
-#define HDMI_WP_AUDIO_CFG 0x80
-#define HDMI_WP_AUDIO_CFG2 0x84
-#define HDMI_WP_AUDIO_CTRL 0x88
-#define HDMI_WP_AUDIO_DATA 0x8C
-
-/* HDMI IP Core System */
+/* OMAP4 HDMI IP Core System */
#define HDMI_CORE_SYS_VND_IDL 0x0
#define HDMI_CORE_SYS_DEV_IDL 0x8
@@ -207,25 +184,6 @@
#define HDMI_CORE_AV_GEN_DBYTE_NELEMS 31
#define HDMI_CORE_AV_GEN2_DBYTE_NELEMS 31
-/* PLL */
-
-#define PLLCTRL_PLL_CONTROL 0x0
-#define PLLCTRL_PLL_STATUS 0x4
-#define PLLCTRL_PLL_GO 0x8
-#define PLLCTRL_CFG1 0xC
-#define PLLCTRL_CFG2 0x10
-#define PLLCTRL_CFG3 0x14
-#define PLLCTRL_SSC_CFG1 0x18
-#define PLLCTRL_SSC_CFG2 0x1C
-#define PLLCTRL_CFG4 0x20
-
-/* HDMI PHY */
-
-#define HDMI_TXPHY_TX_CTRL 0x0
-#define HDMI_TXPHY_DIGITAL_CTRL 0x4
-#define HDMI_TXPHY_POWER_CTRL 0x8
-#define HDMI_TXPHY_PAD_CFG_CTRL 0xC
-
enum hdmi_core_inputbus_width {
HDMI_INPUT_8BIT = 0,
HDMI_INPUT_10BIT = 1,
@@ -268,64 +226,6 @@ enum hdmi_core_packet_ctrl {
HDMI_PACKETREPEATOFF = 0
};
-/* INFOFRAME_AVI_ and INFOFRAME_AUDIO_ definitions */
-enum hdmi_core_infoframe {
- HDMI_INFOFRAME_AVI_DB1Y_RGB = 0,
- HDMI_INFOFRAME_AVI_DB1Y_YUV422 = 1,
- HDMI_INFOFRAME_AVI_DB1Y_YUV444 = 2,
- HDMI_INFOFRAME_AVI_DB1A_ACTIVE_FORMAT_OFF = 0,
- HDMI_INFOFRAME_AVI_DB1A_ACTIVE_FORMAT_ON = 1,
- HDMI_INFOFRAME_AVI_DB1B_NO = 0,
- HDMI_INFOFRAME_AVI_DB1B_VERT = 1,
- HDMI_INFOFRAME_AVI_DB1B_HORI = 2,
- HDMI_INFOFRAME_AVI_DB1B_VERTHORI = 3,
- HDMI_INFOFRAME_AVI_DB1S_0 = 0,
- HDMI_INFOFRAME_AVI_DB1S_1 = 1,
- HDMI_INFOFRAME_AVI_DB1S_2 = 2,
- HDMI_INFOFRAME_AVI_DB2C_NO = 0,
- HDMI_INFOFRAME_AVI_DB2C_ITU601 = 1,
- HDMI_INFOFRAME_AVI_DB2C_ITU709 = 2,
- HDMI_INFOFRAME_AVI_DB2C_EC_EXTENDED = 3,
- HDMI_INFOFRAME_AVI_DB2M_NO = 0,
- HDMI_INFOFRAME_AVI_DB2M_43 = 1,
- HDMI_INFOFRAME_AVI_DB2M_169 = 2,
- HDMI_INFOFRAME_AVI_DB2R_SAME = 8,
- HDMI_INFOFRAME_AVI_DB2R_43 = 9,
- HDMI_INFOFRAME_AVI_DB2R_169 = 10,
- HDMI_INFOFRAME_AVI_DB2R_149 = 11,
- HDMI_INFOFRAME_AVI_DB3ITC_NO = 0,
- HDMI_INFOFRAME_AVI_DB3ITC_YES = 1,
- HDMI_INFOFRAME_AVI_DB3EC_XVYUV601 = 0,
- HDMI_INFOFRAME_AVI_DB3EC_XVYUV709 = 1,
- HDMI_INFOFRAME_AVI_DB3Q_DEFAULT = 0,
- HDMI_INFOFRAME_AVI_DB3Q_LR = 1,
- HDMI_INFOFRAME_AVI_DB3Q_FR = 2,
- HDMI_INFOFRAME_AVI_DB3SC_NO = 0,
- HDMI_INFOFRAME_AVI_DB3SC_HORI = 1,
- HDMI_INFOFRAME_AVI_DB3SC_VERT = 2,
- HDMI_INFOFRAME_AVI_DB3SC_HORIVERT = 3,
- HDMI_INFOFRAME_AVI_DB5PR_NO = 0,
- HDMI_INFOFRAME_AVI_DB5PR_2 = 1,
- HDMI_INFOFRAME_AVI_DB5PR_3 = 2,
- HDMI_INFOFRAME_AVI_DB5PR_4 = 3,
- HDMI_INFOFRAME_AVI_DB5PR_5 = 4,
- HDMI_INFOFRAME_AVI_DB5PR_6 = 5,
- HDMI_INFOFRAME_AVI_DB5PR_7 = 6,
- HDMI_INFOFRAME_AVI_DB5PR_8 = 7,
- HDMI_INFOFRAME_AVI_DB5PR_9 = 8,
- HDMI_INFOFRAME_AVI_DB5PR_10 = 9,
-};
-
-enum hdmi_core_audio_layout {
- HDMI_AUDIO_LAYOUT_2CH = 0,
- HDMI_AUDIO_LAYOUT_8CH = 1
-};
-
-enum hdmi_core_cts_mode {
- HDMI_AUDIO_CTS_MODE_HW = 0,
- HDMI_AUDIO_CTS_MODE_SW = 1
-};
-
enum hdmi_audio_i2s_config {
HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST = 0,
HDMI_AUDIO_I2S_LSB_SHIFTED_FIRST = 1,
@@ -341,17 +241,6 @@ enum hdmi_audio_i2s_config {
HDMI_AUDIO_I2S_SD3_EN = 1 << 3,
};
-enum hdmi_audio_mclk_mode {
- HDMI_AUDIO_MCLK_128FS = 0,
- HDMI_AUDIO_MCLK_256FS = 1,
- HDMI_AUDIO_MCLK_384FS = 2,
- HDMI_AUDIO_MCLK_512FS = 3,
- HDMI_AUDIO_MCLK_768FS = 4,
- HDMI_AUDIO_MCLK_1024FS = 5,
- HDMI_AUDIO_MCLK_1152FS = 6,
- HDMI_AUDIO_MCLK_192FS = 7
-};
-
struct hdmi_core_video_config {
enum hdmi_core_inputbus_width ip_bus_width;
enum hdmi_core_dither_trunc op_dither_truc;
@@ -372,34 +261,6 @@ struct hdmi_core_packet_enable_repeat {
u32 generic_pkt_repeat;
};
-
-struct hdmi_core_audio_i2s_config {
- u8 in_length_bits;
- u8 justification;
- u8 sck_edge_mode;
- u8 vbit;
- u8 direction;
- u8 shift;
- u8 active_sds;
-};
-
-struct hdmi_core_audio_config {
- struct hdmi_core_audio_i2s_config i2s_cfg;
- struct snd_aes_iec958 *iec60958_cfg;
- bool fs_override;
- u32 n;
- u32 cts;
- u32 aud_par_busclk;
- enum hdmi_core_audio_layout layout;
- enum hdmi_core_cts_mode cts_mode;
- bool use_mclk;
- enum hdmi_audio_mclk_mode mclk_mode;
- bool en_acr_pkt;
- bool en_dsd_audio;
- bool en_parallel_aud_input;
- bool en_spdif;
-};
-
int hdmi4_read_edid(struct hdmi_core_data *core, u8 *edid, int len);
void hdmi4_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
struct hdmi_config *cfg);
--
1.8.1.2
^ permalink raw reply related
* [PATCH 05/11] omapdss: HDMI: remove hdmi_ip_data struct
From: Archit Taneja @ 2013-09-17 7:18 UTC (permalink / raw)
To: tomi.valkeinen; +Cc: linux-omap, linux-fbdev, Archit Taneja
In-Reply-To: <1379401597-27222-1-git-send-email-archit@ti.com>
The struct hdmi_ip_data contains information related to HDMI wrapper, PLL, PHY
and core sub-blocks. Now that each of these sub blocks has it's own struct,
hdmi_ip_data serves no purpose. The mutex lock in the struct was also never
used.
Remove this struct to make things cleaner.
Signed-off-by: Archit Taneja <archit@ti.com>
---
drivers/video/omap2/dss/hdmi.c | 91 ++++++++++++++++++++-------------------
drivers/video/omap2/dss/ti_hdmi.h | 14 ------
2 files changed, 47 insertions(+), 58 deletions(-)
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
index 0e9ecd6..f03d6c7 100644
--- a/drivers/video/omap2/dss/hdmi.c
+++ b/drivers/video/omap2/dss/hdmi.c
@@ -53,7 +53,12 @@ static struct {
struct mutex lock;
struct platform_device *pdev;
- struct hdmi_ip_data ip_data;
+ struct hdmi_wp_data wp;
+ struct hdmi_pll_data pll;
+ struct hdmi_phy_data phy;
+ struct hdmi_core_data core;
+
+ struct hdmi_config cfg;
struct clk *sys_clk;
struct regulator *vdda_hdmi_dac_reg;
@@ -348,7 +353,7 @@ static const struct hdmi_config *hdmi_find_timing(
int i;
for (i = 0; i < len; i++) {
- if (timings_arr[i].cm.code = hdmi.ip_data.cfg.cm.code)
+ if (timings_arr[i].cm.code = hdmi.cfg.cm.code)
return &timings_arr[i];
}
return NULL;
@@ -359,15 +364,15 @@ static const struct hdmi_config *hdmi_get_timings(void)
const struct hdmi_config *arr;
int len;
- if (hdmi.ip_data.cfg.cm.mode = HDMI_DVI) {
- arr = vesa_timings;
- len = ARRAY_SIZE(vesa_timings);
- } else {
- arr = cea_timings;
- len = ARRAY_SIZE(cea_timings);
- }
+ if (hdmi.cfg.cm.mode = HDMI_DVI) {
+ arr = vesa_timings;
+ len = ARRAY_SIZE(vesa_timings);
+ } else {
+ arr = cea_timings;
+ len = ARRAY_SIZE(cea_timings);
+ }
- return hdmi_find_timing(arr, len);
+ return hdmi_find_timing(arr, len);
}
static bool hdmi_timings_compare(struct omap_video_timings *timing1,
@@ -467,32 +472,30 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
dss_mgr_disable(mgr);
- p = &hdmi.ip_data.cfg.timings;
+ p = &hdmi.cfg.timings;
DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res);
phy = p->pixel_clock;
- hdmi_pll_compute(&hdmi.ip_data.pll, clk_get_rate(hdmi.sys_clk), phy);
+ hdmi_pll_compute(&hdmi.pll, clk_get_rate(hdmi.sys_clk), phy);
- hdmi_wp_video_stop(&hdmi.ip_data.wp);
+ hdmi_wp_video_stop(&hdmi.wp);
/* config the PLL and PHY hdmi_set_pll_pwrfirst */
- r = hdmi_pll_enable(&hdmi.ip_data.pll, &hdmi.ip_data.wp);
+ r = hdmi_pll_enable(&hdmi.pll, &hdmi.wp);
if (r) {
DSSDBG("Failed to lock PLL\n");
goto err_pll_enable;
}
- r = hdmi_phy_enable(&hdmi.ip_data.phy, &hdmi.ip_data.wp,
- &hdmi.ip_data.cfg);
+ r = hdmi_phy_enable(&hdmi.phy, &hdmi.wp, &hdmi.cfg);
if (r) {
DSSDBG("Failed to start PHY\n");
goto err_phy_enable;
}
- hdmi4_configure(&hdmi.ip_data.core, &hdmi.ip_data.wp,
- &hdmi.ip_data.cfg);
+ hdmi4_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg);
/* bypass TV gamma table */
dispc_enable_gamma_table(0);
@@ -500,7 +503,7 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
/* tv size */
dss_mgr_set_timings(mgr, p);
- r = hdmi_wp_video_start(&hdmi.ip_data.wp);
+ r = hdmi_wp_video_start(&hdmi.wp);
if (r)
goto err_vid_enable;
@@ -511,11 +514,11 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
return 0;
err_mgr_enable:
- hdmi_wp_video_stop(&hdmi.ip_data.wp);
+ hdmi_wp_video_stop(&hdmi.wp);
err_vid_enable:
- hdmi_phy_disable(&hdmi.ip_data.phy, &hdmi.ip_data.wp);
+ hdmi_phy_disable(&hdmi.phy, &hdmi.wp);
err_phy_enable:
- hdmi_pll_disable(&hdmi.ip_data.pll, &hdmi.ip_data.wp);
+ hdmi_pll_disable(&hdmi.pll, &hdmi.wp);
err_pll_enable:
hdmi_power_off_core(dssdev);
return -EIO;
@@ -527,9 +530,9 @@ static void hdmi_power_off_full(struct omap_dss_device *dssdev)
dss_mgr_disable(mgr);
- hdmi_wp_video_stop(&hdmi.ip_data.wp);
- hdmi_phy_disable(&hdmi.ip_data.phy, &hdmi.ip_data.wp);
- hdmi_pll_disable(&hdmi.ip_data.pll, &hdmi.ip_data.wp);
+ hdmi_wp_video_stop(&hdmi.wp);
+ hdmi_phy_disable(&hdmi.phy, &hdmi.wp);
+ hdmi_pll_disable(&hdmi.pll, &hdmi.wp);
hdmi_power_off_core(dssdev);
}
@@ -557,11 +560,11 @@ static void hdmi_display_set_timing(struct omap_dss_device *dssdev,
mutex_lock(&hdmi.lock);
cm = hdmi_get_code(timings);
- hdmi.ip_data.cfg.cm = cm;
+ hdmi.cfg.cm = cm;
t = hdmi_get_timings();
if (t != NULL) {
- hdmi.ip_data.cfg = *t;
+ hdmi.cfg = *t;
dispc_set_tv_pclk(t->timings.pixel_clock * 1000);
}
@@ -590,10 +593,10 @@ static void hdmi_dump_regs(struct seq_file *s)
return;
}
- hdmi_wp_dump(&hdmi.ip_data.wp, s);
- hdmi_pll_dump(&hdmi.ip_data.pll, s);
- hdmi_phy_dump(&hdmi.ip_data.phy, s);
- hdmi4_core_dump(&hdmi.ip_data.core, s);
+ hdmi_wp_dump(&hdmi.wp, s);
+ hdmi_pll_dump(&hdmi.pll, s);
+ hdmi_phy_dump(&hdmi.phy, s);
+ hdmi4_core_dump(&hdmi.core, s);
hdmi_runtime_put();
mutex_unlock(&hdmi.lock);
@@ -608,7 +611,7 @@ static int read_edid(u8 *buf, int len)
r = hdmi_runtime_get();
BUG_ON(r);
- r = hdmi4_read_edid(&hdmi.ip_data.core, buf, len);
+ r = hdmi4_read_edid(&hdmi.core, buf, len);
hdmi_runtime_put();
mutex_unlock(&hdmi.lock);
@@ -709,7 +712,7 @@ int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts)
{
u32 deep_color;
bool deep_color_correct = false;
- u32 pclk = hdmi.ip_data.cfg.timings.pixel_clock;
+ u32 pclk = hdmi.cfg.timings.pixel_clock;
if (n = NULL || cts = NULL)
return -EINVAL;
@@ -807,11 +810,12 @@ int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts)
static bool hdmi_mode_has_audio(void)
{
- if (hdmi.ip_data.cfg.cm.mode = HDMI_HDMI)
+ if (hdmi.cfg.cm.mode = HDMI_HDMI)
return true;
else
return false;
}
+
#endif
static int hdmi_connect(struct omap_dss_device *dssdev,
@@ -891,7 +895,7 @@ static int hdmi_audio_enable(struct omap_dss_device *dssdev)
goto err;
}
- r = hdmi_wp_audio_enable(&hdmi.ip_data.wp, true);
+ r = hdmi_wp_audio_enable(&hdmi.wp, true);
if (r)
goto err;
@@ -905,17 +909,17 @@ err:
static void hdmi_audio_disable(struct omap_dss_device *dssdev)
{
- hdmi_wp_audio_enable(&hdmi.ip_data.wp, false);
+ hdmi_wp_audio_enable(&hdmi.wp, false);
}
static int hdmi_audio_start(struct omap_dss_device *dssdev)
{
- return hdmi4_audio_start(&hdmi.ip_data.core, &hdmi.ip_data.wp);
+ return hdmi4_audio_start(&hdmi.core, &hdmi.wp);
}
static void hdmi_audio_stop(struct omap_dss_device *dssdev)
{
- hdmi4_audio_stop(&hdmi.ip_data.core, &hdmi.ip_data.wp);
+ hdmi4_audio_stop(&hdmi.core, &hdmi.wp);
}
static bool hdmi_audio_supported(struct omap_dss_device *dssdev)
@@ -942,7 +946,7 @@ static int hdmi_audio_config(struct omap_dss_device *dssdev,
goto err;
}
- r = hdmi4_audio_config(&hdmi.ip_data.core, &hdmi.ip_data.wp, audio);
+ r = hdmi4_audio_config(&hdmi.core, &hdmi.wp, audio);
if (r)
goto err;
@@ -1035,21 +1039,20 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
hdmi.pdev = pdev;
mutex_init(&hdmi.lock);
- mutex_init(&hdmi.ip_data.lock);
- r = hdmi_wp_init(pdev, &hdmi.ip_data.wp);
+ r = hdmi_wp_init(pdev, &hdmi.wp);
if (r)
return r;
- r = hdmi_pll_init(pdev, &hdmi.ip_data.pll);
+ r = hdmi_pll_init(pdev, &hdmi.pll);
if (r)
return r;
- r = hdmi_phy_init(pdev, &hdmi.ip_data.phy);
+ r = hdmi_phy_init(pdev, &hdmi.phy);
if (r)
return r;
- r = hdmi4_core_init(pdev, &hdmi.ip_data.core);
+ r = hdmi4_core_init(pdev, &hdmi.core);
if (r)
return r;
diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h
index 9eb9b32..107a606 100644
--- a/drivers/video/omap2/dss/ti_hdmi.h
+++ b/drivers/video/omap2/dss/ti_hdmi.h
@@ -23,8 +23,6 @@
#include <linux/platform_device.h>
-struct hdmi_ip_data;
-
enum hdmi_pll_pwr {
HDMI_PLLPWRCMD_ALLOFF = 0,
HDMI_PLLPWRCMD_PLLONLY = 1,
@@ -208,18 +206,6 @@ struct hdmi_core_data {
struct hdmi_core_infoframe_avi avi_cfg;
};
-struct hdmi_ip_data {
- struct hdmi_wp_data wp;
- struct hdmi_pll_data pll;
- struct hdmi_phy_data phy;
- struct hdmi_core_data core;
-
- struct hdmi_config cfg;
-
- /* ti_hdmi_4xxx_ip private data. These should be in a separate struct */
- struct mutex lock;
-};
-
/* HDMI wrapper funcs */
int hdmi_wp_video_start(struct hdmi_wp_data *wp);
void hdmi_wp_video_stop(struct hdmi_wp_data *wp);
--
1.8.1.2
^ permalink raw reply related
* [PATCH 04/11] omapdss: HDMI: Use OMAP4 HDMI core functions directly and remove hdmi_ip_ops
From: Archit Taneja @ 2013-09-17 7:18 UTC (permalink / raw)
To: tomi.valkeinen; +Cc: linux-omap, linux-fbdev, Archit Taneja
In-Reply-To: <1379401597-27222-1-git-send-email-archit@ti.com>
After removing wrapper, pll and phy funcs from ti_hdmi_4xxx_ip.c, we are left
with OMAP4 HDMI core functions. Use these directly in hdmi.c rather than using
hdmi_ip_ops. Rename the core functions with a 'hdmi4' suffix.
We used to have hdmi_ip_ops so that one could support HDMI within a TI SoC which
had a non-DSS display subsytem. This however never got put into use, and hence
these ops aren't useful any more.
Signed-off-by: Archit Taneja <archit@ti.com>
---
drivers/video/omap2/dss/dss_features.c | 33 -----
drivers/video/omap2/dss/dss_features.h | 8 --
drivers/video/omap2/dss/hdmi.c | 27 ++--
drivers/video/omap2/dss/ti_hdmi.h | 41 +-----
drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 205 ++++++++++++++++--------------
drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h | 14 ++
6 files changed, 144 insertions(+), 184 deletions(-)
diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c
index 2777eb6..f8fd6db 100644
--- a/drivers/video/omap2/dss/dss_features.c
+++ b/drivers/video/omap2/dss/dss_features.c
@@ -789,39 +789,6 @@ static const struct omap_dss_features omap5_dss_features = {
.burst_size_unit = 16,
};
-#if defined(CONFIG_OMAP4_DSS_HDMI)
-/* HDMI OMAP4 Functions*/
-static const struct ti_hdmi_ip_ops omap4_hdmi_functions = {
-
- .video_configure = ti_hdmi_4xxx_basic_configure,
- .read_edid = ti_hdmi_4xxx_read_edid,
- .dump_core = ti_hdmi_4xxx_core_dump,
-#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
- .audio_start = ti_hdmi_4xxx_audio_start,
- .audio_stop = ti_hdmi_4xxx_audio_stop,
- .audio_config = ti_hdmi_4xxx_audio_config,
- .audio_get_dma_port = ti_hdmi_4xxx_audio_get_dma_port,
-#endif
-
-};
-
-void dss_init_hdmi_ip_ops(struct hdmi_ip_data *ip_data,
- enum omapdss_version version)
-{
- switch (version) {
- case OMAPDSS_VER_OMAP4430_ES1:
- case OMAPDSS_VER_OMAP4430_ES2:
- case OMAPDSS_VER_OMAP4:
- ip_data->ops = &omap4_hdmi_functions;
- break;
- default:
- ip_data->ops = NULL;
- }
-
- WARN_ON(ip_data->ops = NULL);
-}
-#endif
-
/* Functions returning values related to a DSS feature */
int dss_feat_get_num_mgrs(void)
{
diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h
index 489b9be..10b0556 100644
--- a/drivers/video/omap2/dss/dss_features.h
+++ b/drivers/video/omap2/dss/dss_features.h
@@ -20,10 +20,6 @@
#ifndef __OMAP2_DSS_FEATURES_H
#define __OMAP2_DSS_FEATURES_H
-#if defined(CONFIG_OMAP4_DSS_HDMI)
-#include "ti_hdmi.h"
-#endif
-
#define MAX_DSS_MANAGERS 4
#define MAX_DSS_OVERLAYS 4
#define MAX_DSS_LCD_MANAGERS 3
@@ -117,8 +113,4 @@ bool dss_feat_rotation_type_supported(enum omap_dss_rotation_type rot_type);
bool dss_has_feature(enum dss_feat_id id);
void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end);
void dss_features_init(enum omapdss_version version);
-#if defined(CONFIG_OMAP4_DSS_HDMI)
-void dss_init_hdmi_ip_ops(struct hdmi_ip_data *ip_data,
- enum omapdss_version version);
-#endif
#endif
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
index f7e2ac6..0e9ecd6 100644
--- a/drivers/video/omap2/dss/hdmi.c
+++ b/drivers/video/omap2/dss/hdmi.c
@@ -37,12 +37,10 @@
#include <video/omapdss.h>
#include "ti_hdmi.h"
+#include "ti_hdmi_4xxx_ip.h"
#include "dss.h"
#include "dss_features.h"
-#define HDMI_CORE_SYS 0x400
-#define HDMI_CORE_AV 0x900
-
/* HDMI EDID Length move this */
#define HDMI_EDID_MAX_LENGTH 256
#define EDID_TIMING_DESCRIPTOR_SIZE 0x12
@@ -493,7 +491,8 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
goto err_phy_enable;
}
- hdmi.ip_data.ops->video_configure(&hdmi.ip_data);
+ hdmi4_configure(&hdmi.ip_data.core, &hdmi.ip_data.wp,
+ &hdmi.ip_data.cfg);
/* bypass TV gamma table */
dispc_enable_gamma_table(0);
@@ -594,7 +593,7 @@ static void hdmi_dump_regs(struct seq_file *s)
hdmi_wp_dump(&hdmi.ip_data.wp, s);
hdmi_pll_dump(&hdmi.ip_data.pll, s);
hdmi_phy_dump(&hdmi.ip_data.phy, s);
- hdmi.ip_data.ops->dump_core(&hdmi.ip_data, s);
+ hdmi4_core_dump(&hdmi.ip_data.core, s);
hdmi_runtime_put();
mutex_unlock(&hdmi.lock);
@@ -609,7 +608,7 @@ static int read_edid(u8 *buf, int len)
r = hdmi_runtime_get();
BUG_ON(r);
- r = hdmi.ip_data.ops->read_edid(&hdmi.ip_data, buf, len);
+ r = hdmi4_read_edid(&hdmi.ip_data.core, buf, len);
hdmi_runtime_put();
mutex_unlock(&hdmi.lock);
@@ -813,7 +812,6 @@ static bool hdmi_mode_has_audio(void)
else
return false;
}
-
#endif
static int hdmi_connect(struct omap_dss_device *dssdev,
@@ -822,8 +820,6 @@ static int hdmi_connect(struct omap_dss_device *dssdev,
struct omap_overlay_manager *mgr;
int r;
- dss_init_hdmi_ip_ops(&hdmi.ip_data, omapdss_get_version());
-
r = hdmi_init_regulator();
if (r)
return r;
@@ -914,12 +910,12 @@ static void hdmi_audio_disable(struct omap_dss_device *dssdev)
static int hdmi_audio_start(struct omap_dss_device *dssdev)
{
- return hdmi.ip_data.ops->audio_start(&hdmi.ip_data);
+ return hdmi4_audio_start(&hdmi.ip_data.core, &hdmi.ip_data.wp);
}
static void hdmi_audio_stop(struct omap_dss_device *dssdev)
{
- hdmi.ip_data.ops->audio_stop(&hdmi.ip_data);
+ hdmi4_audio_stop(&hdmi.ip_data.core, &hdmi.ip_data.wp);
}
static bool hdmi_audio_supported(struct omap_dss_device *dssdev)
@@ -946,7 +942,7 @@ static int hdmi_audio_config(struct omap_dss_device *dssdev,
goto err;
}
- r = hdmi.ip_data.ops->audio_config(&hdmi.ip_data, audio);
+ r = hdmi4_audio_config(&hdmi.ip_data.core, &hdmi.ip_data.wp, audio);
if (r)
goto err;
@@ -1053,6 +1049,10 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
if (r)
return r;
+ r = hdmi4_core_init(pdev, &hdmi.ip_data.core);
+ if (r)
+ return r;
+
r = hdmi_get_clocks(pdev);
if (r) {
DSSERR("can't get clocks\n");
@@ -1061,9 +1061,6 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
- hdmi.ip_data.core_sys_offset = HDMI_CORE_SYS;
- hdmi.ip_data.core_av_offset = HDMI_CORE_AV;
-
hdmi_init_output(pdev);
dss_debugfs_create_file("hdmi", hdmi_dump_regs);
diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h
index 716bac4..9eb9b32 100644
--- a/drivers/video/omap2/dss/ti_hdmi.h
+++ b/drivers/video/omap2/dss/ti_hdmi.h
@@ -145,27 +145,6 @@ struct hdmi_audio_dma {
u16 fifo_threshold;
};
-struct ti_hdmi_ip_ops {
-
- void (*video_configure)(struct hdmi_ip_data *ip_data);
-
- int (*read_edid)(struct hdmi_ip_data *ip_data, u8 *edid, int len);
-
- void (*dump_core)(struct hdmi_ip_data *ip_data, struct seq_file *s);
-
-#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
- int (*audio_start)(struct hdmi_ip_data *ip_data);
-
- void (*audio_stop)(struct hdmi_ip_data *ip_data);
-
- int (*audio_config)(struct hdmi_ip_data *ip_data,
- struct omap_dss_audio *audio);
-
- int (*audio_get_dma_port)(u32 *offset, u32 *size);
-#endif
-
-};
-
/*
* Refer to section 8.2 in HDMI 1.3 specification for
* details about infoframe databytes
@@ -223,17 +202,19 @@ struct hdmi_phy_data {
int irq;
};
+struct hdmi_core_data {
+ void __iomem *base;
+
+ struct hdmi_core_infoframe_avi avi_cfg;
+};
+
struct hdmi_ip_data {
struct hdmi_wp_data wp;
struct hdmi_pll_data pll;
struct hdmi_phy_data phy;
+ struct hdmi_core_data core;
- unsigned long core_sys_offset;
- unsigned long core_av_offset;
-
- const struct ti_hdmi_ip_ops *ops;
struct hdmi_config cfg;
- struct hdmi_core_infoframe_avi avi_cfg;
/* ti_hdmi_4xxx_ip private data. These should be in a separate struct */
struct mutex lock;
@@ -273,9 +254,6 @@ void hdmi_phy_disable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp);
void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s);
int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy);
-int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data, u8 *edid, int len);
-void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data);
-void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts);
int hdmi_wp_audio_enable(struct hdmi_wp_data *wp, bool enable);
@@ -284,10 +262,5 @@ void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp,
struct hdmi_audio_format *aud_fmt);
void hdmi_wp_audio_config_dma(struct hdmi_wp_data *wp,
struct hdmi_audio_dma *aud_dma);
-int ti_hdmi_4xxx_audio_start(struct hdmi_ip_data *ip_data);
-void ti_hdmi_4xxx_audio_stop(struct hdmi_ip_data *ip_data);
-int ti_hdmi_4xxx_audio_config(struct hdmi_ip_data *ip_data,
- struct omap_dss_audio *audio);
-int ti_hdmi_4xxx_audio_get_dma_port(u32 *offset, u32 *size);
#endif
#endif
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
index c705aa1..ca208d8 100644
--- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
+++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
@@ -26,6 +26,7 @@
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/delay.h>
+#include <linux/platform_device.h>
#include <linux/string.h>
#include <linux/seq_file.h>
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
@@ -37,6 +38,8 @@
#include "dss.h"
#include "dss_features.h"
+#define HDMI_CORE_AV 0x500
+
static inline void hdmi_write_reg(void __iomem *base_addr,
const u16 idx, u32 val)
{
@@ -67,19 +70,14 @@ static inline int hdmi_wait_for_bit_change(void __iomem *base_addr,
return val;
}
-static inline void __iomem *hdmi_av_base(struct hdmi_ip_data *ip_data)
-{
- return ip_data->wp.base + ip_data->core_av_offset;
-}
-
-static inline void __iomem *hdmi_core_sys_base(struct hdmi_ip_data *ip_data)
+static inline void __iomem *hdmi_av_base(struct hdmi_core_data *core)
{
- return ip_data->wp.base + ip_data->core_sys_offset;
+ return core->base + HDMI_CORE_AV;
}
-static int hdmi_core_ddc_init(struct hdmi_ip_data *ip_data)
+static int hdmi_core_ddc_init(struct hdmi_core_data *core)
{
- void __iomem *base = hdmi_core_sys_base(ip_data);
+ void __iomem *base = core->base;
/* Turn on CLK for DDC */
REG_FLD_MOD(base, HDMI_CORE_AV_DPD, 0x7, 2, 0);
@@ -119,10 +117,10 @@ static int hdmi_core_ddc_init(struct hdmi_ip_data *ip_data)
return 0;
}
-static int hdmi_core_ddc_edid(struct hdmi_ip_data *ip_data,
+static int hdmi_core_ddc_edid(struct hdmi_core_data *core,
u8 *pedid, int ext)
{
- void __iomem *base = hdmi_core_sys_base(ip_data);
+ void __iomem *base = core->base;
u32 i;
char checksum;
u32 offset = 0;
@@ -201,26 +199,25 @@ static int hdmi_core_ddc_edid(struct hdmi_ip_data *ip_data,
return 0;
}
-int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data,
- u8 *edid, int len)
+int hdmi4_read_edid(struct hdmi_core_data *core, u8 *edid, int len)
{
int r, l;
if (len < 128)
return -EINVAL;
- r = hdmi_core_ddc_init(ip_data);
+ r = hdmi_core_ddc_init(core);
if (r)
return r;
- r = hdmi_core_ddc_edid(ip_data, edid, 0);
+ r = hdmi_core_ddc_edid(core, edid, 0);
if (r)
return r;
l = 128;
if (len >= 128 * 2 && edid[0x7e] > 0) {
- r = hdmi_core_ddc_edid(ip_data, edid + 0x80, 1);
+ r = hdmi_core_ddc_edid(core, edid + 0x80, 1);
if (r)
return r;
l += 128;
@@ -273,30 +270,31 @@ static void hdmi_core_init(struct hdmi_core_video_config *video_cfg,
repeat_cfg->generic_pkt_repeat = 0;
}
-static void hdmi_core_powerdown_disable(struct hdmi_ip_data *ip_data)
+static void hdmi_core_powerdown_disable(struct hdmi_core_data *core)
{
pr_debug("Enter hdmi_core_powerdown_disable\n");
- REG_FLD_MOD(hdmi_core_sys_base(ip_data), HDMI_CORE_SYS_SYS_CTRL1, 0x0, 0, 0);
+ REG_FLD_MOD(core->base, HDMI_CORE_SYS_SYS_CTRL1, 0x0, 0, 0);
}
-static void hdmi_core_swreset_release(struct hdmi_ip_data *ip_data)
+static void hdmi_core_swreset_release(struct hdmi_core_data *core)
{
pr_debug("Enter hdmi_core_swreset_release\n");
- REG_FLD_MOD(hdmi_core_sys_base(ip_data), HDMI_CORE_SYS_SRST, 0x0, 0, 0);
+ REG_FLD_MOD(core->base, HDMI_CORE_SYS_SRST, 0x0, 0, 0);
}
-static void hdmi_core_swreset_assert(struct hdmi_ip_data *ip_data)
+static void hdmi_core_swreset_assert(struct hdmi_core_data *core)
{
pr_debug("Enter hdmi_core_swreset_assert\n");
- REG_FLD_MOD(hdmi_core_sys_base(ip_data), HDMI_CORE_SYS_SRST, 0x1, 0, 0);
+ REG_FLD_MOD(core->base, HDMI_CORE_SYS_SRST, 0x1, 0, 0);
}
/* HDMI_CORE_VIDEO_CONFIG */
-static void hdmi_core_video_config(struct hdmi_ip_data *ip_data,
+static void hdmi_core_video_config(struct hdmi_core_data *core,
struct hdmi_core_video_config *cfg)
{
u32 r = 0;
- void __iomem *core_sys_base = hdmi_core_sys_base(ip_data);
+ void __iomem *core_sys_base = core->base;
+ void __iomem *core_av_base = hdmi_av_base(core);
/* sys_ctrl1 default configuration not tunable */
r = hdmi_read_reg(core_sys_base, HDMI_CORE_SYS_SYS_CTRL1);
@@ -323,23 +321,23 @@ static void hdmi_core_video_config(struct hdmi_ip_data *ip_data,
hdmi_write_reg(core_sys_base, HDMI_CORE_SYS_VID_MODE, r);
/* HDMI_Ctrl */
- r = hdmi_read_reg(hdmi_av_base(ip_data), HDMI_CORE_AV_HDMI_CTRL);
+ r = hdmi_read_reg(core_av_base, HDMI_CORE_AV_HDMI_CTRL);
r = FLD_MOD(r, cfg->deep_color_pkt, 6, 6);
r = FLD_MOD(r, cfg->pkt_mode, 5, 3);
r = FLD_MOD(r, cfg->hdmi_dvi, 0, 0);
- hdmi_write_reg(hdmi_av_base(ip_data), HDMI_CORE_AV_HDMI_CTRL, r);
+ hdmi_write_reg(core_av_base, HDMI_CORE_AV_HDMI_CTRL, r);
/* TMDS_CTRL */
REG_FLD_MOD(core_sys_base,
HDMI_CORE_SYS_TMDS_CTRL, cfg->tclk_sel_clkmult, 6, 5);
}
-static void hdmi_core_aux_infoframe_avi_config(struct hdmi_ip_data *ip_data)
+static void hdmi_core_aux_infoframe_avi_config(struct hdmi_core_data *core)
{
u32 val;
char sum = 0, checksum = 0;
- void __iomem *av_base = hdmi_av_base(ip_data);
- struct hdmi_core_infoframe_avi info_avi = ip_data->avi_cfg;
+ void __iomem *av_base = hdmi_av_base(core);
+ struct hdmi_core_infoframe_avi info_avi = core->avi_cfg;
sum += 0x82 + 0x002 + 0x00D;
hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_TYPE, 0x082);
@@ -410,64 +408,64 @@ static void hdmi_core_aux_infoframe_avi_config(struct hdmi_ip_data *ip_data)
hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_CHSUM, checksum);
}
-static void hdmi_core_av_packet_config(struct hdmi_ip_data *ip_data,
+static void hdmi_core_av_packet_config(struct hdmi_core_data *core,
struct hdmi_core_packet_enable_repeat repeat_cfg)
{
/* enable/repeat the infoframe */
- hdmi_write_reg(hdmi_av_base(ip_data), HDMI_CORE_AV_PB_CTRL1,
+ hdmi_write_reg(hdmi_av_base(core), HDMI_CORE_AV_PB_CTRL1,
(repeat_cfg.audio_pkt << 5) |
(repeat_cfg.audio_pkt_repeat << 4) |
(repeat_cfg.avi_infoframe << 1) |
(repeat_cfg.avi_infoframe_repeat));
/* enable/repeat the packet */
- hdmi_write_reg(hdmi_av_base(ip_data), HDMI_CORE_AV_PB_CTRL2,
+ hdmi_write_reg(hdmi_av_base(core), HDMI_CORE_AV_PB_CTRL2,
(repeat_cfg.gen_cntrl_pkt << 3) |
(repeat_cfg.gen_cntrl_pkt_repeat << 2) |
(repeat_cfg.generic_pkt << 1) |
(repeat_cfg.generic_pkt_repeat));
}
-void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data)
+void hdmi4_configure(struct hdmi_core_data *core,
+ struct hdmi_wp_data *wp, struct hdmi_config *cfg)
{
/* HDMI */
struct omap_video_timings video_timing;
struct hdmi_video_format video_format;
/* HDMI core */
- struct hdmi_core_infoframe_avi *avi_cfg = &ip_data->avi_cfg;
+ struct hdmi_core_infoframe_avi *avi_cfg = &core->avi_cfg;
struct hdmi_core_video_config v_core_cfg;
struct hdmi_core_packet_enable_repeat repeat_cfg;
- struct hdmi_config *cfg = &ip_data->cfg;
hdmi_core_init(&v_core_cfg, avi_cfg, &repeat_cfg);
hdmi_wp_init_vid_fmt_timings(&video_format, &video_timing, cfg);
- hdmi_wp_video_config_timing(&ip_data->wp, &video_timing);
+ hdmi_wp_video_config_timing(wp, &video_timing);
/* video config */
video_format.packing_mode = HDMI_PACK_24b_RGB_YUV444_YUV422;
- hdmi_wp_video_config_format(&ip_data->wp, &video_format);
+ hdmi_wp_video_config_format(wp, &video_format);
- hdmi_wp_video_config_interface(&ip_data->wp, &video_timing);
+ hdmi_wp_video_config_interface(wp, &video_timing);
/*
* configure core video part
* set software reset in the core
*/
- hdmi_core_swreset_assert(ip_data);
+ hdmi_core_swreset_assert(core);
/* power down off */
- hdmi_core_powerdown_disable(ip_data);
+ hdmi_core_powerdown_disable(core);
v_core_cfg.pkt_mode = HDMI_PACKETMODE24BITPERPIXEL;
v_core_cfg.hdmi_dvi = cfg->cm.mode;
- hdmi_core_video_config(ip_data, &v_core_cfg);
+ hdmi_core_video_config(core, &v_core_cfg);
/* release software reset in the core */
- hdmi_core_swreset_release(ip_data);
+ hdmi_core_swreset_release(core);
/*
* configure packet
@@ -492,7 +490,7 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data)
avi_cfg->db10_11_pixel_eofleft = 0;
avi_cfg->db12_13_pixel_sofright = 0;
- hdmi_core_aux_infoframe_avi_config(ip_data);
+ hdmi_core_aux_infoframe_avi_config(core);
/* enable/repeat the infoframe */
repeat_cfg.avi_infoframe = HDMI_PACKETENABLE;
@@ -500,21 +498,21 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data)
/* wakeup */
repeat_cfg.audio_pkt = HDMI_PACKETENABLE;
repeat_cfg.audio_pkt_repeat = HDMI_PACKETREPEATON;
- hdmi_core_av_packet_config(ip_data, repeat_cfg);
+ hdmi_core_av_packet_config(core, repeat_cfg);
}
-void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
+void hdmi4_core_dump(struct hdmi_core_data *core, struct seq_file *s)
{
int i;
#define CORE_REG(i, name) name(i)
#define DUMPCORE(r) seq_printf(s, "%-35s %08x\n", #r,\
- hdmi_read_reg(hdmi_core_sys_base(ip_data), r))
+ hdmi_read_reg(core->base, r))
#define DUMPCOREAV(r) seq_printf(s, "%-35s %08x\n", #r,\
- hdmi_read_reg(hdmi_av_base(ip_data), r))
+ hdmi_read_reg(hdmi_av_base(core), r))
#define DUMPCOREAV2(i, r) seq_printf(s, "%s[%d]%*s %08x\n", #r, i, \
(i < 10) ? 32 - (int)strlen(#r) : 31 - (int)strlen(#r), " ", \
- hdmi_read_reg(hdmi_av_base(ip_data), CORE_REG(i, r)))
+ hdmi_read_reg(hdmi_av_base(core), CORE_REG(i, r)))
DUMPCORE(HDMI_CORE_SYS_VND_IDL);
DUMPCORE(HDMI_CORE_SYS_DEV_IDL);
@@ -672,11 +670,11 @@ void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
}
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
-static void ti_hdmi_4xxx_core_audio_config(struct hdmi_ip_data *ip_data,
+static void hdmi_core_audio_config(struct hdmi_core_data *core,
struct hdmi_core_audio_config *cfg)
{
u32 r;
- void __iomem *av_base = hdmi_av_base(ip_data);
+ void __iomem *av_base = hdmi_av_base(core);
/*
* Parameters for generation of Audio Clock Recovery packets
@@ -771,11 +769,11 @@ static void ti_hdmi_4xxx_core_audio_config(struct hdmi_ip_data *ip_data,
REG_FLD_MOD(av_base, HDMI_CORE_AV_SWAP_I2S, 1, 5, 5);
}
-static void ti_hdmi_4xxx_core_audio_infoframe_cfg(struct hdmi_ip_data *ip_data,
+static void hdmi_core_audio_infoframe_cfg(struct hdmi_core_data *core,
struct snd_cea_861_aud_if *info_aud)
{
u8 sum = 0, checksum = 0;
- void __iomem *av_base = hdmi_av_base(ip_data);
+ void __iomem *av_base = hdmi_av_base(core);
/*
* Set audio info frame type, version and length as
@@ -821,20 +819,20 @@ static void ti_hdmi_4xxx_core_audio_infoframe_cfg(struct hdmi_ip_data *ip_data,
*/
}
-int ti_hdmi_4xxx_audio_config(struct hdmi_ip_data *ip_data,
+int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
struct omap_dss_audio *audio)
{
struct hdmi_audio_format audio_format;
struct hdmi_audio_dma audio_dma;
- struct hdmi_core_audio_config core;
+ struct hdmi_core_audio_config acore;
int err, n, cts, channel_count;
unsigned int fs_nr;
bool word_length_16b = false;
- if (!audio || !audio->iec || !audio->cea || !ip_data)
+ if (!audio || !audio->iec || !audio->cea || !core)
return -EINVAL;
- core.iec60958_cfg = audio->iec;
+ acore.iec60958_cfg = audio->iec;
/*
* In the IEC-60958 status word, check if the audio sample word length
* is 16-bit as several optimizations can be performed in such case.
@@ -845,22 +843,22 @@ int ti_hdmi_4xxx_audio_config(struct hdmi_ip_data *ip_data,
/* I2S configuration. See Phillips' specification */
if (word_length_16b)
- core.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_LEFT;
+ acore.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_LEFT;
else
- core.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_RIGHT;
+ acore.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_RIGHT;
/*
* The I2S input word length is twice the lenght given in the IEC-60958
* status word. If the word size is greater than
* 20 bits, increment by one.
*/
- core.i2s_cfg.in_length_bits = audio->iec->status[4]
+ acore.i2s_cfg.in_length_bits = audio->iec->status[4]
& IEC958_AES4_CON_WORDLEN;
if (audio->iec->status[4] & IEC958_AES4_CON_MAX_WORDLEN_24)
- core.i2s_cfg.in_length_bits++;
- core.i2s_cfg.sck_edge_mode = HDMI_AUDIO_I2S_SCK_EDGE_RISING;
- core.i2s_cfg.vbit = HDMI_AUDIO_I2S_VBIT_FOR_PCM;
- core.i2s_cfg.direction = HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST;
- core.i2s_cfg.shift = HDMI_AUDIO_I2S_FIRST_BIT_SHIFT;
+ acore.i2s_cfg.in_length_bits++;
+ acore.i2s_cfg.sck_edge_mode = HDMI_AUDIO_I2S_SCK_EDGE_RISING;
+ acore.i2s_cfg.vbit = HDMI_AUDIO_I2S_VBIT_FOR_PCM;
+ acore.i2s_cfg.direction = HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST;
+ acore.i2s_cfg.shift = HDMI_AUDIO_I2S_FIRST_BIT_SHIFT;
/* convert sample frequency to a number */
switch (audio->iec->status[3] & IEC958_AES3_CON_FS) {
@@ -892,20 +890,20 @@ int ti_hdmi_4xxx_audio_config(struct hdmi_ip_data *ip_data,
err = hdmi_compute_acr(fs_nr, &n, &cts);
/* Audio clock regeneration settings */
- core.n = n;
- core.cts = cts;
+ acore.n = n;
+ acore.cts = cts;
if (dss_has_feature(FEAT_HDMI_CTS_SWMODE)) {
- core.aud_par_busclk = 0;
- core.cts_mode = HDMI_AUDIO_CTS_MODE_SW;
- core.use_mclk = dss_has_feature(FEAT_HDMI_AUDIO_USE_MCLK);
+ acore.aud_par_busclk = 0;
+ acore.cts_mode = HDMI_AUDIO_CTS_MODE_SW;
+ acore.use_mclk = dss_has_feature(FEAT_HDMI_AUDIO_USE_MCLK);
} else {
- core.aud_par_busclk = (((128 * 31) - 1) << 8);
- core.cts_mode = HDMI_AUDIO_CTS_MODE_HW;
- core.use_mclk = true;
+ acore.aud_par_busclk = (((128 * 31) - 1) << 8);
+ acore.cts_mode = HDMI_AUDIO_CTS_MODE_HW;
+ acore.use_mclk = true;
}
- if (core.use_mclk)
- core.mclk_mode = HDMI_AUDIO_MCLK_128FS;
+ if (acore.use_mclk)
+ acore.mclk_mode = HDMI_AUDIO_MCLK_128FS;
/* Audio channels settings */
channel_count = (audio->cea->db1_ct_cc &
@@ -943,25 +941,25 @@ int ti_hdmi_4xxx_audio_config(struct hdmi_ip_data *ip_data,
*/
if (channel_count = 2) {
audio_format.stereo_channels = HDMI_AUDIO_STEREO_ONECHANNEL;
- core.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN;
- core.layout = HDMI_AUDIO_LAYOUT_2CH;
+ acore.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN;
+ acore.layout = HDMI_AUDIO_LAYOUT_2CH;
} else {
audio_format.stereo_channels = HDMI_AUDIO_STEREO_FOURCHANNELS;
- core.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN |
+ acore.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN |
HDMI_AUDIO_I2S_SD1_EN | HDMI_AUDIO_I2S_SD2_EN |
HDMI_AUDIO_I2S_SD3_EN;
- core.layout = HDMI_AUDIO_LAYOUT_8CH;
+ acore.layout = HDMI_AUDIO_LAYOUT_8CH;
}
- core.en_spdif = false;
+ acore.en_spdif = false;
/* use sample frequency from channel status word */
- core.fs_override = true;
+ acore.fs_override = true;
/* enable ACR packets */
- core.en_acr_pkt = true;
+ acore.en_acr_pkt = true;
/* disable direct streaming digital audio */
- core.en_dsd_audio = false;
+ acore.en_dsd_audio = false;
/* use parallel audio interface */
- core.en_parallel_aud_input = true;
+ acore.en_parallel_aud_input = true;
/* DMA settings */
if (word_length_16b)
@@ -988,37 +986,37 @@ int ti_hdmi_4xxx_audio_config(struct hdmi_ip_data *ip_data,
audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_ON;
/* configure DMA and audio FIFO format*/
- hdmi_wp_audio_config_dma(&ip_data->wp, &audio_dma);
- hdmi_wp_audio_config_format(&ip_data->wp, &audio_format);
+ hdmi_wp_audio_config_dma(wp, &audio_dma);
+ hdmi_wp_audio_config_format(wp, &audio_format);
/* configure the core*/
- ti_hdmi_4xxx_core_audio_config(ip_data, &core);
+ hdmi_core_audio_config(core, &acore);
/* configure CEA 861 audio infoframe*/
- ti_hdmi_4xxx_core_audio_infoframe_cfg(ip_data, audio->cea);
+ hdmi_core_audio_infoframe_cfg(core, audio->cea);
return 0;
}
-int ti_hdmi_4xxx_audio_start(struct hdmi_ip_data *ip_data)
+int hdmi4_audio_start(struct hdmi_core_data *core, struct hdmi_wp_data *wp)
{
- REG_FLD_MOD(hdmi_av_base(ip_data),
+ REG_FLD_MOD(hdmi_av_base(core),
HDMI_CORE_AV_AUD_MODE, true, 0, 0);
- hdmi_wp_audio_core_req_enable(&ip_data->wp, true);
+ hdmi_wp_audio_core_req_enable(wp, true);
return 0;
}
-void ti_hdmi_4xxx_audio_stop(struct hdmi_ip_data *ip_data)
+void hdmi4_audio_stop(struct hdmi_core_data *core, struct hdmi_wp_data *wp)
{
- REG_FLD_MOD(hdmi_av_base(ip_data),
+ REG_FLD_MOD(hdmi_av_base(core),
HDMI_CORE_AV_AUD_MODE, false, 0, 0);
- hdmi_wp_audio_core_req_enable(&ip_data->wp, false);
+ hdmi_wp_audio_core_req_enable(wp, false);
}
-int ti_hdmi_4xxx_audio_get_dma_port(u32 *offset, u32 *size)
+int hdmi4_audio_get_dma_port(u32 *offset, u32 *size)
{
if (!offset || !size)
return -EINVAL;
@@ -1028,3 +1026,22 @@ int ti_hdmi_4xxx_audio_get_dma_port(u32 *offset, u32 *size)
}
#endif
+
+int hdmi4_core_init(struct platform_device *pdev, struct hdmi_core_data *core)
+{
+ struct resource *res;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi_core");
+ if (!res) {
+ DSSERR("can't get CORE IORESOURCE_MEM HDMI\n");
+ return -EINVAL;
+ }
+
+ core->base = devm_request_and_ioremap(&pdev->dev, res);
+ if (!core->base) {
+ DSSERR("can't ioremap HDMI core\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h
index dc49713..b9bb300 100644
--- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h
+++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h
@@ -400,4 +400,18 @@ struct hdmi_core_audio_config {
bool en_spdif;
};
+int hdmi4_read_edid(struct hdmi_core_data *core, u8 *edid, int len);
+void hdmi4_configure(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
+ struct hdmi_config *cfg);
+void hdmi4_core_dump(struct hdmi_core_data *core, struct seq_file *s);
+int hdmi4_core_init(struct platform_device *pdev, struct hdmi_core_data *core);
+
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
+int hdmi4_audio_start(struct hdmi_core_data *core, struct hdmi_wp_data *wp);
+void hdmi4_audio_stop(struct hdmi_core_data *core, struct hdmi_wp_data *wp);
+int hdmi4_audio_config(struct hdmi_core_data *core, struct hdmi_wp_data *wp,
+ struct omap_dss_audio *audio);
+int hdmi4_audio_get_dma_port(u32 *offset, u32 *size);
+#endif
+
#endif
--
1.8.1.2
^ permalink raw reply related
* [PATCH 03/11] omapdss: HDMI: create a PHY library
From: Archit Taneja @ 2013-09-17 7:18 UTC (permalink / raw)
To: tomi.valkeinen; +Cc: linux-omap, linux-fbdev, Archit Taneja
In-Reply-To: <1379401597-27222-1-git-send-email-archit@ti.com>
HDMI PHY is a block common to DSS in OMAP4, OMAP5 and DRA7x. Move the
existing functions from ti_hdmi_4xxx_ip.c to a separate file. These funcs are
called directly from the hdmi driver rather than hdmi_ip_ops function pointer
calls.
Add the PHY library function declarations to ti_hdmi.h. These will be shared
amongst the omap4/5 hdmi platform drivers. Remove the PHY function pointer ops
from the ti_hdmi_ip_ops struct.
Signed-off-by: Archit Taneja <archit@ti.com>
---
drivers/video/omap2/dss/Makefile | 3 +-
drivers/video/omap2/dss/dss_features.c | 3 -
drivers/video/omap2/dss/hdmi.c | 19 ++--
drivers/video/omap2/dss/hdmi_phy.c | 176 ++++++++++++++++++++++++++++++
drivers/video/omap2/dss/ti_hdmi.h | 26 +++--
drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 109 ------------------
6 files changed, 201 insertions(+), 135 deletions(-)
create mode 100644 drivers/video/omap2/dss/hdmi_phy.c
diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile
index 5ea65d3..d88e938 100644
--- a/drivers/video/omap2/dss/Makefile
+++ b/drivers/video/omap2/dss/Makefile
@@ -10,5 +10,6 @@ omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o
omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o
omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o
omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o
-omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o hdmi_wp.o hdmi_pll.o ti_hdmi_4xxx_ip.o
+omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o hdmi_wp.o hdmi_pll.o hdmi_phy.o \
+ ti_hdmi_4xxx_ip.o
ccflags-$(CONFIG_OMAP2_DSS_DEBUG) += -DDEBUG
diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c
index 9ee92e9..2777eb6 100644
--- a/drivers/video/omap2/dss/dss_features.c
+++ b/drivers/video/omap2/dss/dss_features.c
@@ -794,11 +794,8 @@ static const struct omap_dss_features omap5_dss_features = {
static const struct ti_hdmi_ip_ops omap4_hdmi_functions = {
.video_configure = ti_hdmi_4xxx_basic_configure,
- .phy_enable = ti_hdmi_4xxx_phy_enable,
- .phy_disable = ti_hdmi_4xxx_phy_disable,
.read_edid = ti_hdmi_4xxx_read_edid,
.dump_core = ti_hdmi_4xxx_core_dump,
- .dump_phy = ti_hdmi_4xxx_phy_dump,
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
.audio_start = ti_hdmi_4xxx_audio_start,
.audio_stop = ti_hdmi_4xxx_audio_stop,
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
index f6a2eba..f7e2ac6 100644
--- a/drivers/video/omap2/dss/hdmi.c
+++ b/drivers/video/omap2/dss/hdmi.c
@@ -42,7 +42,6 @@
#define HDMI_CORE_SYS 0x400
#define HDMI_CORE_AV 0x900
-#define HDMI_PHY 0x300
/* HDMI EDID Length move this */
#define HDMI_EDID_MAX_LENGTH 256
@@ -487,7 +486,8 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
goto err_pll_enable;
}
- r = hdmi.ip_data.ops->phy_enable(&hdmi.ip_data);
+ r = hdmi_phy_enable(&hdmi.ip_data.phy, &hdmi.ip_data.wp,
+ &hdmi.ip_data.cfg);
if (r) {
DSSDBG("Failed to start PHY\n");
goto err_phy_enable;
@@ -514,7 +514,7 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
err_mgr_enable:
hdmi_wp_video_stop(&hdmi.ip_data.wp);
err_vid_enable:
- hdmi.ip_data.ops->phy_disable(&hdmi.ip_data);
+ hdmi_phy_disable(&hdmi.ip_data.phy, &hdmi.ip_data.wp);
err_phy_enable:
hdmi_pll_disable(&hdmi.ip_data.pll, &hdmi.ip_data.wp);
err_pll_enable:
@@ -529,7 +529,7 @@ static void hdmi_power_off_full(struct omap_dss_device *dssdev)
dss_mgr_disable(mgr);
hdmi_wp_video_stop(&hdmi.ip_data.wp);
- hdmi.ip_data.ops->phy_disable(&hdmi.ip_data);
+ hdmi_phy_disable(&hdmi.ip_data.phy, &hdmi.ip_data.wp);
hdmi_pll_disable(&hdmi.ip_data.pll, &hdmi.ip_data.wp);
hdmi_power_off_core(dssdev);
@@ -593,7 +593,7 @@ static void hdmi_dump_regs(struct seq_file *s)
hdmi_wp_dump(&hdmi.ip_data.wp, s);
hdmi_pll_dump(&hdmi.ip_data.pll, s);
- hdmi.ip_data.ops->dump_phy(&hdmi.ip_data, s);
+ hdmi_phy_dump(&hdmi.ip_data.phy, s);
hdmi.ip_data.ops->dump_core(&hdmi.ip_data, s);
hdmi_runtime_put();
@@ -1049,11 +1049,9 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
if (r)
return r;
- hdmi.ip_data.irq = platform_get_irq(pdev, 0);
- if (hdmi.ip_data.irq < 0) {
- DSSERR("platform_get_irq failed\n");
- return -ENODEV;
- }
+ r = hdmi_phy_init(pdev, &hdmi.ip_data.phy);
+ if (r)
+ return r;
r = hdmi_get_clocks(pdev);
if (r) {
@@ -1065,7 +1063,6 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
hdmi.ip_data.core_sys_offset = HDMI_CORE_SYS;
hdmi.ip_data.core_av_offset = HDMI_CORE_AV;
- hdmi.ip_data.phy_offset = HDMI_PHY;
hdmi_init_output(pdev);
diff --git a/drivers/video/omap2/dss/hdmi_phy.c b/drivers/video/omap2/dss/hdmi_phy.c
new file mode 100644
index 0000000..cacd3c4
--- /dev/null
+++ b/drivers/video/omap2/dss/hdmi_phy.c
@@ -0,0 +1,176 @@
+/*
+ * HDMI PHY
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <video/omapdss.h>
+
+#include "dss.h"
+#include "ti_hdmi.h"
+#include "ti_hdmi_4xxx_ip.h"
+
+#define HDMI_IRQ_LINK_CONNECT (1 << 25)
+#define HDMI_IRQ_LINK_DISCONNECT (1 << 26)
+
+static inline void hdmi_write_reg(void __iomem *base_addr, const u16 idx,
+ u32 val)
+{
+ __raw_writel(val, base_addr + idx);
+}
+
+static inline u32 hdmi_read_reg(void __iomem *base_addr, const u16 idx)
+{
+ return __raw_readl(base_addr + idx);
+}
+
+#define REG_FLD_MOD(base, idx, val, start, end) \
+ hdmi_write_reg(base, idx, FLD_MOD(hdmi_read_reg(base, idx),\
+ val, start, end))
+#define REG_GET(base, idx, start, end) \
+ FLD_GET(hdmi_read_reg(base, idx), start, end)
+
+static inline int hdmi_wait_for_bit_change(void __iomem *base_addr,
+ const u16 idx, int b2, int b1, u32 val)
+{
+ u32 t = 0;
+ while (val != REG_GET(base_addr, idx, b2, b1)) {
+ udelay(1);
+ if (t++ > 10000)
+ return !val;
+ }
+ return val;
+}
+
+void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s)
+{
+#define DUMPPHY(r) seq_printf(s, "%-35s %08x\n", #r,\
+ hdmi_read_reg(phy->base, r))
+
+ DUMPPHY(HDMI_TXPHY_TX_CTRL);
+ DUMPPHY(HDMI_TXPHY_DIGITAL_CTRL);
+ DUMPPHY(HDMI_TXPHY_POWER_CTRL);
+ DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL);
+}
+
+static irqreturn_t hdmi_irq_handler(int irq, void *data)
+{
+ struct hdmi_wp_data *wp = data;
+ u32 irqstatus;
+
+ irqstatus = hdmi_wp_get_irqstatus(wp);
+ hdmi_wp_set_irqstatus(wp, irqstatus);
+
+ if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
+ irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
+ /*
+ * If we get both connect and disconnect interrupts at the same
+ * time, turn off the PHY, clear interrupts, and restart, which
+ * raises connect interrupt if a cable is connected, or nothing
+ * if cable is not connected.
+ */
+ hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
+
+ hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT |
+ HDMI_IRQ_LINK_DISCONNECT);
+
+ hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
+ } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
+ hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON);
+ } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
+ hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
+ }
+
+ return IRQ_HANDLED;
+}
+
+int hdmi_phy_enable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp,
+ struct hdmi_config *cfg)
+{
+ u16 r = 0;
+ u32 irqstatus;
+
+ hdmi_wp_clear_irqenable(wp, 0xffffffff);
+
+ irqstatus = hdmi_wp_get_irqstatus(wp);
+ hdmi_wp_set_irqstatus(wp, irqstatus);
+
+ r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
+ if (r)
+ return r;
+
+ /*
+ * Read address 0 in order to get the SCP reset done completed
+ * Dummy access performed to make sure reset is done
+ */
+ hdmi_read_reg(phy->base, HDMI_TXPHY_TX_CTRL);
+
+ /*
+ * Write to phy address 0 to configure the clock
+ * use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field
+ */
+ REG_FLD_MOD(phy->base, HDMI_TXPHY_TX_CTRL, 0x1, 31, 30);
+
+ /* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */
+ hdmi_write_reg(phy->base, HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000);
+
+ /* Setup max LDO voltage */
+ REG_FLD_MOD(phy->base, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0);
+
+ /* Write to phy address 3 to change the polarity control */
+ REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27);
+
+ r = request_threaded_irq(phy->irq, NULL, hdmi_irq_handler,
+ IRQF_ONESHOT, "OMAP HDMI", wp);
+ if (r) {
+ DSSERR("HDMI IRQ request failed\n");
+ hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
+ return r;
+ }
+
+ hdmi_wp_set_irqenable(wp,
+ HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
+
+ return 0;
+}
+
+void hdmi_phy_disable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp)
+{
+ free_irq(phy->irq, wp);
+
+ hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
+}
+
+int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy)
+{
+ struct resource *res;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi_txphy");
+ if (!res) {
+ DSSERR("can't get PLL CTRL IORESOURCE_MEM HDMI\n");
+ return -EINVAL;
+ }
+
+ phy->base = devm_request_and_ioremap(&pdev->dev, res);
+ if (!phy->base) {
+ DSSERR("can't ioremap PLL ctrl\n");
+ return -ENOMEM;
+ }
+
+ phy->irq = platform_get_irq(pdev, 0);
+ if (phy->irq < 0) {
+ DSSERR("platform_get_irq failed\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h
index 62a83c7..716bac4 100644
--- a/drivers/video/omap2/dss/ti_hdmi.h
+++ b/drivers/video/omap2/dss/ti_hdmi.h
@@ -149,16 +149,10 @@ struct ti_hdmi_ip_ops {
void (*video_configure)(struct hdmi_ip_data *ip_data);
- int (*phy_enable)(struct hdmi_ip_data *ip_data);
-
- void (*phy_disable)(struct hdmi_ip_data *ip_data);
-
int (*read_edid)(struct hdmi_ip_data *ip_data, u8 *edid, int len);
void (*dump_core)(struct hdmi_ip_data *ip_data, struct seq_file *s);
- void (*dump_phy)(struct hdmi_ip_data *ip_data, struct seq_file *s);
-
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
int (*audio_start)(struct hdmi_ip_data *ip_data);
@@ -223,14 +217,20 @@ struct hdmi_pll_data {
struct hdmi_pll_info info;
};
+struct hdmi_phy_data {
+ void __iomem *base;
+
+ int irq;
+};
+
struct hdmi_ip_data {
struct hdmi_wp_data wp;
struct hdmi_pll_data pll;
+ struct hdmi_phy_data phy;
unsigned long core_sys_offset;
unsigned long core_av_offset;
- unsigned long phy_offset;
- int irq;
+
const struct ti_hdmi_ip_ops *ops;
struct hdmi_config cfg;
struct hdmi_core_infoframe_avi avi_cfg;
@@ -266,12 +266,16 @@ void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s);
void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy);
int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll);
-int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data);
-void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data);
+/* HDMI PHY funcs */
+int hdmi_phy_enable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp,
+ struct hdmi_config *cfg);
+void hdmi_phy_disable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp);
+void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s);
+int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy);
+
int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data, u8 *edid, int len);
void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data);
void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
-void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts);
int hdmi_wp_audio_enable(struct hdmi_wp_data *wp, bool enable);
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
index 8cfb54b..c705aa1 100644
--- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
+++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
@@ -37,9 +37,6 @@
#include "dss.h"
#include "dss_features.h"
-#define HDMI_IRQ_LINK_CONNECT (1 << 25)
-#define HDMI_IRQ_LINK_DISCONNECT (1 << 26)
-
static inline void hdmi_write_reg(void __iomem *base_addr,
const u16 idx, u32 val)
{
@@ -70,11 +67,6 @@ static inline int hdmi_wait_for_bit_change(void __iomem *base_addr,
return val;
}
-static inline void __iomem *hdmi_phy_base(struct hdmi_ip_data *ip_data)
-{
- return ip_data->wp.base + ip_data->phy_offset;
-}
-
static inline void __iomem *hdmi_av_base(struct hdmi_ip_data *ip_data)
{
return ip_data->wp.base + ip_data->core_av_offset;
@@ -85,94 +77,6 @@ static inline void __iomem *hdmi_core_sys_base(struct hdmi_ip_data *ip_data)
return ip_data->wp.base + ip_data->core_sys_offset;
}
-static irqreturn_t hdmi_irq_handler(int irq, void *data)
-{
- struct hdmi_ip_data *ip_data = data;
- u32 irqstatus;
-
- irqstatus = hdmi_wp_get_irqstatus(&ip_data->wp);
- hdmi_wp_set_irqstatus(&ip_data->wp, irqstatus);
-
- if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
- irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
- /*
- * If we get both connect and disconnect interrupts at the same
- * time, turn off the PHY, clear interrupts, and restart, which
- * raises connect interrupt if a cable is connected, or nothing
- * if cable is not connected.
- */
- hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_OFF);
-
- hdmi_wp_set_irqstatus(&ip_data->wp, HDMI_IRQ_LINK_CONNECT |
- HDMI_IRQ_LINK_DISCONNECT);
-
- hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_LDOON);
- } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
- hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_TXON);
- } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
- hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_LDOON);
- }
-
- return IRQ_HANDLED;
-}
-
-int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data)
-{
- u16 r = 0;
- u32 irqstatus;
- void __iomem *phy_base = hdmi_phy_base(ip_data);
-
- hdmi_wp_clear_irqenable(&ip_data->wp, 0xffffffff);
-
- irqstatus = hdmi_wp_get_irqstatus(&ip_data->wp);
- hdmi_wp_set_irqstatus(&ip_data->wp, irqstatus);
-
- r = hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_LDOON);
- if (r)
- return r;
-
- /*
- * Read address 0 in order to get the SCP reset done completed
- * Dummy access performed to make sure reset is done
- */
- hdmi_read_reg(phy_base, HDMI_TXPHY_TX_CTRL);
-
- /*
- * Write to phy address 0 to configure the clock
- * use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field
- */
- REG_FLD_MOD(phy_base, HDMI_TXPHY_TX_CTRL, 0x1, 31, 30);
-
- /* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */
- hdmi_write_reg(phy_base, HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000);
-
- /* Setup max LDO voltage */
- REG_FLD_MOD(phy_base, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0);
-
- /* Write to phy address 3 to change the polarity control */
- REG_FLD_MOD(phy_base, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27);
-
- r = request_threaded_irq(ip_data->irq, NULL, hdmi_irq_handler,
- IRQF_ONESHOT, "OMAP HDMI", ip_data);
- if (r) {
- DSSERR("HDMI IRQ request failed\n");
- hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_OFF);
- return r;
- }
-
- hdmi_wp_set_irqenable(&ip_data->wp,
- HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
-
- return 0;
-}
-
-void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data)
-{
- free_irq(ip_data->irq, ip_data);
-
- hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_OFF);
-}
-
static int hdmi_core_ddc_init(struct hdmi_ip_data *ip_data)
{
void __iomem *base = hdmi_core_sys_base(ip_data);
@@ -524,8 +428,6 @@ static void hdmi_core_av_packet_config(struct hdmi_ip_data *ip_data,
(repeat_cfg.generic_pkt_repeat));
}
-
-
void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data)
{
/* HDMI */
@@ -769,17 +671,6 @@ void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
DUMPCOREAV(HDMI_CORE_AV_CEC_ADDR_ID);
}
-void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
-{
-#define DUMPPHY(r) seq_printf(s, "%-35s %08x\n", #r,\
- hdmi_read_reg(hdmi_phy_base(ip_data), r))
-
- DUMPPHY(HDMI_TXPHY_TX_CTRL);
- DUMPPHY(HDMI_TXPHY_DIGITAL_CTRL);
- DUMPPHY(HDMI_TXPHY_POWER_CTRL);
- DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL);
-}
-
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
static void ti_hdmi_4xxx_core_audio_config(struct hdmi_ip_data *ip_data,
struct hdmi_core_audio_config *cfg)
--
1.8.1.2
^ permalink raw reply related
* [PATCH 02/11] omapdss: HDMI: create a HDMI PLL library
From: Archit Taneja @ 2013-09-17 7:18 UTC (permalink / raw)
To: tomi.valkeinen; +Cc: linux-omap, linux-fbdev, Archit Taneja
In-Reply-To: <1379401597-27222-1-git-send-email-archit@ti.com>
HDMI PLL is a block common to DSS in OMAP4, OMAP5 and DRA7x. Move the
existing PLL functions from ti_hdmi_4xxx_ip.c and hdmi.c to a separate file.
These funcs are called directly from the hdmi driver rather than hdmi_ip_ops
function pointer calls.
Add the PLL library function declarations to ti_hdmi.h. These will be shared
amongst the omap4/5 hdmi platform drivers. Remove the PLL function pointer ops
from the ti_hdmi_ip_ops struct. These will be shared amongst the omap4/5 hdmi
platform drivers and other libraries.
Signed-off-by: Archit Taneja <archit@ti.com>
---
drivers/video/omap2/dss/Makefile | 2 +-
drivers/video/omap2/dss/dss_features.c | 3 -
drivers/video/omap2/dss/hdmi.c | 65 ++------
drivers/video/omap2/dss/hdmi_pll.c | 243 ++++++++++++++++++++++++++++++
drivers/video/omap2/dss/ti_hdmi.h | 25 +--
drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 132 ----------------
6 files changed, 267 insertions(+), 203 deletions(-)
create mode 100644 drivers/video/omap2/dss/hdmi_pll.c
diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile
index 56ce6bd..5ea65d3 100644
--- a/drivers/video/omap2/dss/Makefile
+++ b/drivers/video/omap2/dss/Makefile
@@ -10,5 +10,5 @@ omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o
omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o
omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o
omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o
-omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o hdmi_wp.o ti_hdmi_4xxx_ip.o
+omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o hdmi_wp.o hdmi_pll.o ti_hdmi_4xxx_ip.o
ccflags-$(CONFIG_OMAP2_DSS_DEBUG) += -DDEBUG
diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c
index db359e8..9ee92e9 100644
--- a/drivers/video/omap2/dss/dss_features.c
+++ b/drivers/video/omap2/dss/dss_features.c
@@ -797,10 +797,7 @@ static const struct ti_hdmi_ip_ops omap4_hdmi_functions = {
.phy_enable = ti_hdmi_4xxx_phy_enable,
.phy_disable = ti_hdmi_4xxx_phy_disable,
.read_edid = ti_hdmi_4xxx_read_edid,
- .pll_enable = ti_hdmi_4xxx_pll_enable,
- .pll_disable = ti_hdmi_4xxx_pll_disable,
.dump_core = ti_hdmi_4xxx_core_dump,
- .dump_pll = ti_hdmi_4xxx_pll_dump,
.dump_phy = ti_hdmi_4xxx_phy_dump,
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
.audio_start = ti_hdmi_4xxx_audio_start,
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
index f2475fc..f6a2eba 100644
--- a/drivers/video/omap2/dss/hdmi.c
+++ b/drivers/video/omap2/dss/hdmi.c
@@ -42,7 +42,6 @@
#define HDMI_CORE_SYS 0x400
#define HDMI_CORE_AV 0x900
-#define HDMI_PLLCTRL 0x200
#define HDMI_PHY 0x300
/* HDMI EDID Length move this */
@@ -53,9 +52,6 @@
#define EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR 4
#define EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR 4
-#define HDMI_DEFAULT_REGN 16
-#define HDMI_DEFAULT_REGM2 1
-
static struct {
struct mutex lock;
struct platform_device *pdev;
@@ -428,52 +424,6 @@ end: return cm;
}
-static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy,
- struct hdmi_pll_info *pi)
-{
- unsigned long clkin, refclk;
- u32 mf;
-
- clkin = clk_get_rate(hdmi.sys_clk) / 10000;
- /*
- * Input clock is predivided by N + 1
- * out put of which is reference clk
- */
-
- pi->regn = HDMI_DEFAULT_REGN;
-
- refclk = clkin / pi->regn;
-
- pi->regm2 = HDMI_DEFAULT_REGM2;
-
- /*
- * multiplier is pixel_clk/ref_clk
- * Multiplying by 100 to avoid fractional part removal
- */
- pi->regm = phy * pi->regm2 / refclk;
-
- /*
- * fractional multiplier is remainder of the difference between
- * multiplier and actual phy(required pixel clock thus should be
- * multiplied by 2^18(262144) divided by the reference clock
- */
- mf = (phy - pi->regm / pi->regm2 * refclk) * 262144;
- pi->regmf = pi->regm2 * mf / refclk;
-
- /*
- * Dcofreq should be set to 1 if required pixel clock
- * is greater than 1000MHz
- */
- pi->dcofreq = phy > 1000 * 100;
- pi->regsd = ((pi->regm * clkin / 10) / (pi->regn * 250) + 5) / 10;
-
- /* Set the reference clock to sysclk reference */
- pi->refsel = HDMI_REFSEL_SYSCLK;
-
- DSSDBG("M = %d Mf = %d\n", pi->regm, pi->regmf);
- DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd);
-}
-
static int hdmi_power_on_core(struct omap_dss_device *dssdev)
{
int r;
@@ -526,12 +476,12 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
phy = p->pixel_clock;
- hdmi_compute_pll(dssdev, phy, &hdmi.ip_data.pll_data);
+ hdmi_pll_compute(&hdmi.ip_data.pll, clk_get_rate(hdmi.sys_clk), phy);
hdmi_wp_video_stop(&hdmi.ip_data.wp);
/* config the PLL and PHY hdmi_set_pll_pwrfirst */
- r = hdmi.ip_data.ops->pll_enable(&hdmi.ip_data);
+ r = hdmi_pll_enable(&hdmi.ip_data.pll, &hdmi.ip_data.wp);
if (r) {
DSSDBG("Failed to lock PLL\n");
goto err_pll_enable;
@@ -566,7 +516,7 @@ err_mgr_enable:
err_vid_enable:
hdmi.ip_data.ops->phy_disable(&hdmi.ip_data);
err_phy_enable:
- hdmi.ip_data.ops->pll_disable(&hdmi.ip_data);
+ hdmi_pll_disable(&hdmi.ip_data.pll, &hdmi.ip_data.wp);
err_pll_enable:
hdmi_power_off_core(dssdev);
return -EIO;
@@ -580,7 +530,7 @@ static void hdmi_power_off_full(struct omap_dss_device *dssdev)
hdmi_wp_video_stop(&hdmi.ip_data.wp);
hdmi.ip_data.ops->phy_disable(&hdmi.ip_data);
- hdmi.ip_data.ops->pll_disable(&hdmi.ip_data);
+ hdmi_pll_disable(&hdmi.ip_data.pll, &hdmi.ip_data.wp);
hdmi_power_off_core(dssdev);
}
@@ -642,7 +592,7 @@ static void hdmi_dump_regs(struct seq_file *s)
}
hdmi_wp_dump(&hdmi.ip_data.wp, s);
- hdmi.ip_data.ops->dump_pll(&hdmi.ip_data, s);
+ hdmi_pll_dump(&hdmi.ip_data.pll, s);
hdmi.ip_data.ops->dump_phy(&hdmi.ip_data, s);
hdmi.ip_data.ops->dump_core(&hdmi.ip_data, s);
@@ -1095,6 +1045,10 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
if (r)
return r;
+ r = hdmi_pll_init(pdev, &hdmi.ip_data.pll);
+ if (r)
+ return r;
+
hdmi.ip_data.irq = platform_get_irq(pdev, 0);
if (hdmi.ip_data.irq < 0) {
DSSERR("platform_get_irq failed\n");
@@ -1111,7 +1065,6 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
hdmi.ip_data.core_sys_offset = HDMI_CORE_SYS;
hdmi.ip_data.core_av_offset = HDMI_CORE_AV;
- hdmi.ip_data.pll_offset = HDMI_PLLCTRL;
hdmi.ip_data.phy_offset = HDMI_PHY;
hdmi_init_output(pdev);
diff --git a/drivers/video/omap2/dss/hdmi_pll.c b/drivers/video/omap2/dss/hdmi_pll.c
new file mode 100644
index 0000000..d53b8e2
--- /dev/null
+++ b/drivers/video/omap2/dss/hdmi_pll.c
@@ -0,0 +1,243 @@
+/*
+ * HDMI PLL
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <video/omapdss.h>
+
+#include "dss.h"
+#include "ti_hdmi.h"
+#include "ti_hdmi_4xxx_ip.h"
+
+#define HDMI_DEFAULT_REGN 16
+#define HDMI_DEFAULT_REGM2 1
+
+static inline void hdmi_write_reg(void __iomem *base_addr, const u16 idx,
+ u32 val)
+{
+ __raw_writel(val, base_addr + idx);
+}
+
+static inline u32 hdmi_read_reg(void __iomem *base_addr, const u16 idx)
+{
+ return __raw_readl(base_addr + idx);
+}
+
+#define REG_FLD_MOD(base, idx, val, start, end) \
+ hdmi_write_reg(base, idx, FLD_MOD(hdmi_read_reg(base, idx),\
+ val, start, end))
+#define REG_GET(base, idx, start, end) \
+ FLD_GET(hdmi_read_reg(base, idx), start, end)
+
+static inline int hdmi_wait_for_bit_change(void __iomem *base_addr,
+ const u16 idx, int b2, int b1, u32 val)
+{
+ u32 t = 0;
+ while (val != REG_GET(base_addr, idx, b2, b1)) {
+ udelay(1);
+ if (t++ > 10000)
+ return !val;
+ }
+ return val;
+}
+
+void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s)
+{
+#define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\
+ hdmi_read_reg(pll->base, r))
+
+ DUMPPLL(PLLCTRL_PLL_CONTROL);
+ DUMPPLL(PLLCTRL_PLL_STATUS);
+ DUMPPLL(PLLCTRL_PLL_GO);
+ DUMPPLL(PLLCTRL_CFG1);
+ DUMPPLL(PLLCTRL_CFG2);
+ DUMPPLL(PLLCTRL_CFG3);
+ DUMPPLL(PLLCTRL_SSC_CFG1);
+ DUMPPLL(PLLCTRL_SSC_CFG2);
+ DUMPPLL(PLLCTRL_CFG4);
+}
+
+void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy)
+{
+ struct hdmi_pll_info *pi = &pll->info;
+ unsigned long refclk;
+ u32 mf;
+
+ /* use our funky units */
+ clkin /= 10000;
+
+ /*
+ * Input clock is predivided by N + 1
+ * out put of which is reference clk
+ */
+
+ pi->regn = HDMI_DEFAULT_REGN;
+
+ refclk = clkin / pi->regn;
+
+ pi->regm2 = HDMI_DEFAULT_REGM2;
+
+ /*
+ * multiplier is pixel_clk/ref_clk
+ * Multiplying by 100 to avoid fractional part removal
+ */
+ pi->regm = phy * pi->regm2 / refclk;
+
+ /*
+ * fractional multiplier is remainder of the difference between
+ * multiplier and actual phy(required pixel clock thus should be
+ * multiplied by 2^18(262144) divided by the reference clock
+ */
+ mf = (phy - pi->regm / pi->regm2 * refclk) * 262144;
+ pi->regmf = pi->regm2 * mf / refclk;
+
+ /*
+ * Dcofreq should be set to 1 if required pixel clock
+ * is greater than 1000MHz
+ */
+ pi->dcofreq = phy > 1000 * 100;
+ pi->regsd = ((pi->regm * clkin / 10) / (pi->regn * 250) + 5) / 10;
+
+ /* Set the reference clock to sysclk reference */
+ pi->refsel = HDMI_REFSEL_SYSCLK;
+
+ DSSDBG("M = %d Mf = %d\n", pi->regm, pi->regmf);
+ DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd);
+}
+
+
+static int hdmi_pll_config(struct hdmi_pll_data *pll)
+{
+ u32 r;
+ struct hdmi_pll_info *fmt = &pll->info;
+
+ /* PLL start always use manual mode */
+ REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, 0x0, 0, 0);
+
+ r = hdmi_read_reg(pll->base, PLLCTRL_CFG1);
+ r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1_PLL_REGM */
+ r = FLD_MOD(r, fmt->regn - 1, 8, 1); /* CFG1_PLL_REGN */
+ hdmi_write_reg(pll->base, PLLCTRL_CFG1, r);
+
+ r = hdmi_read_reg(pll->base, PLLCTRL_CFG2);
+
+ r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */
+ r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */
+ r = FLD_MOD(r, 0x0, 14, 14); /* PHY_CLKINEN de-assert during locking */
+ r = FLD_MOD(r, fmt->refsel, 22, 21); /* REFSEL */
+
+ if (fmt->dcofreq) {
+ /* divider programming for frequency beyond 1000Mhz */
+ REG_FLD_MOD(pll->base, PLLCTRL_CFG3, fmt->regsd, 17, 10);
+ r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */
+ } else {
+ r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */
+ }
+
+ hdmi_write_reg(pll->base, PLLCTRL_CFG2, r);
+
+ r = hdmi_read_reg(pll->base, PLLCTRL_CFG4);
+ r = FLD_MOD(r, fmt->regm2, 24, 18);
+ r = FLD_MOD(r, fmt->regmf, 17, 0);
+ hdmi_write_reg(pll->base, PLLCTRL_CFG4, r);
+
+ /* go now */
+ REG_FLD_MOD(pll->base, PLLCTRL_PLL_GO, 0x1, 0, 0);
+
+ /* wait for bit change */
+ if (hdmi_wait_for_bit_change(pll->base, PLLCTRL_PLL_GO,
+ 0, 0, 1) != 1) {
+ pr_err("PLL GO bit not set\n");
+ return -ETIMEDOUT;
+ }
+
+ /* Wait till the lock bit is set in PLL status */
+ if (hdmi_wait_for_bit_change(pll->base,
+ PLLCTRL_PLL_STATUS, 1, 1, 1) != 1) {
+ pr_err("cannot lock PLL\n");
+ pr_err("CFG1 0x%x\n",
+ hdmi_read_reg(pll->base, PLLCTRL_CFG1));
+ pr_err("CFG2 0x%x\n",
+ hdmi_read_reg(pll->base, PLLCTRL_CFG2));
+ pr_err("CFG4 0x%x\n",
+ hdmi_read_reg(pll->base, PLLCTRL_CFG4));
+ return -ETIMEDOUT;
+ }
+
+ pr_debug("PLL locked!\n");
+
+ return 0;
+}
+
+static int hdmi_pll_reset(struct hdmi_pll_data *pll)
+{
+ /* SYSRESET controlled by power FSM */
+ REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, 0x0, 3, 3);
+
+ /* READ 0x0 reset is in progress */
+ if (hdmi_wait_for_bit_change(pll->base, PLLCTRL_PLL_STATUS, 0, 0, 1)
+ != 1) {
+ pr_err("Failed to sysreset PLL\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+int hdmi_pll_enable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp)
+{
+ u16 r = 0;
+
+ r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF);
+ if (r)
+ return r;
+
+ r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS);
+ if (r)
+ return r;
+
+ r = hdmi_pll_reset(pll);
+ if (r)
+ return r;
+
+ r = hdmi_pll_config(pll);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+void hdmi_pll_disable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp)
+{
+ hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF);
+}
+
+int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll)
+{
+ struct resource *res;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi_pllctrl");
+ if (!res) {
+ DSSERR("can't get PLL CTRL IORESOURCE_MEM HDMI\n");
+ return -EINVAL;
+ }
+
+ pll->base = devm_request_and_ioremap(&pdev->dev, res);
+ if (!pll->base) {
+ DSSERR("can't ioremap PLL ctrl\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h
index d16f28d..62a83c7 100644
--- a/drivers/video/omap2/dss/ti_hdmi.h
+++ b/drivers/video/omap2/dss/ti_hdmi.h
@@ -155,14 +155,8 @@ struct ti_hdmi_ip_ops {
int (*read_edid)(struct hdmi_ip_data *ip_data, u8 *edid, int len);
- int (*pll_enable)(struct hdmi_ip_data *ip_data);
-
- void (*pll_disable)(struct hdmi_ip_data *ip_data);
-
void (*dump_core)(struct hdmi_ip_data *ip_data, struct seq_file *s);
- void (*dump_pll)(struct hdmi_ip_data *ip_data, struct seq_file *s);
-
void (*dump_phy)(struct hdmi_ip_data *ip_data, struct seq_file *s);
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
@@ -223,17 +217,22 @@ struct hdmi_wp_data {
void __iomem *base;
};
+struct hdmi_pll_data {
+ void __iomem *base;
+
+ struct hdmi_pll_info info;
+};
+
struct hdmi_ip_data {
struct hdmi_wp_data wp;
+ struct hdmi_pll_data pll;
unsigned long core_sys_offset;
unsigned long core_av_offset;
- unsigned long pll_offset;
unsigned long phy_offset;
int irq;
const struct ti_hdmi_ip_ops *ops;
struct hdmi_config cfg;
- struct hdmi_pll_info pll_data;
struct hdmi_core_infoframe_avi avi_cfg;
/* ti_hdmi_4xxx_ip private data. These should be in a separate struct */
@@ -260,13 +259,17 @@ void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt,
struct omap_video_timings *timings, struct hdmi_config *param);
int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp);
+/* HDMI PLL funcs */
+int hdmi_pll_enable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp);
+void hdmi_pll_disable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp);
+void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s);
+void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy);
+int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll);
+
int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data);
void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data);
int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data, u8 *edid, int len);
-int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data);
-void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data);
void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data);
-void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
index d4b8883..8cfb54b 100644
--- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
+++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
@@ -75,11 +75,6 @@ static inline void __iomem *hdmi_phy_base(struct hdmi_ip_data *ip_data)
return ip_data->wp.base + ip_data->phy_offset;
}
-static inline void __iomem *hdmi_pll_base(struct hdmi_ip_data *ip_data)
-{
- return ip_data->wp.base + ip_data->pll_offset;
-}
-
static inline void __iomem *hdmi_av_base(struct hdmi_ip_data *ip_data)
{
return ip_data->wp.base + ip_data->core_av_offset;
@@ -90,117 +85,6 @@ static inline void __iomem *hdmi_core_sys_base(struct hdmi_ip_data *ip_data)
return ip_data->wp.base + ip_data->core_sys_offset;
}
-
-static int hdmi_pll_init(struct hdmi_ip_data *ip_data)
-{
- u32 r;
- void __iomem *pll_base = hdmi_pll_base(ip_data);
- struct hdmi_pll_info *fmt = &ip_data->pll_data;
-
- /* PLL start always use manual mode */
- REG_FLD_MOD(pll_base, PLLCTRL_PLL_CONTROL, 0x0, 0, 0);
-
- r = hdmi_read_reg(pll_base, PLLCTRL_CFG1);
- r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1_PLL_REGM */
- r = FLD_MOD(r, fmt->regn - 1, 8, 1); /* CFG1_PLL_REGN */
-
- hdmi_write_reg(pll_base, PLLCTRL_CFG1, r);
-
- r = hdmi_read_reg(pll_base, PLLCTRL_CFG2);
-
- r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */
- r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */
- r = FLD_MOD(r, 0x0, 14, 14); /* PHY_CLKINEN de-assert during locking */
- r = FLD_MOD(r, fmt->refsel, 22, 21); /* REFSEL */
-
- if (fmt->dcofreq) {
- /* divider programming for frequency beyond 1000Mhz */
- REG_FLD_MOD(pll_base, PLLCTRL_CFG3, fmt->regsd, 17, 10);
- r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */
- } else {
- r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */
- }
-
- hdmi_write_reg(pll_base, PLLCTRL_CFG2, r);
-
- r = hdmi_read_reg(pll_base, PLLCTRL_CFG4);
- r = FLD_MOD(r, fmt->regm2, 24, 18);
- r = FLD_MOD(r, fmt->regmf, 17, 0);
-
- hdmi_write_reg(pll_base, PLLCTRL_CFG4, r);
-
- /* go now */
- REG_FLD_MOD(pll_base, PLLCTRL_PLL_GO, 0x1, 0, 0);
-
- /* wait for bit change */
- if (hdmi_wait_for_bit_change(pll_base, PLLCTRL_PLL_GO,
- 0, 0, 1) != 1) {
- pr_err("PLL GO bit not set\n");
- return -ETIMEDOUT;
- }
-
- /* Wait till the lock bit is set in PLL status */
- if (hdmi_wait_for_bit_change(pll_base,
- PLLCTRL_PLL_STATUS, 1, 1, 1) != 1) {
- pr_err("cannot lock PLL\n");
- pr_err("CFG1 0x%x\n",
- hdmi_read_reg(pll_base, PLLCTRL_CFG1));
- pr_err("CFG2 0x%x\n",
- hdmi_read_reg(pll_base, PLLCTRL_CFG2));
- pr_err("CFG4 0x%x\n",
- hdmi_read_reg(pll_base, PLLCTRL_CFG4));
- return -ETIMEDOUT;
- }
-
- pr_debug("PLL locked!\n");
-
- return 0;
-}
-
-
-static int hdmi_pll_reset(struct hdmi_ip_data *ip_data)
-{
- /* SYSRESET controlled by power FSM */
- REG_FLD_MOD(hdmi_pll_base(ip_data), PLLCTRL_PLL_CONTROL, 0x0, 3, 3);
-
- /* READ 0x0 reset is in progress */
- if (hdmi_wait_for_bit_change(hdmi_pll_base(ip_data),
- PLLCTRL_PLL_STATUS, 0, 0, 1) != 1) {
- pr_err("Failed to sysreset PLL\n");
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data)
-{
- u16 r = 0;
-
- r = hdmi_wp_set_pll_pwr(&ip_data->wp, HDMI_PLLPWRCMD_ALLOFF);
- if (r)
- return r;
-
- r = hdmi_wp_set_pll_pwr(&ip_data->wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS);
- if (r)
- return r;
-
- r = hdmi_pll_reset(ip_data);
- if (r)
- return r;
-
- r = hdmi_pll_init(ip_data);
- if (r)
- return r;
-
- return 0;
-}
-
-void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data)
-{
- hdmi_wp_set_pll_pwr(&ip_data->wp, HDMI_PLLPWRCMD_ALLOFF);
-}
-
static irqreturn_t hdmi_irq_handler(int irq, void *data)
{
struct hdmi_ip_data *ip_data = data;
@@ -717,22 +601,6 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data)
hdmi_core_av_packet_config(ip_data, repeat_cfg);
}
-void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
-{
-#define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\
- hdmi_read_reg(hdmi_pll_base(ip_data), r))
-
- DUMPPLL(PLLCTRL_PLL_CONTROL);
- DUMPPLL(PLLCTRL_PLL_STATUS);
- DUMPPLL(PLLCTRL_PLL_GO);
- DUMPPLL(PLLCTRL_CFG1);
- DUMPPLL(PLLCTRL_CFG2);
- DUMPPLL(PLLCTRL_CFG3);
- DUMPPLL(PLLCTRL_SSC_CFG1);
- DUMPPLL(PLLCTRL_SSC_CFG2);
- DUMPPLL(PLLCTRL_CFG4);
-}
-
void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
{
int i;
--
1.8.1.2
^ permalink raw reply related
* [PATCH 01/11] omapdss: HDMI: create a hdmi wrapper library
From: Archit Taneja @ 2013-09-17 7:18 UTC (permalink / raw)
To: tomi.valkeinen; +Cc: linux-omap, linux-fbdev, Archit Taneja
In-Reply-To: <1379401597-27222-1-git-send-email-archit@ti.com>
HDMI wrapper is a block common to DSS in OMAP4, OMAP5 and DRA7x. Move the
existing functions from ti_hdmi_4xxx_ip.c to a separate file. These funcs are
called directly from the hdmi driver rather than hdmi_ip_ops funtion pointer
calls.
Add new wrapper funcs which can be used by other hdmi libraries like core, pll
and phy. Move some of the enums, structs and funcs related to the wrapper from
ti_hdmi_4xxx_ip.h to ti_hdmi.h. These will be shared amongst the omap4/5 hdmi
platform drivers and other libraries.
The old hdmi_wp_init() is removed since it didn't do anything. Timing parameters
like interlace, hsync_level and vsync_level weren't copied correctly before.
Those are copied correctly now.
Signed-off-by: Archit Taneja <archit@ti.com>
---
drivers/video/omap2/dss/Makefile | 2 +-
drivers/video/omap2/dss/dss_features.c | 5 -
drivers/video/omap2/dss/hdmi.c | 26 +--
drivers/video/omap2/dss/hdmi_wp.c | 285 +++++++++++++++++++++++++++
drivers/video/omap2/dss/ti_hdmi.h | 131 +++++++++++--
drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 308 +++++-------------------------
drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h | 85 ---------
7 files changed, 462 insertions(+), 380 deletions(-)
create mode 100644 drivers/video/omap2/dss/hdmi_wp.c
diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile
index 94832eb..56ce6bd 100644
--- a/drivers/video/omap2/dss/Makefile
+++ b/drivers/video/omap2/dss/Makefile
@@ -10,5 +10,5 @@ omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o
omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o
omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o
omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o
-omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o ti_hdmi_4xxx_ip.o
+omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o hdmi_wp.o ti_hdmi_4xxx_ip.o
ccflags-$(CONFIG_OMAP2_DSS_DEBUG) += -DDEBUG
diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c
index b9cfebb..db359e8 100644
--- a/drivers/video/omap2/dss/dss_features.c
+++ b/drivers/video/omap2/dss/dss_features.c
@@ -799,15 +799,10 @@ static const struct ti_hdmi_ip_ops omap4_hdmi_functions = {
.read_edid = ti_hdmi_4xxx_read_edid,
.pll_enable = ti_hdmi_4xxx_pll_enable,
.pll_disable = ti_hdmi_4xxx_pll_disable,
- .video_enable = ti_hdmi_4xxx_wp_video_start,
- .video_disable = ti_hdmi_4xxx_wp_video_stop,
- .dump_wrapper = ti_hdmi_4xxx_wp_dump,
.dump_core = ti_hdmi_4xxx_core_dump,
.dump_pll = ti_hdmi_4xxx_pll_dump,
.dump_phy = ti_hdmi_4xxx_phy_dump,
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
- .audio_enable = ti_hdmi_4xxx_wp_audio_enable,
- .audio_disable = ti_hdmi_4xxx_wp_audio_disable,
.audio_start = ti_hdmi_4xxx_audio_start,
.audio_stop = ti_hdmi_4xxx_audio_stop,
.audio_config = ti_hdmi_4xxx_audio_config,
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
index 82a9640..f2475fc 100644
--- a/drivers/video/omap2/dss/hdmi.c
+++ b/drivers/video/omap2/dss/hdmi.c
@@ -40,7 +40,6 @@
#include "dss.h"
#include "dss_features.h"
-#define HDMI_WP 0x0
#define HDMI_CORE_SYS 0x400
#define HDMI_CORE_AV 0x900
#define HDMI_PLLCTRL 0x200
@@ -529,7 +528,7 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
hdmi_compute_pll(dssdev, phy, &hdmi.ip_data.pll_data);
- hdmi.ip_data.ops->video_disable(&hdmi.ip_data);
+ hdmi_wp_video_stop(&hdmi.ip_data.wp);
/* config the PLL and PHY hdmi_set_pll_pwrfirst */
r = hdmi.ip_data.ops->pll_enable(&hdmi.ip_data);
@@ -552,7 +551,7 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
/* tv size */
dss_mgr_set_timings(mgr, p);
- r = hdmi.ip_data.ops->video_enable(&hdmi.ip_data);
+ r = hdmi_wp_video_start(&hdmi.ip_data.wp);
if (r)
goto err_vid_enable;
@@ -563,7 +562,7 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
return 0;
err_mgr_enable:
- hdmi.ip_data.ops->video_disable(&hdmi.ip_data);
+ hdmi_wp_video_stop(&hdmi.ip_data.wp);
err_vid_enable:
hdmi.ip_data.ops->phy_disable(&hdmi.ip_data);
err_phy_enable:
@@ -579,7 +578,7 @@ static void hdmi_power_off_full(struct omap_dss_device *dssdev)
dss_mgr_disable(mgr);
- hdmi.ip_data.ops->video_disable(&hdmi.ip_data);
+ hdmi_wp_video_stop(&hdmi.ip_data.wp);
hdmi.ip_data.ops->phy_disable(&hdmi.ip_data);
hdmi.ip_data.ops->pll_disable(&hdmi.ip_data);
@@ -642,7 +641,7 @@ static void hdmi_dump_regs(struct seq_file *s)
return;
}
- hdmi.ip_data.ops->dump_wrapper(&hdmi.ip_data, s);
+ hdmi_wp_dump(&hdmi.ip_data.wp, s);
hdmi.ip_data.ops->dump_pll(&hdmi.ip_data, s);
hdmi.ip_data.ops->dump_phy(&hdmi.ip_data, s);
hdmi.ip_data.ops->dump_core(&hdmi.ip_data, s);
@@ -946,8 +945,7 @@ static int hdmi_audio_enable(struct omap_dss_device *dssdev)
goto err;
}
-
- r = hdmi.ip_data.ops->audio_enable(&hdmi.ip_data);
+ r = hdmi_wp_audio_enable(&hdmi.ip_data.wp, true);
if (r)
goto err;
@@ -961,7 +959,7 @@ err:
static void hdmi_audio_disable(struct omap_dss_device *dssdev)
{
- hdmi.ip_data.ops->audio_disable(&hdmi.ip_data);
+ hdmi_wp_audio_enable(&hdmi.ip_data.wp, false);
}
static int hdmi_audio_start(struct omap_dss_device *dssdev)
@@ -1086,7 +1084,6 @@ static void __exit hdmi_uninit_output(struct platform_device *pdev)
/* HDMI HW IP initialisation */
static int omapdss_hdmihw_probe(struct platform_device *pdev)
{
- struct resource *res;
int r;
hdmi.pdev = pdev;
@@ -1094,12 +1091,9 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
mutex_init(&hdmi.lock);
mutex_init(&hdmi.ip_data.lock);
- res = platform_get_resource(hdmi.pdev, IORESOURCE_MEM, 0);
-
- /* Base address taken from platform */
- hdmi.ip_data.base_wp = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(hdmi.ip_data.base_wp))
- return PTR_ERR(hdmi.ip_data.base_wp);
+ r = hdmi_wp_init(pdev, &hdmi.ip_data.wp);
+ if (r)
+ return r;
hdmi.ip_data.irq = platform_get_irq(pdev, 0);
if (hdmi.ip_data.irq < 0) {
diff --git a/drivers/video/omap2/dss/hdmi_wp.c b/drivers/video/omap2/dss/hdmi_wp.c
new file mode 100644
index 0000000..482940a
--- /dev/null
+++ b/drivers/video/omap2/dss/hdmi_wp.c
@@ -0,0 +1,285 @@
+/*
+ * HDMI wrapper
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <video/omapdss.h>
+
+#include "dss.h"
+#include "ti_hdmi.h"
+#include "ti_hdmi_4xxx_ip.h"
+
+static inline void hdmi_write_reg(void __iomem *base_addr, const u16 idx,
+ u32 val)
+{
+ __raw_writel(val, base_addr + idx);
+}
+
+static inline u32 hdmi_read_reg(void __iomem *base_addr, const u16 idx)
+{
+ return __raw_readl(base_addr + idx);
+}
+
+#define REG_FLD_MOD(base, idx, val, start, end) \
+ hdmi_write_reg(base, idx, FLD_MOD(hdmi_read_reg(base, idx),\
+ val, start, end))
+#define REG_GET(base, idx, start, end) \
+ FLD_GET(hdmi_read_reg(base, idx), start, end)
+
+static inline int hdmi_wait_for_bit_change(void __iomem *base_addr,
+ const u16 idx, int b2, int b1, u32 val)
+{
+ u32 t = 0;
+ while (val != REG_GET(base_addr, idx, b2, b1)) {
+ udelay(1);
+ if (t++ > 10000)
+ return !val;
+ }
+ return val;
+}
+
+void hdmi_wp_dump(struct hdmi_wp_data *wp, struct seq_file *s)
+{
+#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, hdmi_read_reg(wp->base, r))
+
+ DUMPREG(HDMI_WP_REVISION);
+ DUMPREG(HDMI_WP_SYSCONFIG);
+ DUMPREG(HDMI_WP_IRQSTATUS_RAW);
+ DUMPREG(HDMI_WP_IRQSTATUS);
+ DUMPREG(HDMI_WP_IRQENABLE_SET);
+ DUMPREG(HDMI_WP_IRQENABLE_CLR);
+ DUMPREG(HDMI_WP_IRQWAKEEN);
+ DUMPREG(HDMI_WP_PWR_CTRL);
+ DUMPREG(HDMI_WP_DEBOUNCE);
+ DUMPREG(HDMI_WP_VIDEO_CFG);
+ DUMPREG(HDMI_WP_VIDEO_SIZE);
+ DUMPREG(HDMI_WP_VIDEO_TIMING_H);
+ DUMPREG(HDMI_WP_VIDEO_TIMING_V);
+ DUMPREG(HDMI_WP_WP_CLK);
+ DUMPREG(HDMI_WP_AUDIO_CFG);
+ DUMPREG(HDMI_WP_AUDIO_CFG2);
+ DUMPREG(HDMI_WP_AUDIO_CTRL);
+ DUMPREG(HDMI_WP_AUDIO_DATA);
+}
+
+u32 hdmi_wp_get_irqstatus(struct hdmi_wp_data *wp)
+{
+ return hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS);
+}
+
+void hdmi_wp_set_irqstatus(struct hdmi_wp_data *wp, u32 irqstatus)
+{
+ hdmi_write_reg(wp->base, HDMI_WP_IRQSTATUS, irqstatus);
+ /* flush posted write */
+ hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS);
+}
+
+void hdmi_wp_set_irqenable(struct hdmi_wp_data *wp, u32 mask)
+{
+ hdmi_write_reg(wp->base, HDMI_WP_IRQENABLE_SET, mask);
+}
+
+void hdmi_wp_clear_irqenable(struct hdmi_wp_data *wp, u32 mask)
+{
+ hdmi_write_reg(wp->base, HDMI_WP_IRQENABLE_CLR, mask);
+}
+
+/* PHY_PWR_CMD */
+int hdmi_wp_set_phy_pwr(struct hdmi_wp_data *wp, enum hdmi_phy_pwr val)
+{
+ /* Return if already the state */
+ if (REG_GET(wp->base, HDMI_WP_PWR_CTRL, 5, 4) = val)
+ return 0;
+
+ /* Command for power control of HDMI PHY */
+ REG_FLD_MOD(wp->base, HDMI_WP_PWR_CTRL, val, 7, 6);
+
+ /* Status of the power control of HDMI PHY */
+ if (hdmi_wait_for_bit_change(wp->base, HDMI_WP_PWR_CTRL, 5, 4, val)
+ != val) {
+ pr_err("Failed to set PHY power mode to %d\n", val);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+/* PLL_PWR_CMD */
+int hdmi_wp_set_pll_pwr(struct hdmi_wp_data *wp, enum hdmi_pll_pwr val)
+{
+ /* Command for power control of HDMI PLL */
+ REG_FLD_MOD(wp->base, HDMI_WP_PWR_CTRL, val, 3, 2);
+
+ /* wait till PHY_PWR_STATUS is set */
+ if (hdmi_wait_for_bit_change(wp->base, HDMI_WP_PWR_CTRL, 1, 0, val)
+ != val) {
+ pr_err("Failed to set PLL_PWR_STATUS\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+int hdmi_wp_video_start(struct hdmi_wp_data *wp)
+{
+ REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, true, 31, 31);
+
+ return 0;
+}
+
+void hdmi_wp_video_stop(struct hdmi_wp_data *wp)
+{
+ REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, false, 31, 31);
+}
+
+void hdmi_wp_video_config_format(struct hdmi_wp_data *wp,
+ struct hdmi_video_format *video_fmt)
+{
+ u32 l = 0;
+
+ REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, video_fmt->packing_mode,
+ 10, 8);
+
+ l |= FLD_VAL(video_fmt->y_res, 31, 16);
+ l |= FLD_VAL(video_fmt->x_res, 15, 0);
+ hdmi_write_reg(wp->base, HDMI_WP_VIDEO_SIZE, l);
+}
+
+void hdmi_wp_video_config_interface(struct hdmi_wp_data *wp,
+ struct omap_video_timings *timings)
+{
+ u32 r;
+ bool vsync_pol, hsync_pol;
+ pr_debug("Enter hdmi_wp_video_config_interface\n");
+
+ vsync_pol = timings->vsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
+ hsync_pol = timings->hsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
+
+ r = hdmi_read_reg(wp->base, HDMI_WP_VIDEO_CFG);
+ r = FLD_MOD(r, vsync_pol, 7, 7);
+ r = FLD_MOD(r, hsync_pol, 6, 6);
+ r = FLD_MOD(r, timings->interlace, 3, 3);
+ r = FLD_MOD(r, 1, 1, 0); /* HDMI_TIMING_MASTER_24BIT */
+ hdmi_write_reg(wp->base, HDMI_WP_VIDEO_CFG, r);
+}
+
+void hdmi_wp_video_config_timing(struct hdmi_wp_data *wp,
+ struct omap_video_timings *timings)
+{
+ u32 timing_h = 0;
+ u32 timing_v = 0;
+
+ pr_debug("Enter hdmi_wp_video_config_timing\n");
+
+ timing_h |= FLD_VAL(timings->hbp, 31, 20);
+ timing_h |= FLD_VAL(timings->hfp, 19, 8);
+ timing_h |= FLD_VAL(timings->hsw, 7, 0);
+ hdmi_write_reg(wp->base, HDMI_WP_VIDEO_TIMING_H, timing_h);
+
+ timing_v |= FLD_VAL(timings->vbp, 31, 20);
+ timing_v |= FLD_VAL(timings->vfp, 19, 8);
+ timing_v |= FLD_VAL(timings->vsw, 7, 0);
+ hdmi_write_reg(wp->base, HDMI_WP_VIDEO_TIMING_V, timing_v);
+}
+
+void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt,
+ struct omap_video_timings *timings, struct hdmi_config *param)
+{
+ pr_debug("Enter hdmi_wp_video_init_format\n");
+
+ video_fmt->packing_mode = HDMI_PACK_10b_RGB_YUV444;
+ video_fmt->y_res = param->timings.y_res;
+ video_fmt->x_res = param->timings.x_res;
+
+ timings->hbp = param->timings.hbp;
+ timings->hfp = param->timings.hfp;
+ timings->hsw = param->timings.hsw;
+ timings->vbp = param->timings.vbp;
+ timings->vfp = param->timings.vfp;
+ timings->vsw = param->timings.vsw;
+ timings->vsync_level = param->timings.vsync_level;
+ timings->hsync_level = param->timings.hsync_level;
+ timings->interlace = param->timings.interlace;
+}
+
+#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
+void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp,
+ struct hdmi_audio_format *aud_fmt)
+{
+ u32 r;
+
+ DSSDBG("Enter hdmi_wp_audio_config_format\n");
+
+ r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CFG);
+ r = FLD_MOD(r, aud_fmt->stereo_channels, 26, 24);
+ r = FLD_MOD(r, aud_fmt->active_chnnls_msk, 23, 16);
+ r = FLD_MOD(r, aud_fmt->en_sig_blk_strt_end, 5, 5);
+ r = FLD_MOD(r, aud_fmt->type, 4, 4);
+ r = FLD_MOD(r, aud_fmt->justification, 3, 3);
+ r = FLD_MOD(r, aud_fmt->sample_order, 2, 2);
+ r = FLD_MOD(r, aud_fmt->samples_per_word, 1, 1);
+ r = FLD_MOD(r, aud_fmt->sample_size, 0, 0);
+ hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CFG, r);
+}
+
+void hdmi_wp_audio_config_dma(struct hdmi_wp_data *wp,
+ struct hdmi_audio_dma *aud_dma)
+{
+ u32 r;
+
+ DSSDBG("Enter hdmi_wp_audio_config_dma\n");
+
+ r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CFG2);
+ r = FLD_MOD(r, aud_dma->transfer_size, 15, 8);
+ r = FLD_MOD(r, aud_dma->block_size, 7, 0);
+ hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CFG2, r);
+
+ r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CTRL);
+ r = FLD_MOD(r, aud_dma->mode, 9, 9);
+ r = FLD_MOD(r, aud_dma->fifo_threshold, 8, 0);
+ hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CTRL, r);
+}
+
+int hdmi_wp_audio_enable(struct hdmi_wp_data *wp, bool enable)
+{
+ REG_FLD_MOD(wp->base, HDMI_WP_AUDIO_CTRL, enable, 31, 31);
+
+ return 0;
+}
+
+int hdmi_wp_audio_core_req_enable(struct hdmi_wp_data *wp, bool enable)
+{
+ REG_FLD_MOD(wp->base, HDMI_WP_AUDIO_CTRL, enable, 30, 30);
+
+ return 0;
+}
+#endif
+
+int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp)
+{
+ struct resource *res;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi_wp");
+ if (!res) {
+ DSSERR("can't get WP IORESOURCE_MEM HDMI\n");
+ return -EINVAL;
+ }
+
+ wp->base = devm_request_and_ioremap(&pdev->dev, res);
+ if (!wp->base) {
+ DSSERR("can't ioremap HDMI wrapper\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h
index 45215f4..d16f28d 100644
--- a/drivers/video/omap2/dss/ti_hdmi.h
+++ b/drivers/video/omap2/dss/ti_hdmi.h
@@ -21,6 +21,8 @@
#ifndef _TI_HDMI_H
#define _TI_HDMI_H
+#include <linux/platform_device.h>
+
struct hdmi_ip_data;
enum hdmi_pll_pwr {
@@ -30,6 +32,12 @@ enum hdmi_pll_pwr {
HDMI_PLLPWRCMD_BOTHON_NOPHYCLK = 3
};
+enum hdmi_phy_pwr {
+ HDMI_PHYPWRCMD_OFF = 0,
+ HDMI_PHYPWRCMD_LDOON = 1,
+ HDMI_PHYPWRCMD_TXON = 2
+};
+
enum hdmi_core_hdmi_dvi {
HDMI_DVI = 0,
HDMI_HDMI = 1
@@ -42,11 +50,67 @@ enum hdmi_clk_refsel {
HDMI_REFSEL_SYSCLK = 3
};
+enum hdmi_packing_mode {
+ HDMI_PACK_10b_RGB_YUV444 = 0,
+ HDMI_PACK_24b_RGB_YUV444_YUV422 = 1,
+ HDMI_PACK_20b_YUV422 = 2,
+ HDMI_PACK_ALREADYPACKED = 7
+};
+
+enum hdmi_stereo_channels {
+ HDMI_AUDIO_STEREO_NOCHANNELS = 0,
+ HDMI_AUDIO_STEREO_ONECHANNEL = 1,
+ HDMI_AUDIO_STEREO_TWOCHANNELS = 2,
+ HDMI_AUDIO_STEREO_THREECHANNELS = 3,
+ HDMI_AUDIO_STEREO_FOURCHANNELS = 4
+};
+
+enum hdmi_audio_type {
+ HDMI_AUDIO_TYPE_LPCM = 0,
+ HDMI_AUDIO_TYPE_IEC = 1
+};
+
+enum hdmi_audio_justify {
+ HDMI_AUDIO_JUSTIFY_LEFT = 0,
+ HDMI_AUDIO_JUSTIFY_RIGHT = 1
+};
+
+enum hdmi_audio_sample_order {
+ HDMI_AUDIO_SAMPLE_RIGHT_FIRST = 0,
+ HDMI_AUDIO_SAMPLE_LEFT_FIRST = 1
+};
+
+enum hdmi_audio_samples_perword {
+ HDMI_AUDIO_ONEWORD_ONESAMPLE = 0,
+ HDMI_AUDIO_ONEWORD_TWOSAMPLES = 1
+};
+
+enum hdmi_audio_sample_size {
+ HDMI_AUDIO_SAMPLE_16BITS = 0,
+ HDMI_AUDIO_SAMPLE_24BITS = 1
+};
+
+enum hdmi_audio_transf_mode {
+ HDMI_AUDIO_TRANSF_DMA = 0,
+ HDMI_AUDIO_TRANSF_IRQ = 1
+};
+
+enum hdmi_audio_blk_strt_end_sig {
+ HDMI_AUDIO_BLOCK_SIG_STARTEND_ON = 0,
+ HDMI_AUDIO_BLOCK_SIG_STARTEND_OFF = 1
+};
+
struct hdmi_cm {
int code;
int mode;
};
+struct hdmi_video_format {
+ enum hdmi_packing_mode packing_mode;
+ u32 y_res; /* Line per panel */
+ u32 x_res; /* pixel per line */
+};
+
struct hdmi_config {
struct omap_video_timings timings;
struct hdmi_cm cm;
@@ -63,6 +127,24 @@ struct hdmi_pll_info {
enum hdmi_clk_refsel refsel;
};
+struct hdmi_audio_format {
+ enum hdmi_stereo_channels stereo_channels;
+ u8 active_chnnls_msk;
+ enum hdmi_audio_type type;
+ enum hdmi_audio_justify justification;
+ enum hdmi_audio_sample_order sample_order;
+ enum hdmi_audio_samples_perword samples_per_word;
+ enum hdmi_audio_sample_size sample_size;
+ enum hdmi_audio_blk_strt_end_sig en_sig_blk_strt_end;
+};
+
+struct hdmi_audio_dma {
+ u8 transfer_size;
+ u8 block_size;
+ enum hdmi_audio_transf_mode mode;
+ u16 fifo_threshold;
+};
+
struct ti_hdmi_ip_ops {
void (*video_configure)(struct hdmi_ip_data *ip_data);
@@ -77,12 +159,6 @@ struct ti_hdmi_ip_ops {
void (*pll_disable)(struct hdmi_ip_data *ip_data);
- int (*video_enable)(struct hdmi_ip_data *ip_data);
-
- void (*video_disable)(struct hdmi_ip_data *ip_data);
-
- void (*dump_wrapper)(struct hdmi_ip_data *ip_data, struct seq_file *s);
-
void (*dump_core)(struct hdmi_ip_data *ip_data, struct seq_file *s);
void (*dump_pll)(struct hdmi_ip_data *ip_data, struct seq_file *s);
@@ -90,10 +166,6 @@ struct ti_hdmi_ip_ops {
void (*dump_phy)(struct hdmi_ip_data *ip_data, struct seq_file *s);
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
- int (*audio_enable)(struct hdmi_ip_data *ip_data);
-
- void (*audio_disable)(struct hdmi_ip_data *ip_data);
-
int (*audio_start)(struct hdmi_ip_data *ip_data);
void (*audio_stop)(struct hdmi_ip_data *ip_data);
@@ -147,8 +219,13 @@ struct hdmi_core_infoframe_avi {
u16 db12_13_pixel_sofright;
};
+struct hdmi_wp_data {
+ void __iomem *base;
+};
+
struct hdmi_ip_data {
- void __iomem *base_wp; /* HDMI wrapper */
+ struct hdmi_wp_data wp;
+
unsigned long core_sys_offset;
unsigned long core_av_offset;
unsigned long pll_offset;
@@ -162,22 +239,44 @@ struct hdmi_ip_data {
/* ti_hdmi_4xxx_ip private data. These should be in a separate struct */
struct mutex lock;
};
+
+/* HDMI wrapper funcs */
+int hdmi_wp_video_start(struct hdmi_wp_data *wp);
+void hdmi_wp_video_stop(struct hdmi_wp_data *wp);
+void hdmi_wp_dump(struct hdmi_wp_data *wp, struct seq_file *s);
+u32 hdmi_wp_get_irqstatus(struct hdmi_wp_data *wp);
+void hdmi_wp_set_irqstatus(struct hdmi_wp_data *wp, u32 irqstatus);
+void hdmi_wp_set_irqenable(struct hdmi_wp_data *wp, u32 mask);
+void hdmi_wp_clear_irqenable(struct hdmi_wp_data *wp, u32 mask);
+int hdmi_wp_set_phy_pwr(struct hdmi_wp_data *wp, enum hdmi_phy_pwr val);
+int hdmi_wp_set_pll_pwr(struct hdmi_wp_data *wp, enum hdmi_pll_pwr val);
+void hdmi_wp_video_config_format(struct hdmi_wp_data *wp,
+ struct hdmi_video_format *video_fmt);
+void hdmi_wp_video_config_interface(struct hdmi_wp_data *wp,
+ struct omap_video_timings *timings);
+void hdmi_wp_video_config_timing(struct hdmi_wp_data *wp,
+ struct omap_video_timings *timings);
+void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt,
+ struct omap_video_timings *timings, struct hdmi_config *param);
+int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp);
+
int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data);
void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data);
int ti_hdmi_4xxx_read_edid(struct hdmi_ip_data *ip_data, u8 *edid, int len);
-int ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data);
-void ti_hdmi_4xxx_wp_video_stop(struct hdmi_ip_data *ip_data);
int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data);
void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data);
void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data);
-void ti_hdmi_4xxx_wp_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
void ti_hdmi_4xxx_core_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s);
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
int hdmi_compute_acr(u32 sample_freq, u32 *n, u32 *cts);
-int ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data);
-void ti_hdmi_4xxx_wp_audio_disable(struct hdmi_ip_data *ip_data);
+int hdmi_wp_audio_enable(struct hdmi_wp_data *wp, bool enable);
+int hdmi_wp_audio_core_req_enable(struct hdmi_wp_data *wp, bool enable);
+void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp,
+ struct hdmi_audio_format *aud_fmt);
+void hdmi_wp_audio_config_dma(struct hdmi_wp_data *wp,
+ struct hdmi_audio_dma *aud_dma);
int ti_hdmi_4xxx_audio_start(struct hdmi_ip_data *ip_data);
void ti_hdmi_4xxx_audio_stop(struct hdmi_ip_data *ip_data);
int ti_hdmi_4xxx_audio_config(struct hdmi_ip_data *ip_data,
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
index fd4172b..d4b8883 100644
--- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
+++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
@@ -52,43 +52,44 @@ static inline u32 hdmi_read_reg(void __iomem *base_addr,
return __raw_readl(base_addr + idx);
}
-static inline void __iomem *hdmi_wp_base(struct hdmi_ip_data *ip_data)
+#define REG_FLD_MOD(base, idx, val, start, end) \
+ hdmi_write_reg(base, idx, FLD_MOD(hdmi_read_reg(base, idx),\
+ val, start, end))
+#define REG_GET(base, idx, start, end) \
+ FLD_GET(hdmi_read_reg(base, idx), start, end)
+
+static inline int hdmi_wait_for_bit_change(void __iomem *base_addr,
+ const u16 idx, int b2, int b1, u32 val)
{
- return ip_data->base_wp;
+ u32 t = 0;
+ while (val != REG_GET(base_addr, idx, b2, b1)) {
+ udelay(1);
+ if (t++ > 10000)
+ return !val;
+ }
+ return val;
}
static inline void __iomem *hdmi_phy_base(struct hdmi_ip_data *ip_data)
{
- return ip_data->base_wp + ip_data->phy_offset;
+ return ip_data->wp.base + ip_data->phy_offset;
}
static inline void __iomem *hdmi_pll_base(struct hdmi_ip_data *ip_data)
{
- return ip_data->base_wp + ip_data->pll_offset;
+ return ip_data->wp.base + ip_data->pll_offset;
}
static inline void __iomem *hdmi_av_base(struct hdmi_ip_data *ip_data)
{
- return ip_data->base_wp + ip_data->core_av_offset;
+ return ip_data->wp.base + ip_data->core_av_offset;
}
static inline void __iomem *hdmi_core_sys_base(struct hdmi_ip_data *ip_data)
{
- return ip_data->base_wp + ip_data->core_sys_offset;
+ return ip_data->wp.base + ip_data->core_sys_offset;
}
-static inline int hdmi_wait_for_bit_change(void __iomem *base_addr,
- const u16 idx,
- int b2, int b1, u32 val)
-{
- u32 t = 0;
- while (val != REG_GET(base_addr, idx, b2, b1)) {
- udelay(1);
- if (t++ > 10000)
- return !val;
- }
- return val;
-}
static int hdmi_pll_init(struct hdmi_ip_data *ip_data)
{
@@ -156,41 +157,6 @@ static int hdmi_pll_init(struct hdmi_ip_data *ip_data)
return 0;
}
-/* PHY_PWR_CMD */
-static int hdmi_set_phy_pwr(struct hdmi_ip_data *ip_data, enum hdmi_phy_pwr val)
-{
- /* Return if already the state */
- if (REG_GET(hdmi_wp_base(ip_data), HDMI_WP_PWR_CTRL, 5, 4) = val)
- return 0;
-
- /* Command for power control of HDMI PHY */
- REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_PWR_CTRL, val, 7, 6);
-
- /* Status of the power control of HDMI PHY */
- if (hdmi_wait_for_bit_change(hdmi_wp_base(ip_data),
- HDMI_WP_PWR_CTRL, 5, 4, val) != val) {
- pr_err("Failed to set PHY power mode to %d\n", val);
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-/* PLL_PWR_CMD */
-static int hdmi_set_pll_pwr(struct hdmi_ip_data *ip_data, enum hdmi_pll_pwr val)
-{
- /* Command for power control of HDMI PLL */
- REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_PWR_CTRL, val, 3, 2);
-
- /* wait till PHY_PWR_STATUS is set */
- if (hdmi_wait_for_bit_change(hdmi_wp_base(ip_data), HDMI_WP_PWR_CTRL,
- 1, 0, val) != val) {
- pr_err("Failed to set PLL_PWR_STATUS\n");
- return -ETIMEDOUT;
- }
-
- return 0;
-}
static int hdmi_pll_reset(struct hdmi_ip_data *ip_data)
{
@@ -211,11 +177,11 @@ int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data)
{
u16 r = 0;
- r = hdmi_set_pll_pwr(ip_data, HDMI_PLLPWRCMD_ALLOFF);
+ r = hdmi_wp_set_pll_pwr(&ip_data->wp, HDMI_PLLPWRCMD_ALLOFF);
if (r)
return r;
- r = hdmi_set_pll_pwr(ip_data, HDMI_PLLPWRCMD_BOTHON_ALLCLKS);
+ r = hdmi_wp_set_pll_pwr(&ip_data->wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS);
if (r)
return r;
@@ -232,19 +198,16 @@ int ti_hdmi_4xxx_pll_enable(struct hdmi_ip_data *ip_data)
void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data)
{
- hdmi_set_pll_pwr(ip_data, HDMI_PLLPWRCMD_ALLOFF);
+ hdmi_wp_set_pll_pwr(&ip_data->wp, HDMI_PLLPWRCMD_ALLOFF);
}
static irqreturn_t hdmi_irq_handler(int irq, void *data)
{
struct hdmi_ip_data *ip_data = data;
- void __iomem *wp_base = hdmi_wp_base(ip_data);
u32 irqstatus;
- irqstatus = hdmi_read_reg(wp_base, HDMI_WP_IRQSTATUS);
- hdmi_write_reg(wp_base, HDMI_WP_IRQSTATUS, irqstatus);
- /* flush posted write */
- hdmi_read_reg(wp_base, HDMI_WP_IRQSTATUS);
+ irqstatus = hdmi_wp_get_irqstatus(&ip_data->wp);
+ hdmi_wp_set_irqstatus(&ip_data->wp, irqstatus);
if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
@@ -254,18 +217,16 @@ static irqreturn_t hdmi_irq_handler(int irq, void *data)
* raises connect interrupt if a cable is connected, or nothing
* if cable is not connected.
*/
- hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
+ hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_OFF);
- hdmi_write_reg(wp_base, HDMI_WP_IRQSTATUS,
- HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
- /* flush posted write */
- hdmi_read_reg(wp_base, HDMI_WP_IRQSTATUS);
+ hdmi_wp_set_irqstatus(&ip_data->wp, HDMI_IRQ_LINK_CONNECT |
+ HDMI_IRQ_LINK_DISCONNECT);
- hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON);
+ hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_LDOON);
} else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
- hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON);
+ hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_TXON);
} else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
- hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON);
+ hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_LDOON);
}
return IRQ_HANDLED;
@@ -274,15 +235,15 @@ static irqreturn_t hdmi_irq_handler(int irq, void *data)
int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data)
{
u16 r = 0;
+ u32 irqstatus;
void __iomem *phy_base = hdmi_phy_base(ip_data);
- hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_IRQENABLE_CLR,
- 0xffffffff);
+ hdmi_wp_clear_irqenable(&ip_data->wp, 0xffffffff);
- hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_IRQSTATUS,
- HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
+ irqstatus = hdmi_wp_get_irqstatus(&ip_data->wp);
+ hdmi_wp_set_irqstatus(&ip_data->wp, irqstatus);
- r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON);
+ r = hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_LDOON);
if (r)
return r;
@@ -311,12 +272,12 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data)
IRQF_ONESHOT, "OMAP HDMI", ip_data);
if (r) {
DSSERR("HDMI IRQ request failed\n");
- hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
+ hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_OFF);
return r;
}
- hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_IRQENABLE_SET,
- HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
+ hdmi_wp_set_irqenable(&ip_data->wp,
+ HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
return 0;
}
@@ -325,7 +286,7 @@ void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data)
{
free_irq(ip_data->irq, ip_data);
- hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
+ hdmi_wp_set_phy_pwr(&ip_data->wp, HDMI_PHYPWRCMD_OFF);
}
static int hdmi_core_ddc_init(struct hdmi_ip_data *ip_data)
@@ -679,99 +640,7 @@ static void hdmi_core_av_packet_config(struct hdmi_ip_data *ip_data,
(repeat_cfg.generic_pkt_repeat));
}
-static void hdmi_wp_init(struct omap_video_timings *timings,
- struct hdmi_video_format *video_fmt)
-{
- pr_debug("Enter hdmi_wp_init\n");
-
- timings->hbp = 0;
- timings->hfp = 0;
- timings->hsw = 0;
- timings->vbp = 0;
- timings->vfp = 0;
- timings->vsw = 0;
-
- video_fmt->packing_mode = HDMI_PACK_10b_RGB_YUV444;
- video_fmt->y_res = 0;
- video_fmt->x_res = 0;
-
-}
-
-int ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data)
-{
- REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, true, 31, 31);
- return 0;
-}
-
-void ti_hdmi_4xxx_wp_video_stop(struct hdmi_ip_data *ip_data)
-{
- REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, false, 31, 31);
-}
-
-static void hdmi_wp_video_init_format(struct hdmi_video_format *video_fmt,
- struct omap_video_timings *timings, struct hdmi_config *param)
-{
- pr_debug("Enter hdmi_wp_video_init_format\n");
-
- video_fmt->y_res = param->timings.y_res;
- video_fmt->x_res = param->timings.x_res;
-
- timings->hbp = param->timings.hbp;
- timings->hfp = param->timings.hfp;
- timings->hsw = param->timings.hsw;
- timings->vbp = param->timings.vbp;
- timings->vfp = param->timings.vfp;
- timings->vsw = param->timings.vsw;
-}
-
-static void hdmi_wp_video_config_format(struct hdmi_ip_data *ip_data,
- struct hdmi_video_format *video_fmt)
-{
- u32 l = 0;
-
- REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG,
- video_fmt->packing_mode, 10, 8);
-
- l |= FLD_VAL(video_fmt->y_res, 31, 16);
- l |= FLD_VAL(video_fmt->x_res, 15, 0);
- hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_SIZE, l);
-}
-
-static void hdmi_wp_video_config_interface(struct hdmi_ip_data *ip_data)
-{
- u32 r;
- bool vsync_pol, hsync_pol;
- pr_debug("Enter hdmi_wp_video_config_interface\n");
-
- vsync_pol = ip_data->cfg.timings.vsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
- hsync_pol = ip_data->cfg.timings.hsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
-
- r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG);
- r = FLD_MOD(r, vsync_pol, 7, 7);
- r = FLD_MOD(r, hsync_pol, 6, 6);
- r = FLD_MOD(r, ip_data->cfg.timings.interlace, 3, 3);
- r = FLD_MOD(r, 1, 1, 0); /* HDMI_TIMING_MASTER_24BIT */
- hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, r);
-}
-
-static void hdmi_wp_video_config_timing(struct hdmi_ip_data *ip_data,
- struct omap_video_timings *timings)
-{
- u32 timing_h = 0;
- u32 timing_v = 0;
-
- pr_debug("Enter hdmi_wp_video_config_timing\n");
- timing_h |= FLD_VAL(timings->hbp, 31, 20);
- timing_h |= FLD_VAL(timings->hfp, 19, 8);
- timing_h |= FLD_VAL(timings->hsw, 7, 0);
- hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_TIMING_H, timing_h);
-
- timing_v |= FLD_VAL(timings->vbp, 31, 20);
- timing_v |= FLD_VAL(timings->vfp, 19, 8);
- timing_v |= FLD_VAL(timings->vsw, 7, 0);
- hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_TIMING_V, timing_v);
-}
void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data)
{
@@ -784,20 +653,18 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data)
struct hdmi_core_packet_enable_repeat repeat_cfg;
struct hdmi_config *cfg = &ip_data->cfg;
- hdmi_wp_init(&video_timing, &video_format);
-
hdmi_core_init(&v_core_cfg, avi_cfg, &repeat_cfg);
- hdmi_wp_video_init_format(&video_format, &video_timing, cfg);
+ hdmi_wp_init_vid_fmt_timings(&video_format, &video_timing, cfg);
- hdmi_wp_video_config_timing(ip_data, &video_timing);
+ hdmi_wp_video_config_timing(&ip_data->wp, &video_timing);
/* video config */
video_format.packing_mode = HDMI_PACK_24b_RGB_YUV444_YUV422;
- hdmi_wp_video_config_format(ip_data, &video_format);
+ hdmi_wp_video_config_format(&ip_data->wp, &video_format);
- hdmi_wp_video_config_interface(ip_data);
+ hdmi_wp_video_config_interface(&ip_data->wp, &video_timing);
/*
* configure core video part
@@ -850,31 +717,6 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data)
hdmi_core_av_packet_config(ip_data, repeat_cfg);
}
-void ti_hdmi_4xxx_wp_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
-{
-#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r,\
- hdmi_read_reg(hdmi_wp_base(ip_data), r))
-
- DUMPREG(HDMI_WP_REVISION);
- DUMPREG(HDMI_WP_SYSCONFIG);
- DUMPREG(HDMI_WP_IRQSTATUS_RAW);
- DUMPREG(HDMI_WP_IRQSTATUS);
- DUMPREG(HDMI_WP_IRQENABLE_SET);
- DUMPREG(HDMI_WP_IRQENABLE_CLR);
- DUMPREG(HDMI_WP_IRQWAKEEN);
- DUMPREG(HDMI_WP_PWR_CTRL);
- DUMPREG(HDMI_WP_DEBOUNCE);
- DUMPREG(HDMI_WP_VIDEO_CFG);
- DUMPREG(HDMI_WP_VIDEO_SIZE);
- DUMPREG(HDMI_WP_VIDEO_TIMING_H);
- DUMPREG(HDMI_WP_VIDEO_TIMING_V);
- DUMPREG(HDMI_WP_WP_CLK);
- DUMPREG(HDMI_WP_AUDIO_CFG);
- DUMPREG(HDMI_WP_AUDIO_CFG2);
- DUMPREG(HDMI_WP_AUDIO_CTRL);
- DUMPREG(HDMI_WP_AUDIO_DATA);
-}
-
void ti_hdmi_4xxx_pll_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
{
#define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\
@@ -1071,43 +913,6 @@ void ti_hdmi_4xxx_phy_dump(struct hdmi_ip_data *ip_data, struct seq_file *s)
}
#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
-static void ti_hdmi_4xxx_wp_audio_config_format(struct hdmi_ip_data *ip_data,
- struct hdmi_audio_format *aud_fmt)
-{
- u32 r;
-
- DSSDBG("Enter hdmi_wp_audio_config_format\n");
-
- r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG);
- r = FLD_MOD(r, aud_fmt->stereo_channels, 26, 24);
- r = FLD_MOD(r, aud_fmt->active_chnnls_msk, 23, 16);
- r = FLD_MOD(r, aud_fmt->en_sig_blk_strt_end, 5, 5);
- r = FLD_MOD(r, aud_fmt->type, 4, 4);
- r = FLD_MOD(r, aud_fmt->justification, 3, 3);
- r = FLD_MOD(r, aud_fmt->sample_order, 2, 2);
- r = FLD_MOD(r, aud_fmt->samples_per_word, 1, 1);
- r = FLD_MOD(r, aud_fmt->sample_size, 0, 0);
- hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG, r);
-}
-
-static void ti_hdmi_4xxx_wp_audio_config_dma(struct hdmi_ip_data *ip_data,
- struct hdmi_audio_dma *aud_dma)
-{
- u32 r;
-
- DSSDBG("Enter hdmi_wp_audio_config_dma\n");
-
- r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG2);
- r = FLD_MOD(r, aud_dma->transfer_size, 15, 8);
- r = FLD_MOD(r, aud_dma->block_size, 7, 0);
- hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CFG2, r);
-
- r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CTRL);
- r = FLD_MOD(r, aud_dma->mode, 9, 9);
- r = FLD_MOD(r, aud_dma->fifo_threshold, 8, 0);
- hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_AUDIO_CTRL, r);
-}
-
static void ti_hdmi_4xxx_core_audio_config(struct hdmi_ip_data *ip_data,
struct hdmi_core_audio_config *cfg)
{
@@ -1424,8 +1229,8 @@ int ti_hdmi_4xxx_audio_config(struct hdmi_ip_data *ip_data,
audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_ON;
/* configure DMA and audio FIFO format*/
- ti_hdmi_4xxx_wp_audio_config_dma(ip_data, &audio_dma);
- ti_hdmi_4xxx_wp_audio_config_format(ip_data, &audio_format);
+ hdmi_wp_audio_config_dma(&ip_data->wp, &audio_dma);
+ hdmi_wp_audio_config_format(&ip_data->wp, &audio_format);
/* configure the core*/
ti_hdmi_4xxx_core_audio_config(ip_data, &core);
@@ -1436,25 +1241,13 @@ int ti_hdmi_4xxx_audio_config(struct hdmi_ip_data *ip_data,
return 0;
}
-int ti_hdmi_4xxx_wp_audio_enable(struct hdmi_ip_data *ip_data)
-{
- REG_FLD_MOD(hdmi_wp_base(ip_data),
- HDMI_WP_AUDIO_CTRL, true, 31, 31);
- return 0;
-}
-
-void ti_hdmi_4xxx_wp_audio_disable(struct hdmi_ip_data *ip_data)
-{
- REG_FLD_MOD(hdmi_wp_base(ip_data),
- HDMI_WP_AUDIO_CTRL, false, 31, 31);
-}
-
int ti_hdmi_4xxx_audio_start(struct hdmi_ip_data *ip_data)
{
REG_FLD_MOD(hdmi_av_base(ip_data),
HDMI_CORE_AV_AUD_MODE, true, 0, 0);
- REG_FLD_MOD(hdmi_wp_base(ip_data),
- HDMI_WP_AUDIO_CTRL, true, 30, 30);
+
+ hdmi_wp_audio_core_req_enable(&ip_data->wp, true);
+
return 0;
}
@@ -1462,8 +1255,8 @@ void ti_hdmi_4xxx_audio_stop(struct hdmi_ip_data *ip_data)
{
REG_FLD_MOD(hdmi_av_base(ip_data),
HDMI_CORE_AV_AUD_MODE, false, 0, 0);
- REG_FLD_MOD(hdmi_wp_base(ip_data),
- HDMI_WP_AUDIO_CTRL, false, 30, 30);
+
+ hdmi_wp_audio_core_req_enable(&ip_data->wp, false);
}
int ti_hdmi_4xxx_audio_get_dma_port(u32 *offset, u32 *size)
@@ -1474,4 +1267,5 @@ int ti_hdmi_4xxx_audio_get_dma_port(u32 *offset, u32 *size)
*size = 4;
return 0;
}
+
#endif
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h
index b25269c..dc49713 100644
--- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h
+++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h
@@ -226,18 +226,6 @@
#define HDMI_TXPHY_POWER_CTRL 0x8
#define HDMI_TXPHY_PAD_CFG_CTRL 0xC
-#define REG_FLD_MOD(base, idx, val, start, end) \
- hdmi_write_reg(base, idx, FLD_MOD(hdmi_read_reg(base, idx),\
- val, start, end))
-#define REG_GET(base, idx, start, end) \
- FLD_GET(hdmi_read_reg(base, idx), start, end)
-
-enum hdmi_phy_pwr {
- HDMI_PHYPWRCMD_OFF = 0,
- HDMI_PHYPWRCMD_LDOON = 1,
- HDMI_PHYPWRCMD_TXON = 2
-};
-
enum hdmi_core_inputbus_width {
HDMI_INPUT_8BIT = 0,
HDMI_INPUT_10BIT = 1,
@@ -328,13 +316,6 @@ enum hdmi_core_infoframe {
HDMI_INFOFRAME_AVI_DB5PR_10 = 9,
};
-enum hdmi_packing_mode {
- HDMI_PACK_10b_RGB_YUV444 = 0,
- HDMI_PACK_24b_RGB_YUV444_YUV422 = 1,
- HDMI_PACK_20b_YUV422 = 2,
- HDMI_PACK_ALREADYPACKED = 7
-};
-
enum hdmi_core_audio_layout {
HDMI_AUDIO_LAYOUT_2CH = 0,
HDMI_AUDIO_LAYOUT_8CH = 1
@@ -345,49 +326,6 @@ enum hdmi_core_cts_mode {
HDMI_AUDIO_CTS_MODE_SW = 1
};
-enum hdmi_stereo_channels {
- HDMI_AUDIO_STEREO_NOCHANNELS = 0,
- HDMI_AUDIO_STEREO_ONECHANNEL = 1,
- HDMI_AUDIO_STEREO_TWOCHANNELS = 2,
- HDMI_AUDIO_STEREO_THREECHANNELS = 3,
- HDMI_AUDIO_STEREO_FOURCHANNELS = 4
-};
-
-enum hdmi_audio_type {
- HDMI_AUDIO_TYPE_LPCM = 0,
- HDMI_AUDIO_TYPE_IEC = 1
-};
-
-enum hdmi_audio_justify {
- HDMI_AUDIO_JUSTIFY_LEFT = 0,
- HDMI_AUDIO_JUSTIFY_RIGHT = 1
-};
-
-enum hdmi_audio_sample_order {
- HDMI_AUDIO_SAMPLE_RIGHT_FIRST = 0,
- HDMI_AUDIO_SAMPLE_LEFT_FIRST = 1
-};
-
-enum hdmi_audio_samples_perword {
- HDMI_AUDIO_ONEWORD_ONESAMPLE = 0,
- HDMI_AUDIO_ONEWORD_TWOSAMPLES = 1
-};
-
-enum hdmi_audio_sample_size {
- HDMI_AUDIO_SAMPLE_16BITS = 0,
- HDMI_AUDIO_SAMPLE_24BITS = 1
-};
-
-enum hdmi_audio_transf_mode {
- HDMI_AUDIO_TRANSF_DMA = 0,
- HDMI_AUDIO_TRANSF_IRQ = 1
-};
-
-enum hdmi_audio_blk_strt_end_sig {
- HDMI_AUDIO_BLOCK_SIG_STARTEND_ON = 0,
- HDMI_AUDIO_BLOCK_SIG_STARTEND_OFF = 1
-};
-
enum hdmi_audio_i2s_config {
HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST = 0,
HDMI_AUDIO_I2S_LSB_SHIFTED_FIRST = 1,
@@ -434,29 +372,6 @@ struct hdmi_core_packet_enable_repeat {
u32 generic_pkt_repeat;
};
-struct hdmi_video_format {
- enum hdmi_packing_mode packing_mode;
- u32 y_res; /* Line per panel */
- u32 x_res; /* pixel per line */
-};
-
-struct hdmi_audio_format {
- enum hdmi_stereo_channels stereo_channels;
- u8 active_chnnls_msk;
- enum hdmi_audio_type type;
- enum hdmi_audio_justify justification;
- enum hdmi_audio_sample_order sample_order;
- enum hdmi_audio_samples_perword samples_per_word;
- enum hdmi_audio_sample_size sample_size;
- enum hdmi_audio_blk_strt_end_sig en_sig_blk_strt_end;
-};
-
-struct hdmi_audio_dma {
- u8 transfer_size;
- u8 block_size;
- enum hdmi_audio_transf_mode mode;
- u16 fifo_threshold;
-};
struct hdmi_core_audio_i2s_config {
u8 in_length_bits;
--
1.8.1.2
^ permalink raw reply related
* [PATCH 00/11] omapdss: HDMI: Refactor driver for easier addition of OMAP5/DRA7 hdmi IP
From: Archit Taneja @ 2013-09-17 7:18 UTC (permalink / raw)
To: tomi.valkeinen; +Cc: linux-omap, linux-fbdev, Archit Taneja
The HDMI IP in OMAP4 DSS is present in a few other TI SoCs which don't have a
Display Subsystem similar to what's in OMAP. The original driver was designed
to provide ops such that it could be plugged into a different display subsytem
in some other SoC. These other SoCs, however, never got their baseport code
upstreamed, and the HDMI IP remains exclusive to OMAP DSS.
Refactor the code such that HDMI sub blocks(wrapper, PLL, PHY) are made into
libraries. The HDMI core IP(the encoder) is the only block which varies between
OMAP4 and OMAP5/DRA7 IPs. The rest can be shared by the 2 HDMI drivers.
This will allow more code reuse between the 2 HDMI drivers, and allow easier
addition of the OMAP5/DRA7 HDMI driver. The driver is also simplified now as it
doesn't need to provide ops to different display subsystems.
The new driver needs an address space split according to the HDMI sub blocks.
This requires us to change the OMAP4 hwmod data, or ensure that we add a split
address space when we add support for DSS in DT.
The last patch in the series splits the address space in omap4 hwmod data. If
hwmod changes are disregarded. The driver can temporarily be hacked to contain
the base addresses.
Archit Taneja (11):
omapdss: HDMI: create a hdmi wrapper library
omapdss: HDMI: create a HDMI PLL library
omapdss: HDMI: create a PHY library
omapdss: HDMI: Use OMAP4 HDMI core functions directly and remove
hdmi_ip_ops
omapdss: HDMI: remove hdmi_ip_data struct
omapdss: HDMI: Clean up the header files
omapdss: HDMI: add HDMI wrapper IRQ flags
omapdss: HDMI: Rename hdmi driver files to nicer names
omapdss: OMAP4: HDMI: remove unnecessary edid macros
omapdss: HDMI: move common functions to a separate file
[experimental] arm: omap: omap4 hwmod data: Split hdmi address space
arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 19 +
drivers/video/omap2/dss/Makefile | 3 +-
drivers/video/omap2/dss/core.c | 4 +-
drivers/video/omap2/dss/dss.h | 4 +-
drivers/video/omap2/dss/dss_features.c | 44 -
drivers/video/omap2/dss/dss_features.h | 8 -
drivers/video/omap2/dss/hdmi.c | 1184 ----------------------
drivers/video/omap2/dss/hdmi4.c | 696 +++++++++++++
drivers/video/omap2/dss/hdmi4_core.c | 1016 +++++++++++++++++++
drivers/video/omap2/dss/hdmi4_core.h | 278 ++++++
drivers/video/omap2/dss/hdmi_common.c | 423 ++++++++
drivers/video/omap2/dss/hdmi_phy.c | 142 +++
drivers/video/omap2/dss/hdmi_pll.c | 212 ++++
drivers/video/omap2/dss/hdmi_wp.c | 254 +++++
drivers/video/omap2/dss/ti_hdmi.h | 403 ++++++--
drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c | 1477 ----------------------------
drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h | 488 ---------
17 files changed, 3377 insertions(+), 3278 deletions(-)
delete mode 100644 drivers/video/omap2/dss/hdmi.c
create mode 100644 drivers/video/omap2/dss/hdmi4.c
create mode 100644 drivers/video/omap2/dss/hdmi4_core.c
create mode 100644 drivers/video/omap2/dss/hdmi4_core.h
create mode 100644 drivers/video/omap2/dss/hdmi_common.c
create mode 100644 drivers/video/omap2/dss/hdmi_phy.c
create mode 100644 drivers/video/omap2/dss/hdmi_pll.c
create mode 100644 drivers/video/omap2/dss/hdmi_wp.c
delete mode 100644 drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
delete mode 100644 drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h
--
1.8.1.2
^ permalink raw reply
* [PATCH 19/19] video: w100fb: use dev_get_platdata()
From: Jingoo Han @ 2013-09-17 5:11 UTC (permalink / raw)
To: linux-fbdev
Use the wrapper function for retrieving the platform data instead of
accessing dev->platform_data directly. This is a cosmetic change
to make the code simpler and enhance the readability.
Signed-off-by: Jingoo Han <jg1.han@samsung.com>
---
drivers/video/w100fb.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/video/w100fb.c b/drivers/video/w100fb.c
index 7a299e95..2a0437a 100644
--- a/drivers/video/w100fb.c
+++ b/drivers/video/w100fb.c
@@ -680,7 +680,7 @@ int w100fb_probe(struct platform_device *pdev)
par = info->par;
platform_set_drvdata(pdev, info);
- inf = pdev->dev.platform_data;
+ inf = dev_get_platdata(&pdev->dev);
par->chip_id = chip_id;
par->mach = inf;
par->fastpll_mode = 0;
--
1.7.10.4
^ permalink raw reply related
* [PATCH 18/19] video: tmiofb: use dev_get_platdata()
From: Jingoo Han @ 2013-09-17 5:11 UTC (permalink / raw)
To: linux-fbdev
Use the wrapper function for retrieving the platform data instead of
accessing dev->platform_data directly. This is a cosmetic change
to make the code simpler and enhance the readability.
Signed-off-by: Jingoo Han <jg1.han@samsung.com>
---
drivers/video/tmiofb.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c
index deb8733..b5b69a6 100644
--- a/drivers/video/tmiofb.c
+++ b/drivers/video/tmiofb.c
@@ -250,7 +250,7 @@ static irqreturn_t tmiofb_irq(int irq, void *__info)
*/
static int tmiofb_hw_stop(struct platform_device *dev)
{
- struct tmio_fb_data *data = dev->dev.platform_data;
+ struct tmio_fb_data *data = dev_get_platdata(&dev->dev);
struct fb_info *info = platform_get_drvdata(dev);
struct tmiofb_par *par = info->par;
@@ -311,7 +311,7 @@ static int tmiofb_hw_init(struct platform_device *dev)
*/
static void tmiofb_hw_mode(struct platform_device *dev)
{
- struct tmio_fb_data *data = dev->dev.platform_data;
+ struct tmio_fb_data *data = dev_get_platdata(&dev->dev);
struct fb_info *info = platform_get_drvdata(dev);
struct fb_videomode *mode = info->mode;
struct tmiofb_par *par = info->par;
@@ -557,7 +557,7 @@ static int tmiofb_ioctl(struct fb_info *fbi,
static struct fb_videomode *
tmiofb_find_mode(struct fb_info *info, struct fb_var_screeninfo *var)
{
- struct tmio_fb_data *data = info->device->platform_data;
+ struct tmio_fb_data *data = dev_get_platdata(info->device);
struct fb_videomode *best = NULL;
int i;
@@ -577,7 +577,7 @@ static int tmiofb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
struct fb_videomode *mode;
- struct tmio_fb_data *data = info->device->platform_data;
+ struct tmio_fb_data *data = dev_get_platdata(info->device);
mode = tmiofb_find_mode(info, var);
if (!mode || var->bits_per_pixel > 16)
@@ -678,7 +678,7 @@ static struct fb_ops tmiofb_ops = {
static int tmiofb_probe(struct platform_device *dev)
{
const struct mfd_cell *cell = mfd_get_cell(dev);
- struct tmio_fb_data *data = dev->dev.platform_data;
+ struct tmio_fb_data *data = dev_get_platdata(&dev->dev);
struct resource *ccr = platform_get_resource(dev, IORESOURCE_MEM, 1);
struct resource *lcr = platform_get_resource(dev, IORESOURCE_MEM, 0);
struct resource *vram = platform_get_resource(dev, IORESOURCE_MEM, 2);
--
1.7.10.4
^ permalink raw reply related
* [PATCH 17/19] video: simplefb: use dev_get_platdata()
From: Jingoo Han @ 2013-09-17 5:11 UTC (permalink / raw)
To: linux-fbdev
Use the wrapper function for retrieving the platform data instead of
accessing dev->platform_data directly. This is a cosmetic change
to make the code simpler and enhance the readability.
Signed-off-by: Jingoo Han <jg1.han@samsung.com>
---
drivers/video/simplefb.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/video/simplefb.c b/drivers/video/simplefb.c
index 8d78106..ad46d2d 100644
--- a/drivers/video/simplefb.c
+++ b/drivers/video/simplefb.c
@@ -132,7 +132,7 @@ static int simplefb_parse_dt(struct platform_device *pdev,
static int simplefb_parse_pd(struct platform_device *pdev,
struct simplefb_params *params)
{
- struct simplefb_platform_data *pd = pdev->dev.platform_data;
+ struct simplefb_platform_data *pd = dev_get_platdata(&pdev->dev);
int i;
params->width = pd->width;
@@ -167,7 +167,7 @@ static int simplefb_probe(struct platform_device *pdev)
return -ENODEV;
ret = -ENODEV;
- if (pdev->dev.platform_data)
+ if (dev_get_platdata(&pdev->dev))
ret = simplefb_parse_pd(pdev, ¶ms);
else if (pdev->dev.of_node)
ret = simplefb_parse_dt(pdev, ¶ms);
--
1.7.10.4
^ permalink raw reply related
* [PATCH 16/19] video: s3c-fb: use dev_get_platdata()
From: Jingoo Han @ 2013-09-17 5:10 UTC (permalink / raw)
To: linux-fbdev
Use the wrapper function for retrieving the platform data instead of
accessing dev->platform_data directly. This is a cosmetic change
to make the code simpler and enhance the readability.
Signed-off-by: Jingoo Han <jg1.han@samsung.com>
---
drivers/video/s3c-fb.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c
index 2e7991c..62acae2 100644
--- a/drivers/video/s3c-fb.c
+++ b/drivers/video/s3c-fb.c
@@ -1378,7 +1378,7 @@ static int s3c_fb_probe(struct platform_device *pdev)
return -EINVAL;
}
- pd = pdev->dev.platform_data;
+ pd = dev_get_platdata(&pdev->dev);
if (!pd) {
dev_err(dev, "no platform data specified\n");
return -EINVAL;
--
1.7.10.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox