* Re: CDF meeting @FOSDEM report
From: Tomi Valkeinen @ 2013-02-06 15:00 UTC (permalink / raw)
To: Alex Deucher
Cc: Laurent Pinchart, linux-fbdev, Sebastien Guiriec, dri-devel,
Jesse Barnes, Benjamin Gaignard, Sumit Semwal, Tom Gall,
Kyungmin Park, linux-media, Stephen Warren, Thierry Reding,
Mark Zhang, linaro-mm-sig, Stéphane Marchesin,
Alexandre Courbot, Ragesh Radhakrishnan, Thomas Petazzoni,
Sunil Joshi, Maxime Ripard, Vikas Sajjan
In-Reply-To: <CADnq5_P1GFbAwoe9kTeARq8ZLP1tOBc9Rn1h2KrRYxkoLxLXfw@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1840 bytes --]
On 2013-02-06 16:44, Alex Deucher wrote:
> On Wed, Feb 6, 2013 at 6:11 AM, Tomi Valkeinen <tomi.valkeinen@ti.com> wrote:
>> What is an encoder? Something that takes a video signal in, and lets the
>> CPU store the received data to memory? Isn't that a decoder?
>>
>> Or do you mean something that takes a video signal in, and outputs a
>> video signal in another format? (transcoder?)
>
> In KMS parlance, we have two objects a crtc and an encoder. A crtc
> reads data from memory and produces a data stream with display timing.
> The encoder then takes that datastream and timing from the crtc and
> converts it some sort of physical signal (LVDS, TMDS, DP, etc.). It's
Isn't the video stream between CRTC and encoder just as physical, it
just happens to be inside the GPU?
This is the case for OMAP, at least, where DISPC could be considered
CRTC, and DSI/HDMI/etc could be considered encoder. The stream between
DISPC and DSI/HDMI is plain parallel RGB signal. The video stream could
as well be outside OMAP.
> not always a perfect match to the hardware. For example a lot of GPUs
> have a DVO encoder which feeds a secondary encoder like an sil164 DVO
> to TMDS encoder.
Right. I think mapping the DRM entities to CDF ones is one of the bigger
question marks we have with CDF. While I'm no expert on DRM, I think we
have the following options:
1. Force DRM's model to CDF, meaning one encoder.
2. Extend DRM to support multiple encoders in a chain.
3. Support multiple encoders in a chain in CDF, but somehow map them to
a single encoder in DRM side.
I really dislike the first option, as it would severely limit where CDF
can be used, or would force you to write some kind of combined drivers,
so that you can have one encoder driver running multiple encoder devices.
Tomi
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 899 bytes --]
^ permalink raw reply
* Re: [Linaro-mm-sig] CDF meeting @FOSDEM report
From: Daniel Vetter @ 2013-02-06 16:14 UTC (permalink / raw)
To: Tomi Valkeinen
Cc: Alex Deucher, Thomas Petazzoni, linux-fbdev, Stephen Warren,
Thierry Reding, Mark Zhang, dri-devel, Sunil Joshi, linaro-mm-sig,
Stéphane Marchesin, Kyungmin Park, Jesse Barnes,
Laurent Pinchart, Sebastien Guiriec, Alexandre Courbot,
Maxime Ripard, Vikas Sajjan, Ragesh Radhakrishnan, linux-media
In-Reply-To: <51127008.7050808@ti.com>
On Wed, Feb 6, 2013 at 4:00 PM, Tomi Valkeinen <tomi.valkeinen@ti.com> wrote:
>> not always a perfect match to the hardware. For example a lot of GPUs
>> have a DVO encoder which feeds a secondary encoder like an sil164 DVO
>> to TMDS encoder.
>
> Right. I think mapping the DRM entities to CDF ones is one of the bigger
> question marks we have with CDF. While I'm no expert on DRM, I think we
> have the following options:
>
> 1. Force DRM's model to CDF, meaning one encoder.
>
> 2. Extend DRM to support multiple encoders in a chain.
>
> 3. Support multiple encoders in a chain in CDF, but somehow map them to
> a single encoder in DRM side.
4. Ignore drm kms encoders.
They are only exposed to userspace as a means for userspace to
discover very simple constraints, e.g. 1 encoder connected to 2
outputs means you can only use one of the outputs at the same time.
They are completely irrelevant for the actual modeset interface
exposed to drivers, so you could create a fake kms encoder for each
connector you expose through kms.
The crtc helpers use the encoders as a real entity, and if you opt to
use the crtc helpers to implement the modeset sequence in your driver
it makes sense to map them to some real piece of hw. But you can
essentially pick any transcoder in your crtc -> final output chain for
this. Generic userspace needs to be able to cope with a failed modeset
due to arbitrary reasons anyway, so can't presume that simply because
the currently exposed constraints are fulfilled it'll work.
> I really dislike the first option, as it would severely limit where CDF
> can be used, or would force you to write some kind of combined drivers,
> so that you can have one encoder driver running multiple encoder devices.
Imo CDF and drm encoders don't really have that much to do with each
another, it should just be a driver implementation detail. Of course,
if common patterns emerge we could extract them somehow. E.g. if many
drivers end up exposing the CDF transcoder chain as a drm encoder
using the crtc helpers, we could add some library functions to make
that simpler.
Another conclusion (at least from my pov) from the fosdem discussion
is that we should separate the panel interface from the actual
control/pixel data buses. That should give us more flexibility for
insane hw and also directly exposing properties and knobs to the
userspace interface from e.g. dsi transcoders. So I don't think we'll
end up with _the_ canonical CDF sink interface anyway.
-Daniel
--
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
^ permalink raw reply
* Re: Unmerged patches for 3.9
From: Sachin Kamat @ 2013-02-07 7:19 UTC (permalink / raw)
To: linux-fbdev
Resending due to wrong fbdev mailing address. Sorry.
On 7 February 2013 12:34, Sachin Kamat <sachin.kamat@linaro.org> wrote:
> Hi Florian,
>
> I have the following 'Acked' patches missing in your tree.
>
> https://patchwork.kernel.org/patch/1864681/
> https://patchwork.kernel.org/patch/1923041/
> https://patchwork.kernel.org/patch/1926501/
>
> Could you please pick them up in your tree as they have been pending
> almost since a couple of months now.
>
> --
> With warm regards,
> Sachin
--
With warm regards,
Sachin
^ permalink raw reply
* RE: [PATCH v3 00/10] video: da8xx-fb: runtime timing configuration
From: Mohammed, Afzal @ 2013-02-07 9:05 UTC (permalink / raw)
To: Florian Tobias Schandinat, Valkeinen, Tomi, Nori, Sekhar,
linux-omap@vger.kernel.org, linux-fbdev@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <cover.1358250489.git.afzal@ti.com>
SGkgVG9taSwgRmxvcmlhbiwNCg0KT24gVHVlLCBKYW4gMTUsIDIwMTMgYXQgMTk6MDA6NTAsIE1v
aGFtbWVkLCBBZnphbCB3cm90ZToNCg0KPiBUaGlzIHNlcmllcyBtYWtlcyBkYTh4eC1mYiBkcml2
ZXIgKGRldmljZSBmb3VuZCBvbiBEYVZpbmNpIGFuZCBBTTMzNXgpDQo+IGNhcGFibGUgb2YgaGFu
ZGxpbmcgcnVudGltZSB0aW1pbmcgY29uZmlndXJhdGlvbiBieSBhZGRpbmcgZmJfc2V0X3Bhci4N
Cj4gDQo+IFRoZSBsYXN0IGNoYW5nZSBhZGRzIGFjdHVhbCBmYl9zZXRfcGFyIHN1cHBvcnQuIE90
aGVyIHByZWNlZWRpbmcNCj4gY2hhbmdlcyBtYWtlcyB0aGUgd2F5IGNsZWFyIGZvciBpdCBhcyB3
ZWxsIGFzIGRvZXMgY2VydGFpbiBjbGVhbnVwJ3MNCj4gb24gdGhlIHdheS4NCj4gDQo+IFRoaXMg
aGFzIGJlZW4gdGVzdGVkIG9uIGRhODUwIGV2bSBhcyBpcy4gVGhpcyB3YXMgYWxzbyB0ZXN0ZWQg
b24NCj4gYW0zMzV4LWV2bSAmIGFtMzM1eC1ldm1zayB3aXRoIGEgc2VyaWVzIHRoYXQgYWRkcyBE
VCBzdXBwb3J0Lg0KPiANCj4gVGhpcyBpcyBiYXNlZCBvbiB2My44LXJjMywgdGhpcyBpcyB0aGUg
b25seSBjaGFuZ2UgaW4gdGhpcyByZXZpc2lvbi4NCj4gVGhlIG5ldyB2ZXJzaW9uIG9mIHRoaXMg
c2VyaWVzIGlzIGJlaW5nIHBvc3RlZCBzbyB0aGF0IHRoaXMgc2VyaWVzIGNhbg0KPiBiZSBhcHBs
aWVkIGVhc2lseSAoYXMgX19kZXYqIGFyZSByZW1vdmVkLCB0aGVyZSB3b3VsZCBiZSBtZXJnZQ0K
PiBjb25mbGljdHMgd2l0aCB2Miwgd2hpY2ggd2FzIGJhc2VkIG9uIC1yYzIpLg0KPiBzZXJpZXMN
Cg0KQ2FuIHlvdSBwbGVhc2UgY29uc2lkZXIgdGhpcyBzZXJpZXMgZm9yIGluY2x1c2lvbi4gVGhl
cmUgYXJlIG5vDQpwZW5kaW5nIGNvbW1lbnRzIG9yIGRlcGVuZGVuY3kgZm9yIHRoaXMgc2VyaWVz
LiBJZiB5b3UgbmVlZCBhDQpwdWxsIHJlcXVlc3QsIGxldCBtZSBrbm93LCBJIHdpbGwgc2VudCBp
dC4NCg0KUmVnYXJkcw0KQWZ6YWwNCg0K
^ permalink raw reply
* Re: [PATCH v3 00/10] video: da8xx-fb: runtime timing configuration
From: Tomi Valkeinen @ 2013-02-07 9:13 UTC (permalink / raw)
To: Mohammed, Afzal
Cc: Florian Tobias Schandinat, Nori, Sekhar,
linux-omap@vger.kernel.org, linux-fbdev@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <C8443D0743D26F4388EA172BF4E2A7A93EAA2846@DBDE01.ent.ti.com>
[-- Attachment #1: Type: text/plain, Size: 1200 bytes --]
Hi,
On 2013-02-07 11:05, Mohammed, Afzal wrote:
> Hi Tomi, Florian,
>
> On Tue, Jan 15, 2013 at 19:00:50, Mohammed, Afzal wrote:
>
>> This series makes da8xx-fb driver (device found on DaVinci and AM335x)
>> capable of handling runtime timing configuration by adding fb_set_par.
>>
>> The last change adds actual fb_set_par support. Other preceeding
>> changes makes the way clear for it as well as does certain cleanup's
>> on the way.
>>
>> This has been tested on da850 evm as is. This was also tested on
>> am335x-evm & am335x-evmsk with a series that adds DT support.
>>
>> This is based on v3.8-rc3, this is the only change in this revision.
>> The new version of this series is being posted so that this series can
>> be applied easily (as __dev* are removed, there would be merge
>> conflicts with v2, which was based on -rc2).
>> series
>
> Can you please consider this series for inclusion. There are no
> pending comments or dependency for this series. If you need a
> pull request, let me know, I will sent it.
I handled only the pull request for 3.8 merge window to help Florian
with it. I didn't mean to become a co-maintainer or such =).
Tomi
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 899 bytes --]
^ permalink raw reply
* Re: [PATCH v2,RESEND] video console: add a driver for lcd2s character display
From: Lars Poeschel @ 2013-02-07 9:26 UTC (permalink / raw)
To: Arnd Bergmann
Cc: FlorianSchandinat, mathieu.poirier, linux-fbdev, linux-kernel
In-Reply-To: <201212170934.15312.poeschel@lemonage.de>
As it seems there are no further comments, could this please be included ?
Thanks,
Lars
On Monday 17 December 2012 at 09:34:15, Lars Poeschel wrote:
> From: Lars Poeschel <poeschel@lemonage.de>
>
> This driver allows to use a lcd2s 20x4 character display as
> a linux console output device.
>
> Signed-off-by: Lars Poeschel <poeschel@lemonage.de>
>
> ---
> drivers/video/console/Kconfig | 10 ++
> drivers/video/console/Makefile | 1 +
> drivers/video/console/lcd2scon.c | 360
> ++++++++++++++++++++++++++++++++++++++ 3 files changed, 371 insertions(+)
> create mode 100644 drivers/video/console/lcd2scon.c
>
> diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
> index e2c96d0..44fc3bf 100644
> --- a/drivers/video/console/Kconfig
> +++ b/drivers/video/console/Kconfig
> @@ -129,6 +129,16 @@ config STI_CONSOLE
> machines. Say Y here to build support for it into your kernel.
> The alternative is to use your primary serial port as a
> console.
>
> +config LCD2S_CONSOLE
> + tristate "lcd2s 20x4 character display over I2C console"
> + depends on I2C
> + default n
> + help
> + This is a driver that lets you use the lcd2s 20x4 character
> display + from modtronix engineering as a console output device.
> The display + is a simple single color character display. You
> have to connect it + to an I2C bus.
> +
> config FONTS
> bool "Select compiled-in fonts"
> depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
> diff --git a/drivers/video/console/Makefile
> b/drivers/video/console/Makefile index a862e91..74d5993 100644
> --- a/drivers/video/console/Makefile
> +++ b/drivers/video/console/Makefile
> @@ -23,6 +23,7 @@ font-objs += $(font-objs-y)
> obj-$(CONFIG_DUMMY_CONSOLE) += dummycon.o
> obj-$(CONFIG_SGI_NEWPORT_CONSOLE) += newport_con.o font.o
> obj-$(CONFIG_STI_CONSOLE) += sticon.o sticore.o font.o
> +obj-$(CONFIG_LCD2S_CONSOLE) += lcd2scon.o
> obj-$(CONFIG_VGA_CONSOLE) += vgacon.o
> obj-$(CONFIG_MDA_CONSOLE) += mdacon.o
> obj-$(CONFIG_FRAMEBUFFER_CONSOLE) += fbcon.o bitblit.o font.o
> softcursor.o diff --git a/drivers/video/console/lcd2scon.c
> b/drivers/video/console/lcd2scon.c new file mode 100644
> index 0000000..c139811
> --- /dev/null
> +++ b/drivers/video/console/lcd2scon.c
> @@ -0,0 +1,360 @@
> +/*
> + * console driver for LCD2S 4x20 character displays connected through
> i2c. + *
> + * This is a driver allowing you to use a LCD2S 4x20 from modtronix
> + * engineering as console output device.
> + *
> + * (C) 2012 by Lemonage Software GmbH
> + * Author: Lars Poeschel <poeschel@lemonage.de>
> + * All rights reserved.
> + *
> + * 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/kd.h>
> +#include <linux/tty.h>
> +#include <linux/console_struct.h>
> +#include <linux/console.h>
> +#include <linux/vt_kern.h>
> +#include <linux/i2c.h>
> +
> +#define LCD2S_CMD_CUR_BLINK_OFF 0x10
> +#define LCD2S_CMD_CUR_UL_OFF 0x11
> +#define LCD2S_CMD_DISPLAY_OFF 0x12
> +#define LCD2S_CMD_CUR_BLINK_ON 0x18
> +#define LCD2S_CMD_CUR_UL_ON 0x19
> +#define LCD2S_CMD_DISPLAY_ON 0x1a
> +#define LCD2S_CMD_BACKLIGHT_OFF 0x20
> +#define LCD2S_CMD_BACKLIGHT_ON 0x28
> +#define LCD2S_CMD_WRITE 0x80
> +#define LCD2S_CMD_SHIFT_UP 0x87
> +#define LCD2S_CMD_SHIFT_DOWN 0x88
> +#define LCD2S_CMD_CUR_ADDR 0x89
> +#define LCD2S_CMD_CUR_POS 0x8a
> +#define LCD2S_CMD_CUR_RESET 0x8b
> +#define LCD2S_CMD_CLEAR 0x8c
> +
> +#define LCD2S_FIRST 8
> +#define LCD2S_LAST 9
> +
> +#define LCD2S_ROWS 4
> +#define LCD2S_COLS 20
> +
> +struct lcd2s_data {
> + struct i2c_client *i2c;
> + int row;
> + int col;
> + unsigned int cur_blink:1;
> + unsigned int cur_ul:1;
> +};
> +
> +static struct lcd2s_data lcd2s;
> +
> +static void lcd2s_set_cursor(int row, int col)
> +{
> + u8 buf[] = { LCD2S_CMD_CUR_POS, row + 1, col + 1};
> +
> + if (row = lcd2s.row && col = lcd2s.col)
> + return;
> +
> + i2c_master_send(lcd2s.i2c, buf, sizeof(buf));
> + lcd2s.row = row;
> + lcd2s.col = col;
> +}
> +
> +static void lcd2s_reset_cursor(void)
> +{
> + i2c_smbus_write_byte(lcd2s.i2c, LCD2S_CMD_CUR_RESET);
> + lcd2s.row = 0;
> + lcd2s.col = 0;
> +}
> +
> +static void lcd2s_increase_cursor(struct vc_data *con, int inc)
> +{
> + lcd2s.col += inc;
> + if ((lcd2s.col / con->vc_cols) >= 1) {
> + lcd2s.row += (lcd2s.col / con->vc_cols);
> + lcd2s.col %= con->vc_cols;
> + }
> +}
> +
> +static void lcd2s_cursor_ul_on(void)
> +{
> + if (!lcd2s.cur_ul) {
> + i2c_smbus_write_byte(lcd2s.i2c, LCD2S_CMD_CUR_UL_ON);
> + lcd2s.cur_ul = 1;
> + }
> +}
> +
> +static void lcd2s_cursor_ul_off(void)
> +{
> + if (lcd2s.cur_ul) {
> + i2c_smbus_write_byte(lcd2s.i2c, LCD2S_CMD_CUR_UL_OFF);
> + lcd2s.cur_ul = 0;
> + }
> +}
> +
> +static void lcd2s_cursor_blink_on(void)
> +{
> + if (!lcd2s.cur_blink) {
> + i2c_smbus_write_byte(lcd2s.i2c, LCD2S_CMD_CUR_BLINK_ON);
> + lcd2s.cur_blink = 1;
> + }
> +}
> +
> +static void lcd2s_cursor_blink_off(void)
> +{
> + if (lcd2s.cur_blink) {
> + i2c_smbus_write_byte(lcd2s.i2c, LCD2S_CMD_CUR_BLINK_OFF);
> + lcd2s.cur_blink = 0;
> + }
> +}
> +
> +static const char *lcd2s_startup(void)
> +{
> + return "lcd2s console";
> +}
> +
> +
> +/*
> + * init is set if console is currently allocated during init
> + */
> +static void lcd2s_init(struct vc_data *con, int init)
> +{
> + lcd2s.cur_blink = 0;
> + lcd2s.cur_ul = 0;
> +
> + /* turn display on */
> + i2c_smbus_write_byte(lcd2s.i2c, LCD2S_CMD_DISPLAY_ON);
> + i2c_smbus_write_byte(lcd2s.i2c, LCD2S_CMD_BACKLIGHT_ON);
> + i2c_smbus_write_byte(lcd2s.i2c, LCD2S_CMD_CLEAR);
> + lcd2s_cursor_ul_on();
> + lcd2s_reset_cursor();
> +
> + con->vc_can_do_color = 0;
> + con->vc_hi_font_mask = 0;
> +
> + if (init) {
> + con->vc_rows = LCD2S_ROWS;
> + con->vc_cols = LCD2S_COLS;
> + } else
> + vc_resize(con, LCD2S_COLS, LCD2S_ROWS);
> +}
> +
> +static void lcd2s_deinit(struct vc_data *con)
> +{
> + i2c_smbus_write_byte(lcd2s.i2c, LCD2S_CMD_DISPLAY_OFF);
> + i2c_smbus_write_byte(lcd2s.i2c, LCD2S_CMD_BACKLIGHT_OFF);
> + lcd2s_cursor_blink_off();
> + lcd2s_cursor_ul_off();
> +}
> +
> +static void lcd2s_clear(struct vc_data *con, int s_row, int s_col,
> + int height, int width)
> +{
> + u8 buf[width];
> + int i;
> +
> + if (width <= 0 || height <= 0)
> + return;
> +
> + /* if the whole display is to clear, we have a single command */
> + if (s_col = 0 && s_row = 0 &&
> + height >= con->vc_rows - 1 && width >= con->vc_cols - 1) {
> + i2c_smbus_write_byte(lcd2s.i2c, LCD2S_CMD_CLEAR);
> + return;
> + }
> +
> + for (i = 0; i <= width; i++)
> + buf[i] = ' ';
> +
> + for (i = s_col; i <= height; i++) {
> + lcd2s_set_cursor(s_row, i);
> + i2c_master_send(lcd2s.i2c, buf, width);
> + }
> +}
> +
> +static void lcd2s_putc(struct vc_data *con, int data, int row, int col)
> +{
> + u8 buf[] = {LCD2S_CMD_WRITE, data};
> +
> + lcd2s_set_cursor(row, col);
> +
> + i2c_master_send(lcd2s.i2c, buf, sizeof(buf));
> + lcd2s_increase_cursor(con, 1);
> +}
> +
> +static void lcd2s_putcs(struct vc_data *con, const unsigned short *buf,
> + int len, int row, int col)
> +{
> + u8 *i2c_buf;
> + int i;
> +
> + i2c_buf = kzalloc(len + 1, GFP_KERNEL);
> + if (!i2c_buf)
> + return;
> +
> + lcd2s_set_cursor(row, col);
> +
> + i2c_buf[0] = LCD2S_CMD_WRITE;
> + for (i = 0; i < len ; i++)
> + i2c_buf[i + 1] = buf[i];
> +
> + i2c_master_send(lcd2s.i2c, i2c_buf, len + 1);
> + kfree(i2c_buf);
> + lcd2s_increase_cursor(con, len);
> +}
> +
> +static void lcd2s_cursor(struct vc_data *con, int mode)
> +{
> + switch (mode) {
> + case CM_ERASE:
> + lcd2s_cursor_blink_off();
> + lcd2s_cursor_ul_off();
> + break;
> + case CM_MOVE:
> + case CM_DRAW:
> + lcd2s_set_cursor(con->vc_y, con->vc_x);
> +
> + switch (con->vc_cursor_type & CUR_HWMASK) {
> + case CUR_UNDERLINE:
> + lcd2s_cursor_ul_on();
> + lcd2s_cursor_blink_off();
> + break;
> + case CUR_NONE:
> + lcd2s_cursor_blink_off();
> + lcd2s_cursor_ul_off();
> + break;
> + default:
> + lcd2s_cursor_blink_on();
> + lcd2s_cursor_ul_off();
> + break;
> + }
> + break;
> + }
> +}
> +
> +static int lcd2s_scroll(struct vc_data *con, int top, int bot,
> + int dir, int lines)
> +{
> + /* we can only scroll the whole display */
> + if (top = 0 && bot = con->vc_rows) {
> + while (lines--) {
> + switch (dir) {
> + case SM_UP:
> + i2c_smbus_write_byte(lcd2s.i2c,
> + LCD2S_CMD_SHIFT_UP);
> + break;
> + case SM_DOWN:
> + i2c_smbus_write_byte(lcd2s.i2c,
> + LCD2S_CMD_SHIFT_DOWN);
> + break;
> + }
> + }
> + return 0;
> + }
> + return 1;
> +}
> +
> +static void lcd2s_bmove(struct vc_data *conp, int s_row, int s_col,
> + int d_row, int d_col, int height, int width)
> +{
> +}
> +
> +static int lcd2s_switch(struct vc_data *con)
> +{
> + return 1;
> +}
> +
> +static int lcd2s_blank(struct vc_data *con, int blank, int mode_switch)
> +{
> + switch (blank) {
> + case 0: /* unblank */
> + i2c_smbus_write_byte(lcd2s.i2c, LCD2S_CMD_DISPLAY_ON);
> + i2c_smbus_write_byte(lcd2s.i2c, LCD2S_CMD_BACKLIGHT_ON);
> + break;
> + case 1: /* normal blanking */
> + i2c_smbus_write_byte(lcd2s.i2c, LCD2S_CMD_DISPLAY_OFF);
> + i2c_smbus_write_byte(lcd2s.i2c, LCD2S_CMD_BACKLIGHT_OFF);
> + break;
> + }
> + return 0;
> +}
> +
> +static int lcd2s_set_palette(struct vc_data *con, unsigned char *table)
> +{
> + return -EINVAL;
> +}
> +
> +static int lcd2s_scrolldelta(struct vc_data *con, int lines)
> +{
> + return 0;
> +}
> +
> +static struct consw lcd2s_con = {
> + .owner = THIS_MODULE,
> + .con_startup = lcd2s_startup,
> + .con_init = lcd2s_init,
> + .con_deinit = lcd2s_deinit,
> + .con_clear = lcd2s_clear,
> + .con_putc = lcd2s_putc,
> + .con_putcs = lcd2s_putcs,
> + .con_cursor = lcd2s_cursor,
> + .con_scroll = lcd2s_scroll,
> + .con_bmove = lcd2s_bmove,
> + .con_switch = lcd2s_switch,
> + .con_blank = lcd2s_blank,
> + .con_set_palette = lcd2s_set_palette,
> + .con_scrolldelta = lcd2s_scrolldelta,
> +};
> +
> +static int __devinit lcd2s_i2c_probe(struct i2c_client *i2c,
> + const struct i2c_device_id *id)
> +{
> + if (!i2c_check_functionality(i2c->adapter,
> + I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
> + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
> + return -EIO;
> +
> + lcd2s.i2c = i2c;
> +
> + take_over_console(&lcd2s_con, LCD2S_FIRST, LCD2S_LAST, 1);
> +
> + return 0;
> +}
> +
> +static __devexit int lcd2s_i2c_remove(struct i2c_client *i2c)
> +{
> + /* unregister from console subsystem */
> + unregister_con_driver(&lcd2s_con);
> + return 0;
> +}
> +
> +static const struct i2c_device_id lcd2s_i2c_id[] = {
> + { "lcd2s", 0 },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, lcd2s_i2c_id);
> +
> +static struct i2c_driver lcd2s_i2c_driver = {
> + .driver = {
> + .name = "lcd2s",
> + .owner = THIS_MODULE,
> + },
> + .probe = lcd2s_i2c_probe,
> + .remove = __devexit_p(lcd2s_i2c_remove),
> + .id_table = lcd2s_i2c_id,
> +};
> +
> +module_i2c_driver(lcd2s_i2c_driver)
> +
> +MODULE_DESCRIPTION("LCD2S character display console driver");
> +MODULE_AUTHOR("Lars Poeschel");
> +MODULE_LICENSE("GPL");
^ permalink raw reply
* Re: [RFC PATCH 4/4] video: display: Add Samsung s6e8ax0 display panel driver
From: Vikas Sajjan @ 2013-02-07 9:46 UTC (permalink / raw)
To: Tomasz Figa
Cc: dri-devel, linux-fbdev, linux-samsung-soc, kyungmin.park,
m.szyprowski, tomasz.figa, Daniel Vetter, Marcus Lorentzon,
Laurent Pinchart, rob, tomi.valkeinen, inki.dae, dh09.lee,
ville.syrjala, s.nawrocki, aditya.ps, sunil joshi
In-Reply-To: <1359560343-31636-5-git-send-email-t.figa@samsung.com>
Hi Figa,
On Wed, Jan 30, 2013 at 9:09 PM, Tomasz Figa <t.figa@samsung.com> wrote:
> This patch adds Common Display Framework driver for Samsung s6e8ax0
> MIPI DSI display panel.
>
> Signed-off-by: Tomasz Figa <t.figa@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
> drivers/video/display/Kconfig | 3 +
> drivers/video/display/Makefile | 1 +
> drivers/video/display/panel-s6e8ax0.c | 1027 +++++++++++++++++++++++++++++++++
> include/video/panel-s6e8ax0.h | 41 ++
> 4 files changed, 1072 insertions(+)
> create mode 100644 drivers/video/display/panel-s6e8ax0.c
> create mode 100644 include/video/panel-s6e8ax0.h
>
> diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
> index b14527a..f19ec04 100644
> --- a/drivers/video/display/Kconfig
> +++ b/drivers/video/display/Kconfig
> @@ -5,6 +5,9 @@ menuconfig DISPLAY_CORE
>
> if DISPLAY_CORE
>
> +config DISPLAY_PANEL_S6E8AX0
> + tristate "S6E8AX0 DSI video mode panel"
> + select OF_VIDEOMODE
>
> config DISPLAY_SOURCE_EXYNOS_DSI
> tristate "Samsung SoC MIPI DSI Master"
> diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
> index 40a283a..0f7fdc2 100644
> --- a/drivers/video/display/Makefile
> +++ b/drivers/video/display/Makefile
> @@ -1,2 +1,3 @@
> obj-$(CONFIG_DISPLAY_CORE) += display-core.o
> +obj-$(CONFIG_DISPLAY_PANEL_S6E8AX0) += panel-s6e8ax0.o
> obj-$(CONFIG_DISPLAY_SOURCE_EXYNOS_DSI) += source-exynos_dsi.o
> diff --git a/drivers/video/display/panel-s6e8ax0.c b/drivers/video/display/panel-s6e8ax0.c
> new file mode 100644
> index 0000000..4c09fe2
> --- /dev/null
> +++ b/drivers/video/display/panel-s6e8ax0.c
> @@ -0,0 +1,1027 @@
> +/* linux/drivers/video/exynos/s6e8ax0.c
> + *
> + * MIPI-DSI based s6e8ax0 AMOLED lcd 4.65 inch panel driver.
> + *
> + * Inki Dae, <inki.dae@samsung.com>
> + * Donghwa Lee, <dh09.lee@samsung.com>
> + * Tomasz Figa, <t.figa@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/mutex.h>
> +#include <linux/wait.h>
> +#include <linux/ctype.h>
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +
> +#include <linux/fb.h>
> +#include <linux/gpio.h>
> +#include <linux/lcd.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_videomode.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/backlight.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <video/display.h>
> +#include <video/mipi_display.h>
> +#include <video/panel-s6e8ax0.h>
> +
> +#define LDI_MTP_LENGTH 24
> +#define DSIM_PM_STABLE_TIME 10
> +#define MIN_BRIGHTNESS 0
> +#define MAX_BRIGHTNESS 24
> +#define GAMMA_TABLE_COUNT 26
> +
> +struct s6e8ax0 {
> + struct display_entity entity;
> + struct device *dev;
> +
> + struct s6e8ax0_platform_data *pdata;
> + struct backlight_device *bd;
> + struct lcd_device *ld;
> + struct regulator_bulk_data supplies[2];
> +
> + bool enabled;
> + unsigned int id;
> + unsigned int gamma;
> + unsigned int acl_enable;
> + unsigned int cur_acl;
> + int power;
> +
> + unsigned int reset_gpio;
> +};
> +
> +#define to_panel(p) container_of(p, struct s6e8ax0, entity)
> +
> +static const unsigned char s6e8ax0_22_gamma_30[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xf5, 0x00, 0xff, 0xad, 0xaf,
> + 0xbA, 0xc3, 0xd8, 0xc5, 0x9f, 0xc6, 0x9e, 0xc1, 0xdc, 0xc0,
> + 0x00, 0x61, 0x00, 0x5a, 0x00, 0x74,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_50[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xe8, 0x1f, 0xf7, 0xad, 0xc0,
> + 0xb5, 0xc4, 0xdc, 0xc4, 0x9e, 0xc6, 0x9c, 0xbb, 0xd8, 0xbb,
> + 0x00, 0x70, 0x00, 0x68, 0x00, 0x86,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_60[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xde, 0x1f, 0xef, 0xad, 0xc4,
> + 0xb3, 0xc3, 0xdd, 0xc4, 0x9e, 0xc6, 0x9c, 0xbc, 0xd6, 0xba,
> + 0x00, 0x75, 0x00, 0x6e, 0x00, 0x8d,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_70[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xd8, 0x1f, 0xe7, 0xaf, 0xc8,
> + 0xb4, 0xc4, 0xdd, 0xc3, 0x9d, 0xc6, 0x9c, 0xbb, 0xd6, 0xb9,
> + 0x00, 0x7a, 0x00, 0x72, 0x00, 0x93,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_80[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xc9, 0x1f, 0xde, 0xae, 0xc9,
> + 0xb1, 0xc3, 0xdd, 0xc2, 0x9d, 0xc5, 0x9b, 0xbc, 0xd6, 0xbb,
> + 0x00, 0x7f, 0x00, 0x77, 0x00, 0x99,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_90[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xc7, 0x1f, 0xd9, 0xb0, 0xcc,
> + 0xb2, 0xc3, 0xdc, 0xc1, 0x9c, 0xc6, 0x9c, 0xbc, 0xd4, 0xb9,
> + 0x00, 0x83, 0x00, 0x7b, 0x00, 0x9e,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_100[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xbd, 0x80, 0xcd, 0xba, 0xce,
> + 0xb3, 0xc4, 0xde, 0xc3, 0x9c, 0xc4, 0x9, 0xb8, 0xd3, 0xb6,
> + 0x00, 0x88, 0x00, 0x80, 0x00, 0xa5,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_120[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb9, 0x95, 0xc8, 0xb1, 0xcf,
> + 0xb2, 0xc6, 0xdf, 0xc5, 0x9b, 0xc3, 0x99, 0xb6, 0xd2, 0xb6,
> + 0x00, 0x8f, 0x00, 0x86, 0x00, 0xac,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_130[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc7, 0xb1, 0xd0,
> + 0xb2, 0xc4, 0xdd, 0xc3, 0x9a, 0xc3, 0x98, 0xb6, 0xd0, 0xb4,
> + 0x00, 0x92, 0x00, 0x8a, 0x00, 0xb1,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_140[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc5, 0xb2, 0xd0,
> + 0xb3, 0xc3, 0xde, 0xc3, 0x9b, 0xc2, 0x98, 0xb6, 0xd0, 0xb4,
> + 0x00, 0x95, 0x00, 0x8d, 0x00, 0xb5,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_150[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xa0, 0xc2, 0xb2, 0xd0,
> + 0xb2, 0xc1, 0xdd, 0xc2, 0x9b, 0xc2, 0x98, 0xb4, 0xcf, 0xb1,
> + 0x00, 0x99, 0x00, 0x90, 0x00, 0xba,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_160[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xa5, 0xbf, 0xb0, 0xd0,
> + 0xb1, 0xc3, 0xde, 0xc2, 0x99, 0xc1, 0x97, 0xb4, 0xce, 0xb1,
> + 0x00, 0x9c, 0x00, 0x93, 0x00, 0xbe,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_170[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb5, 0xbf, 0xb1, 0xd1,
> + 0xb1, 0xc3, 0xde, 0xc3, 0x99, 0xc0, 0x96, 0xb4, 0xce, 0xb1,
> + 0x00, 0x9f, 0x00, 0x96, 0x00, 0xc2,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_180[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb7, 0xbe, 0xb3, 0xd2,
> + 0xb3, 0xc3, 0xde, 0xc2, 0x97, 0xbf, 0x95, 0xb4, 0xcd, 0xb1,
> + 0x00, 0xa2, 0x00, 0x99, 0x00, 0xc5,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_190[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbe, 0xb2, 0xd2,
> + 0xb2, 0xc3, 0xdd, 0xc3, 0x98, 0xbf, 0x95, 0xb2, 0xcc, 0xaf,
> + 0x00, 0xa5, 0x00, 0x9c, 0x00, 0xc9,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_200[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbc, 0xb2, 0xd2,
> + 0xb1, 0xc4, 0xdd, 0xc3, 0x97, 0xbe, 0x95, 0xb1, 0xcb, 0xae,
> + 0x00, 0xa8, 0x00, 0x9f, 0x00, 0xcd,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_210[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc1, 0xbd, 0xb1, 0xd1,
> + 0xb1, 0xc2, 0xde, 0xc2, 0x97, 0xbe, 0x94, 0xB0, 0xc9, 0xad,
> + 0x00, 0xae, 0x00, 0xa4, 0x00, 0xd4,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_220[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc7, 0xbd, 0xb1, 0xd1,
> + 0xb1, 0xc2, 0xdd, 0xc2, 0x97, 0xbd, 0x94, 0xb0, 0xc9, 0xad,
> + 0x00, 0xad, 0x00, 0xa2, 0x00, 0xd3,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_230[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc3, 0xbd, 0xb2, 0xd1,
> + 0xb1, 0xc3, 0xdd, 0xc1, 0x96, 0xbd, 0x94, 0xb0, 0xc9, 0xad,
> + 0x00, 0xb0, 0x00, 0xa7, 0x00, 0xd7,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_240[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xcb, 0xbd, 0xb1, 0xd2,
> + 0xb1, 0xc3, 0xdD, 0xc2, 0x95, 0xbd, 0x93, 0xaf, 0xc8, 0xab,
> + 0x00, 0xb3, 0x00, 0xa9, 0x00, 0xdb,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_250[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xcc, 0xbe, 0xb0, 0xd2,
> + 0xb0, 0xc3, 0xdD, 0xc2, 0x94, 0xbc, 0x92, 0xae, 0xc8, 0xab,
> + 0x00, 0xb6, 0x00, 0xab, 0x00, 0xde,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_260[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xd0, 0xbe, 0xaf, 0xd1,
> + 0xaf, 0xc2, 0xdd, 0xc1, 0x96, 0xbc, 0x93, 0xaf, 0xc8, 0xac,
> + 0x00, 0xb7, 0x00, 0xad, 0x00, 0xe0,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_270[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xcF, 0xbd, 0xb0, 0xd2,
> + 0xaf, 0xc2, 0xdc, 0xc1, 0x95, 0xbd, 0x93, 0xae, 0xc6, 0xaa,
> + 0x00, 0xba, 0x00, 0xb0, 0x00, 0xe4,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_280[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xd0, 0xbd, 0xaf, 0xd0,
> + 0xad, 0xc4, 0xdd, 0xc3, 0x95, 0xbd, 0x93, 0xac, 0xc5, 0xa9,
> + 0x00, 0xbd, 0x00, 0xb2, 0x00, 0xe7,
> +};
> +
> +static const unsigned char s6e8ax0_22_gamma_300[] = {
> + 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb5, 0xd3, 0xbd, 0xb1, 0xd2,
> + 0xb0, 0xc0, 0xdc, 0xc0, 0x94, 0xba, 0x91, 0xac, 0xc5, 0xa9,
> + 0x00, 0xc2, 0x00, 0xb7, 0x00, 0xed,
> +};
> +
> +static const unsigned char *s6e8ax0_22_gamma_table[] = {
> + s6e8ax0_22_gamma_30,
> + s6e8ax0_22_gamma_50,
> + s6e8ax0_22_gamma_60,
> + s6e8ax0_22_gamma_70,
> + s6e8ax0_22_gamma_80,
> + s6e8ax0_22_gamma_90,
> + s6e8ax0_22_gamma_100,
> + s6e8ax0_22_gamma_120,
> + s6e8ax0_22_gamma_130,
> + s6e8ax0_22_gamma_140,
> + s6e8ax0_22_gamma_150,
> + s6e8ax0_22_gamma_160,
> + s6e8ax0_22_gamma_170,
> + s6e8ax0_22_gamma_180,
> + s6e8ax0_22_gamma_190,
> + s6e8ax0_22_gamma_200,
> + s6e8ax0_22_gamma_210,
> + s6e8ax0_22_gamma_220,
> + s6e8ax0_22_gamma_230,
> + s6e8ax0_22_gamma_240,
> + s6e8ax0_22_gamma_250,
> + s6e8ax0_22_gamma_260,
> + s6e8ax0_22_gamma_270,
> + s6e8ax0_22_gamma_280,
> + s6e8ax0_22_gamma_300,
> +};
> +
> +static void s6e8ax0_panel_cond(struct s6e8ax0 *lcd)
> +{
> + static const unsigned char data_to_send[] = {
> + 0xf8, 0x3d, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d,
> + 0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08,
> + 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0,
> + 0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc1, 0xff, 0xff, 0xc8
> + };
> + static const unsigned char data_to_send_panel_reverse[] = {
> + 0xf8, 0x19, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d,
> + 0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08,
> + 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0,
> + 0xc1, 0x01, 0x41, 0xc1, 0x00, 0xc1, 0xf6, 0xf6, 0xc1
> + };
> +
> + if (lcd->pdata->panel_reverse)
> + dsi_dcs_write(lcd->entity.source, 0,
> + data_to_send_panel_reverse,
> + ARRAY_SIZE(data_to_send_panel_reverse));
> + else
> + dsi_dcs_write(lcd->entity.source, 0,
> + data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_display_cond(struct s6e8ax0 *lcd)
> +{
> + static const unsigned char data_to_send[] = {
> + 0xf2, 0x80, 0x03, 0x0d
> + };
> +
> + dsi_dcs_write(lcd->entity.source, 0,
> + data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +/* Gamma 2.2 Setting (200cd, 7500K, 10MPCD) */
> +static void s6e8ax0_gamma_cond(struct s6e8ax0 *lcd)
> +{
> + unsigned int gamma = lcd->bd->props.brightness;
> +
> + dsi_dcs_write(lcd->entity.source, 0,
> + s6e8ax0_22_gamma_table[gamma],
> + GAMMA_TABLE_COUNT);
> +}
> +
> +static void s6e8ax0_gamma_update(struct s6e8ax0 *lcd)
> +{
> + static const unsigned char data_to_send[] = {
> + 0xf7, 0x03
> + };
> +
> + dsi_dcs_write(lcd->entity.source, 0, data_to_send,
> + ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_etc_cond1(struct s6e8ax0 *lcd)
> +{
> + static const unsigned char data_to_send[] = {
> + 0xd1, 0xfe, 0x80, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x40,
> + 0x0d, 0x00, 0x00
> + };
> +
> + dsi_dcs_write(lcd->entity.source, 0,
> + data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_etc_cond2(struct s6e8ax0 *lcd)
> +{
> + static const unsigned char data_to_send[] = {
> + 0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0,
> + 0x00
> + };
> +
> + dsi_dcs_write(lcd->entity.source, 0,
> + data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_etc_cond3(struct s6e8ax0 *lcd)
> +{
> + static const unsigned char data_to_send[] = {
> + 0xe1, 0x10, 0x1c, 0x17, 0x08, 0x1d
> + };
> +
> + dsi_dcs_write(lcd->entity.source, 0,
> + data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_etc_cond4(struct s6e8ax0 *lcd)
> +{
> + static const unsigned char data_to_send[] = {
> + 0xe2, 0xed, 0x07, 0xc3, 0x13, 0x0d, 0x03
> + };
> +
> + dsi_dcs_write(lcd->entity.source, 0,
> + data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_etc_cond5(struct s6e8ax0 *lcd)
> +{
> + static const unsigned char data_to_send[] = {
> + 0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x19, 0x33, 0x02
> + };
> +
> + dsi_dcs_write(lcd->entity.source, 0,
> + data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +static void s6e8ax0_etc_cond6(struct s6e8ax0 *lcd)
> +{
> + static const unsigned char data_to_send[] = {
> + 0xe3, 0x40
> + };
> +
> + dsi_dcs_write(lcd->entity.source, 0,
> + data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_etc_cond7(struct s6e8ax0 *lcd)
> +{
> + static const unsigned char data_to_send[] = {
> + 0xe4, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00
> + };
> +
> + dsi_dcs_write(lcd->entity.source, 0,
> + data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_elvss_set(struct s6e8ax0 *lcd)
> +{
> + static const unsigned char data_to_send[] = {
> + 0xb1, 0x04, 0x00
> + };
> +
> + dsi_dcs_write(lcd->entity.source, 0,
> + data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_elvss_nvm_set(struct s6e8ax0 *lcd)
> +{
> + static const unsigned char data_to_send[] = {
> + 0xd9, 0x5c, 0x20, 0x0c, 0x0f, 0x41, 0x00, 0x10, 0x11,
> + 0x12, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcb, 0xed,
> + 0x64, 0xaf
> + };
> +
> + dsi_dcs_write(lcd->entity.source, 0,
> + data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_sleep_in(struct s6e8ax0 *lcd)
> +{
> + static const unsigned char data_to_send[] = {
> + 0x10
> + };
> +
> + dsi_dcs_write(lcd->entity.source, 0,
> + data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_sleep_out(struct s6e8ax0 *lcd)
> +{
> + static const unsigned char data_to_send[] = {
> + 0x11
> + };
> +
> + dsi_dcs_write(lcd->entity.source, 0,
> + data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_display_on(struct s6e8ax0 *lcd)
> +{
> + static const unsigned char data_to_send[] = {
> + 0x29
> + };
> +
> + dsi_dcs_write(lcd->entity.source, 0,
> + data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_display_off(struct s6e8ax0 *lcd)
> +{
> + static const unsigned char data_to_send[] = {
> + 0x28
> + };
> +
> + dsi_dcs_write(lcd->entity.source, 0,
> + data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_apply_level2_key(struct s6e8ax0 *lcd)
> +{
> + static const unsigned char data_to_send[] = {
> + 0xf0, 0x5a, 0x5a
> + };
> +
> + dsi_dcs_write(lcd->entity.source, 0,
> + data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_acl_on(struct s6e8ax0 *lcd)
> +{
> + static const unsigned char data_to_send[] = {
> + 0xc0, 0x01
> + };
> +
> + dsi_dcs_write(lcd->entity.source, 0,
> + data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +static void s6e8ax0_acl_off(struct s6e8ax0 *lcd)
> +{
> + static const unsigned char data_to_send[] = {
> + 0xc0, 0x00
> + };
> +
> + dsi_dcs_write(lcd->entity.source, 0,
> + data_to_send, ARRAY_SIZE(data_to_send));
> +}
> +
> +/* Full white 50% reducing setting */
> +static void s6e8ax0_acl_ctrl_set(struct s6e8ax0 *lcd)
> +{
> + /* Full white 50% reducing setting */
> + static const unsigned char cutoff_50[] = {
> + 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
> + 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x01, 0x08, 0x0f, 0x16, 0x1d, 0x24, 0x2a, 0x31, 0x38,
> + 0x3f, 0x46
> + };
> + /* Full white 45% reducing setting */
> + static const unsigned char cutoff_45[] = {
> + 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
> + 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x01, 0x07, 0x0d, 0x13, 0x19, 0x1f, 0x25, 0x2b, 0x31,
> + 0x37, 0x3d
> + };
> + /* Full white 40% reducing setting */
> + static const unsigned char cutoff_40[] = {
> + 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
> + 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x01, 0x06, 0x0c, 0x11, 0x16, 0x1c, 0x21, 0x26, 0x2b,
> + 0x31, 0x36
> + };
> +
> + if (lcd->acl_enable) {
> + if (lcd->cur_acl = 0) {
> + if (lcd->gamma = 0 || lcd->gamma = 1) {
> + s6e8ax0_acl_off(lcd);
> + dev_dbg(lcd->dev,
> + "cur_acl=%d\n", lcd->cur_acl);
> + } else
> + s6e8ax0_acl_on(lcd);
> + }
> + switch (lcd->gamma) {
> + case 0: /* 30cd */
> + s6e8ax0_acl_off(lcd);
> + lcd->cur_acl = 0;
> + break;
> + case 1 ... 3: /* 50cd ~ 90cd */
> + dsi_dcs_write(lcd->entity.source, 0,
> + cutoff_40,
> + ARRAY_SIZE(cutoff_40));
> + lcd->cur_acl = 40;
> + break;
> + case 4 ... 7: /* 120cd ~ 210cd */
> + dsi_dcs_write(lcd->entity.source, 0,
> + cutoff_45,
> + ARRAY_SIZE(cutoff_45));
> + lcd->cur_acl = 45;
> + break;
> + case 8 ... 10: /* 220cd ~ 300cd */
> + dsi_dcs_write(lcd->entity.source, 0,
> + cutoff_50,
> + ARRAY_SIZE(cutoff_50));
> + lcd->cur_acl = 50;
> + break;
> + default:
> + break;
> + }
> + } else {
> + s6e8ax0_acl_off(lcd);
> + lcd->cur_acl = 0;
> + dev_dbg(lcd->dev, "cur_acl = %d\n", lcd->cur_acl);
> + }
> +}
> +
> +static void s6e8ax0_read_id(struct s6e8ax0 *lcd, u8 *mtp_id)
> +{
> + unsigned int ret;
> + u8 addr = 0xd1; /* MTP ID */
> +
> + ret = dsi_dcs_read(lcd->entity.source, 0, addr, mtp_id, 3);
> +}
> +
> +static int s6e8ax0_panel_init(struct s6e8ax0 *lcd)
> +{
> + s6e8ax0_apply_level2_key(lcd);
> + s6e8ax0_sleep_out(lcd);
> + msleep(1);
> + s6e8ax0_panel_cond(lcd);
> + s6e8ax0_display_cond(lcd);
> + s6e8ax0_gamma_cond(lcd);
> + s6e8ax0_gamma_update(lcd);
> +
> + s6e8ax0_etc_cond1(lcd);
> + s6e8ax0_etc_cond2(lcd);
> + s6e8ax0_etc_cond3(lcd);
> + s6e8ax0_etc_cond4(lcd);
> + s6e8ax0_etc_cond5(lcd);
> + s6e8ax0_etc_cond6(lcd);
> + s6e8ax0_etc_cond7(lcd);
> +
> + s6e8ax0_elvss_nvm_set(lcd);
> + s6e8ax0_elvss_set(lcd);
> +
> + s6e8ax0_acl_ctrl_set(lcd);
> + s6e8ax0_acl_on(lcd);
> +
> + /* if ID3 value is not 33h, branch private elvss mode */
> + msleep(lcd->pdata->power_on_delay);
> +
> + return 0;
> +}
> +
> +static int s6e8ax0_update_gamma_ctrl(struct s6e8ax0 *lcd, int brightness)
> +{
> + dsi_dcs_write(lcd->entity.source, 0,
> + s6e8ax0_22_gamma_table[brightness],
> + ARRAY_SIZE(s6e8ax0_22_gamma_table));
> +
> + /* update gamma table. */
> + s6e8ax0_gamma_update(lcd);
> + lcd->gamma = brightness;
> +
> + return 0;
> +}
> +
> +static int s6e8ax0_gamma_ctrl(struct s6e8ax0 *lcd, int gamma)
> +{
> + s6e8ax0_update_gamma_ctrl(lcd, gamma);
> +
> + return 0;
> +}
> +
> +static int s6e8ax0_get_brightness(struct backlight_device *bd)
> +{
> + return bd->props.brightness;
> +}
> +
> +static int s6e8ax0_set_brightness(struct backlight_device *bd)
> +{
> + int ret = 0, brightness = bd->props.brightness;
> + struct s6e8ax0 *lcd = bl_get_data(bd);
> +
> + if (brightness < MIN_BRIGHTNESS ||
> + brightness > bd->props.max_brightness) {
> + dev_err(lcd->dev,
> + "lcd brightness should be %d to %d.\n",
> + MIN_BRIGHTNESS, MAX_BRIGHTNESS);
> + return -EINVAL;
> + }
> +
> + ret = s6e8ax0_gamma_ctrl(lcd, brightness);
> + if (ret) {
> + dev_err(lcd->dev,
> + "lcd brightness setting failed.\n");
> + return -EIO;
> + }
> +
> + return ret;
> +}
> +
> +static const struct backlight_ops s6e8ax0_backlight_ops = {
> + .get_brightness = s6e8ax0_get_brightness,
> + .update_status = s6e8ax0_set_brightness,
> +};
> +
> +static int s6e8ax0_set_power(struct lcd_device *ld, int power)
> +{
> + struct s6e8ax0 *lcd = lcd_get_data(ld);
> + enum display_entity_state state;
> + int ret;
> +
> + if (power = FB_BLANK_POWERDOWN)
> + state = DISPLAY_ENTITY_STATE_OFF;
> + else if (power != FB_BLANK_UNBLANK)
> + state = DISPLAY_ENTITY_STATE_STANDBY;
> + else
> + state = DISPLAY_ENTITY_STATE_ON;
> +
> + ret = display_entity_set_state(&lcd->entity, state);
> + if (ret)
> + return ret;
> +
> + lcd->power = power;
> + return 0;
> +}
> +
> +static int s6e8ax0_get_power(struct lcd_device *ld)
> +{
> + struct s6e8ax0 *lcd = lcd_get_data(ld);
> +
> + return lcd->power;
> +}
> +
> +static struct lcd_ops s6e8ax0_lcd_ops = {
> + .set_power = s6e8ax0_set_power,
> + .get_power = s6e8ax0_get_power,
> +};
> +
> +static void s6e8ax0_set_sequence(struct s6e8ax0 *lcd)
> +{
> + u8 mtp_id[3] = {0, };
> +
> + msleep(100);
> +
> + s6e8ax0_read_id(lcd, mtp_id);
> + if (mtp_id[0] = 0x00)
> + dev_err(lcd->dev, "read id failed\n");
> +
> + dev_info(lcd->dev, "Read ID : %x, %x, %x\n",
> + mtp_id[0], mtp_id[1], mtp_id[2]);
> +
> + if (mtp_id[2] = 0x33)
> + dev_info(lcd->dev,
> + "ID-3 is 0xff does not support dynamic elvss\n");
> + else
> + dev_info(lcd->dev,
> + "ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]);
> +
> + s6e8ax0_panel_init(lcd);
> + s6e8ax0_display_on(lcd);
> +}
> +
> +#ifdef CONFIG_OF
> +static int s6e8ax0_generic_reset(struct device *dev)
> +{
> + struct s6e8ax0 *lcd = dev_get_drvdata(dev);
> +
> + mdelay(10);
> + gpio_set_value(lcd->reset_gpio, 0);
> + mdelay(10);
> + gpio_set_value(lcd->reset_gpio, 1);
> +
> + return 0;
> +}
> +
> +static struct s6e8ax0_platform_data *s6e8ax0_parse_dt(struct s6e8ax0 *lcd)
> +{
> + struct device_node *node = lcd->dev->of_node;
> + struct s6e8ax0_platform_data *data;
> + const __be32 *prop_data;
> + int ret;
> +
> + data = devm_kzalloc(lcd->dev, sizeof(*data), GFP_KERNEL);
> + if (!data) {
> + dev_err(lcd->dev, "failed to allocate platform data.\n");
> + return NULL;
> + }
> +
> + ret = of_get_videomode(node, &data->mode, 0);
> + if (ret) {
> + dev_err(lcd->dev, "failed to read video mode from DT\n");
> + return NULL;
> + }
> +
> + lcd->reset_gpio = of_get_named_gpio(node, "reset-gpio", 0);
> + if (lcd->reset_gpio < 0)
> + return NULL;
> +
> + prop_data = of_get_property(node, "reset-delay", NULL);
> + if (!prop_data)
> + return NULL;
> + data->reset_delay = be32_to_cpu(*prop_data);
> +
> + prop_data = of_get_property(node, "power-off-delay", NULL);
> + if (!prop_data)
> + return NULL;
> + data->power_off_delay = be32_to_cpu(*prop_data);
> +
> + prop_data = of_get_property(node, "power-on-delay", NULL);
> + if (!prop_data)
> + return NULL;
> + data->power_on_delay = be32_to_cpu(*prop_data);
> +
> + data->reset = s6e8ax0_generic_reset;
> +
> + return data;
> +}
> +
> +static struct of_device_id s6e8ax0_of_match[] = {
> + { .compatible = "samsung,s6e8ax0" },
> + { }
> +};
> +
> +MODULE_DEVICE_TABLE(of, s6e8ax0_of_match);
> +#else
> +static struct s6e8ax0_platform_data *s6e8ax0_parse_dt(struct s6e8ax0 *lcd)
> +{
> + return NULL;
> +}
> +#endif
> +
> +static const struct display_entity_interface_params s6e8ax0_params = {
> + .type = DISPLAY_ENTITY_INTERFACE_DSI,
> + .p.dsi = {
> + .format = DSI_FMT_RGB888,
> + .mode = DSI_MODE_VIDEO | DSI_MODE_VIDEO_BURST
> + | DSI_MODE_VIDEO_HFP | DSI_MODE_VIDEO_HBP
> + | DSI_MODE_VIDEO_HSA | DSI_MODE_EOT_PACKET
> + | DSI_MODE_VSYNC_FLUSH,
> + .data_lanes = 0xf,
> + .hs_clk_freq = 500000000,
> + .esc_clk_freq = 10000000,
> + },
> +};
> +
> +static int s6e8ax0_set_state(struct display_entity *entity,
> + enum display_entity_state state)
> +{
> + struct s6e8ax0 *panel = to_panel(entity);
> +
> + switch (state) {
> + case DISPLAY_ENTITY_STATE_OFF:
> + if (entity->state = DISPLAY_ENTITY_STATE_ON) {
> + s6e8ax0_sleep_in(panel);
> + panel->entity.source->common_ops->set_stream(entity->source,
> + DISPLAY_ENTITY_STREAM_STOPPED);
> +
> + msleep(panel->pdata->power_off_delay);
> + s6e8ax0_display_off(panel);
> +
> + panel->entity.source->ops.dsi->disable(entity->source);
> +
> + regulator_bulk_disable(ARRAY_SIZE(panel->supplies),
> + panel->supplies);
> + } else if (entity->state = DISPLAY_ENTITY_STATE_STANDBY) {
> + msleep(panel->pdata->power_off_delay);
> + s6e8ax0_display_off(panel);
> +
> + panel->entity.source->ops.dsi->disable(entity->source);
> +
> + regulator_bulk_disable(ARRAY_SIZE(panel->supplies),
> + panel->supplies);
> + }
> + break;
> +
> + case DISPLAY_ENTITY_STATE_STANDBY:
> + if (entity->state = DISPLAY_ENTITY_STATE_ON) {
> + s6e8ax0_sleep_in(panel);
> + panel->entity.source->common_ops->set_stream(entity->source,
> + DISPLAY_ENTITY_STREAM_STOPPED);
> + } else if (entity->state = DISPLAY_ENTITY_STATE_OFF) {
> + msleep(panel->pdata->power_on_delay);
> +
> + regulator_bulk_enable(ARRAY_SIZE(panel->supplies),
> + panel->supplies);
> +
> + msleep(panel->pdata->reset_delay);
> +
> + /* lcd reset */
> + if (panel->pdata->reset)
> + panel->pdata->reset(panel->dev);
> + msleep(5);
> +
> + panel->entity.source->ops.dsi->enable(entity->source);
> +
> + s6e8ax0_set_sequence(panel);
> + s6e8ax0_sleep_in(panel);
> + }
> + break;
> +
> + case DISPLAY_ENTITY_STATE_ON:
> + if (entity->state = DISPLAY_ENTITY_STATE_OFF) {
> + msleep(panel->pdata->power_on_delay);
> +
> + regulator_bulk_enable(ARRAY_SIZE(panel->supplies),
> + panel->supplies);
> +
> + msleep(panel->pdata->reset_delay);
> +
> + /* lcd reset */
> + if (panel->pdata->reset)
> + panel->pdata->reset(panel->dev);
> + msleep(5);
> +
> + panel->entity.source->ops.dsi->enable(entity->source);
> +
> + s6e8ax0_set_sequence(panel);
> +
> + panel->entity.source->common_ops->set_stream(entity->source,
> + DISPLAY_ENTITY_STREAM_CONTINUOUS);
> + } else {
> + panel->entity.source->common_ops->set_stream(entity->source,
> + DISPLAY_ENTITY_STREAM_CONTINUOUS);
> + s6e8ax0_sleep_out(panel);
> + }
> + break;
> +
> + default:
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static int s6e8ax0_get_modes(struct display_entity *entity,
> + const struct videomode **modes)
> +{
> + struct s6e8ax0 *panel = to_panel(entity);
> +
> + *modes = &panel->pdata->mode;
> + return 1;
> +}
> +
> +static int s6e8ax0_get_size(struct display_entity *entity,
> + unsigned int *width, unsigned int *height)
> +{
> + struct s6e8ax0 *panel = to_panel(entity);
> +
> + *width = panel->pdata->width;
> + *height = panel->pdata->height;
> + return 0;
> +}
> +
> +static int s6e8ax0_get_params(struct display_entity *entity,
> + struct display_entity_interface_params *params)
> +{
> + *params = s6e8ax0_params;
> + return 0;
> +}
> +
> +static const struct display_entity_control_ops s6e8ax0_control_ops = {
> + .set_state = s6e8ax0_set_state,
> + .get_modes = s6e8ax0_get_modes,
> + .get_size = s6e8ax0_get_size,
> + .get_params = s6e8ax0_get_params,
> +};
> +
> +static void s6e8ax0_release(struct display_entity *entity)
> +{
> + struct s6e8ax0 *panel = to_panel(entity);
> +
> + backlight_device_unregister(panel->bd);
> + lcd_device_unregister(panel->ld);
> + regulator_bulk_free(ARRAY_SIZE(panel->supplies), panel->supplies);
> + kfree(panel);
> +}
> +
> +static int s6e8ax0_probe(struct platform_device *pdev)
> +{
> + struct s6e8ax0 *lcd;
> + int ret;
> +
> + lcd = kzalloc(sizeof(struct s6e8ax0), GFP_KERNEL);
> + if (!lcd) {
> + dev_err(&pdev->dev, "failed to allocate s6e8ax0 structure.\n");
> + return -ENOMEM;
> + }
> +
> + lcd->dev = &pdev->dev;
> + lcd->pdata = (struct s6e8ax0_platform_data *)pdev->dev.platform_data;
> +
> + if (!lcd->pdata) {
> + lcd->pdata = s6e8ax0_parse_dt(lcd);
> + if (!lcd->pdata) {
> + dev_err(&pdev->dev, "failed to find platform data\n");
> + return -ENODEV;
> + }
> + }
> +
> + lcd->supplies[0].supply = "vdd3";
> + lcd->supplies[1].supply = "vci";
> + ret = regulator_bulk_get(&pdev->dev,
> + ARRAY_SIZE(lcd->supplies), lcd->supplies);
> + if (ret) {
> + dev_err(&pdev->dev, "Failed to get regulators: %d\n", ret);
> + goto err_regulator_bulk_get;
> + }
> +
> + lcd->ld = lcd_device_register("s6e8ax0", &pdev->dev, lcd,
> + &s6e8ax0_lcd_ops);
> + if (IS_ERR(lcd->ld)) {
> + dev_err(&pdev->dev, "failed to register lcd ops.\n");
> + ret = PTR_ERR(lcd->ld);
> + goto err_lcd_register;
> + }
> +
> + lcd->bd = backlight_device_register("s6e8ax0-bl", &pdev->dev, lcd,
> + &s6e8ax0_backlight_ops, NULL);
> + if (IS_ERR(lcd->bd)) {
> + dev_err(&pdev->dev, "failed to register backlight ops.\n");
> + ret = PTR_ERR(lcd->bd);
> + goto err_backlight_register;
> + }
> +
I think we should try to remove the dependency with LCD framework and
Backlight framework, and incorporate those functionality as par of
CDF.
you can refer to my similar patch "Make s6e8ax0 panel driver compliant
with CDF" ( http://comments.gmane.org/gmane.linux.drivers.video-input-infrastructure/59187
) which i had posted couple of weeks back, where I made an attempt to
remove "lcd_ops" dependency.
> + lcd->acl_enable = 1;
> + lcd->cur_acl = 0;
> + lcd->power = FB_BLANK_UNBLANK;
> +
> + lcd->bd->props.max_brightness = MAX_BRIGHTNESS;
> + lcd->bd->props.brightness = MAX_BRIGHTNESS;
> +
> + lcd->entity.of_node = pdev->dev.of_node;
> + lcd->entity.dev = &pdev->dev;
> + lcd->entity.release = s6e8ax0_release;
> + lcd->entity.ops = &s6e8ax0_control_ops;
> +
> + platform_set_drvdata(pdev, lcd);
> +
> + ret = display_entity_register(&lcd->entity);
> + if (ret < 0)
> + goto err_display_register;
> +
> + display_entity_set_state(&lcd->entity, DISPLAY_ENTITY_STATE_ON);
> +
> + dev_dbg(&pdev->dev, "probed s6e8ax0 panel driver.\n");
> +
> + return 0;
> +
> +err_display_register:
> + backlight_device_unregister(lcd->bd);
> +err_backlight_register:
> + lcd_device_unregister(lcd->ld);
> +err_lcd_register:
> + regulator_bulk_free(ARRAY_SIZE(lcd->supplies), lcd->supplies);
> +err_regulator_bulk_get:
> + kfree(lcd);
> +
> + return ret;
> +}
> +
> +static int s6e8ax0_remove(struct platform_device *dev)
> +{
> + struct s6e8ax0 *lcd = platform_get_drvdata(dev);
> +
> + platform_set_drvdata(dev, NULL);
> + display_entity_unregister(&lcd->entity);
> +
> + return 0;
> +}
> +
> +static int s6e8ax0_suspend(struct device *dev)
> +{
> + struct s6e8ax0 *lcd = dev_get_drvdata(dev);
> +
> + if (lcd->power = FB_BLANK_UNBLANK)
> + display_entity_set_state(&lcd->entity,
> + DISPLAY_ENTITY_STATE_OFF);
> +
> + return 0;
> +}
> +
> +static int s6e8ax0_resume(struct device *dev)
> +{
> + struct s6e8ax0 *lcd = dev_get_drvdata(dev);
> +
> + if (lcd->power = FB_BLANK_UNBLANK)
> + display_entity_set_state(&lcd->entity,
> + DISPLAY_ENTITY_STATE_ON);
> +
> + return 0;
> +}
> +
> +static struct dev_pm_ops s6e8ax0_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(s6e8ax0_suspend, s6e8ax0_resume)
> +};
> +
> +static struct platform_driver s6e8ax0_driver = {
> + .probe = s6e8ax0_probe,
> + .remove = s6e8ax0_remove,
> + .driver = {
> + .name = "panel_s6e8ax0",
> + .owner = THIS_MODULE,
> + .of_match_table = of_match_ptr(s6e8ax0_of_match),
> + .pm = &s6e8ax0_pm_ops,
> + },
> +};
> +module_platform_driver(s6e8ax0_driver);
> +
> +MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
> +MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
> +MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
> +MODULE_DESCRIPTION("MIPI-DSI based s6e8ax0 AMOLED LCD Panel Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/video/panel-s6e8ax0.h b/include/video/panel-s6e8ax0.h
> new file mode 100644
> index 0000000..e522bfb
> --- /dev/null
> +++ b/include/video/panel-s6e8ax0.h
> @@ -0,0 +1,41 @@
> +/*
> + * Renesas R61505-based Display Panels
> + *
> + * Copyright (C) 2012 Renesas Solutions Corp.
> + *
> + * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#ifndef __PANEL_S6E8AX0_H__
> +#define __PANEL_S6E8AX0_H__
> +
> +#include <linux/videomode.h>
> +
> +struct s6e8ax0_platform_data {
> + unsigned long width; /* Panel width in mm */
> + unsigned long height; /* Panel height in mm */
> + struct videomode mode;
> +
> + /* reset lcd panel device. */
> + int (*reset)(struct device *dev);
> +
> + /* it indicates whether lcd panel was enabled
> + from bootloader or not. */
> + int lcd_enabled;
> + /* it means delay for stable time when it becomes low to high
> + or high to low that is dependent on whether reset gpio is
> + low active or high active. */
> + unsigned int reset_delay;
> + /* stable time needing to become lcd power on. */
> + unsigned int power_on_delay;
> + /* stable time needing to become lcd power off. */
> + unsigned int power_off_delay;
> + /* panel is reversed */
> + bool panel_reverse;
> +};
> +
> +#endif /* __PANEL_S6E8AX0_H__ */
> --
> 1.8.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" 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: [RFC PATCH 4/4] video: display: Add Samsung s6e8ax0 display panel driver
From: Tomasz Figa @ 2013-02-07 10:18 UTC (permalink / raw)
To: Vikas Sajjan
Cc: dri-devel, linux-fbdev, linux-samsung-soc, kyungmin.park,
m.szyprowski, tomasz.figa, Daniel Vetter, Marcus Lorentzon,
Laurent Pinchart, rob, tomi.valkeinen, inki.dae, dh09.lee,
ville.syrjala, s.nawrocki, aditya.ps, sunil joshi
In-Reply-To: <CAGm_ybj-oN6OtVr5EAcH5w=cRfkVphfrwHTer40wgas1venbBQ@mail.gmail.com>
On Thursday 07 of February 2013 15:04:30 Vikas Sajjan wrote:
> Hi Figa,
>
> On Wed, Jan 30, 2013 at 9:09 PM, Tomasz Figa <t.figa@samsung.com> wrote:
> > This patch adds Common Display Framework driver for Samsung s6e8ax0
> > MIPI DSI display panel.
> >
> > Signed-off-by: Tomasz Figa <t.figa@samsung.com>
> > Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> > ---
> >
> > drivers/video/display/Kconfig | 3 +
> > drivers/video/display/Makefile | 1 +
> > drivers/video/display/panel-s6e8ax0.c | 1027
> > +++++++++++++++++++++++++++++++++ include/video/panel-s6e8ax0.h
> > | 41 ++
> > 4 files changed, 1072 insertions(+)
> > create mode 100644 drivers/video/display/panel-s6e8ax0.c
> > create mode 100644 include/video/panel-s6e8ax0.h
> >
[snip]
> > + lcd->ld = lcd_device_register("s6e8ax0", &pdev->dev, lcd,
> > + &s6e8ax0_lcd_ops);
> > + if (IS_ERR(lcd->ld)) {
> > + dev_err(&pdev->dev, "failed to register lcd ops.\n");
> > + ret = PTR_ERR(lcd->ld);
> > + goto err_lcd_register;
> > + }
> > +
> > + lcd->bd = backlight_device_register("s6e8ax0-bl", &pdev->dev,
> > lcd, + &s6e8ax0_backlight_ops, NULL);
> > + if (IS_ERR(lcd->bd)) {
> > + dev_err(&pdev->dev, "failed to register backlight
> > ops.\n"); + ret = PTR_ERR(lcd->bd);
> > + goto err_backlight_register;
> > + }
> > +
>
> I think we should try to remove the dependency with LCD framework and
> Backlight framework, and incorporate those functionality as par of
> CDF.
> you can refer to my similar patch "Make s6e8ax0 panel driver compliant
> with CDF" (
> http://comments.gmane.org/gmane.linux.drivers.video-input-infrastructur
> e/59187 ) which i had posted couple of weeks back, where I made an
> attempt to remove "lcd_ops" dependency.
Yes, I have written in the cover letter that those interfaces is just a
hack to be able to control the display from userspace in current state of
CDF.
I agree that CDF will have to be extended with backlight/brightness
control. However currently CDF does not expose any interface to userspace.
Laurent, what's your opinion on this?
P.S. Tomasz is my first name.
Best regards,
--
Tomasz Figa
Samsung Poland R&D Center
SW Solution Development, Linux Platform
^ permalink raw reply
* [RFC PATCH v2 2/5] drm/edid: temporarily exposing generic edid-read interface from drm
From: Rahul Sharma @ 2013-02-07 11:49 UTC (permalink / raw)
To: linux-media, dri-devel, alsa-devel, linux-fbdev
Cc: tomi.valkeinen, laurent.pinchart, broonie, inki.dae,
kyungmin.park, r.sh.open, joshi
It exposes generic interface from drm_edid.c to get the edid data and length
by any display entity. Once I get clear idea about edid handling in CDF, I need
to revert these temporary changes.
Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
---
drivers/gpu/drm/drm_edid.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 88 insertions(+)
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 5a3770f..567a565 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -31,6 +31,7 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/module.h>
+#include <video/display.h>
#include <drm/drmP.h>
#include <drm/drm_edid.h>
#include "drm_edid_modes.h"
@@ -386,6 +387,93 @@ out:
return NULL;
}
+int generic_drm_do_get_edid(struct i2c_adapter *adapter,
+ struct display_entity_edid *edid)
+{
+ int i, j = 0, valid_extensions = 0;
+ u8 *block, *new;
+ bool print_bad_edid = 0;
+
+ block = kmalloc(EDID_LENGTH, GFP_KERNEL);
+ if (!block)
+ return -ENOMEM;
+
+ /* base block fetch */
+ for (i = 0; i < 4; i++) {
+ if (drm_do_probe_ddc_edid(adapter, block, 0, EDID_LENGTH))
+ goto out;
+ if (drm_edid_block_valid(block, 0, print_bad_edid))
+ break;
+ if (i = 0 && drm_edid_is_zero(block, EDID_LENGTH))
+ goto carp;
+ }
+ if (i = 4)
+ goto carp;
+
+ /* if there's no extensions, we're done */
+ if (block[0x7e] = 0) {
+ edid->edid = block;
+ edid->len = EDID_LENGTH;
+ return 0;
+ }
+
+ new = krealloc(block, (block[0x7e] + 1) * EDID_LENGTH, GFP_KERNEL);
+ if (!new)
+ goto out;
+ block = new;
+ edid->len = (block[0x7e] + 1) * EDID_LENGTH;
+
+ for (j = 1; j <= block[0x7e]; j++) {
+ for (i = 0; i < 4; i++) {
+ if (drm_do_probe_ddc_edid(adapter,
+ block + (valid_extensions + 1) * EDID_LENGTH,
+ j, EDID_LENGTH))
+ goto out;
+ if (drm_edid_block_valid(block + (valid_extensions + 1)*
+ EDID_LENGTH, j, print_bad_edid)) {
+ valid_extensions++;
+ break;
+ }
+ }
+ if (i = 4)
+ DRM_DEBUG_KMS("Ignoring inv lock %d.\n", j);
+ }
+
+ if (valid_extensions != block[0x7e]) {
+ block[EDID_LENGTH-1] += block[0x7e] - valid_extensions;
+ block[0x7e] = valid_extensions;
+ new = krealloc(block, (valid_extensions + 1)*
+ EDID_LENGTH, GFP_KERNEL);
+ if (!new)
+ goto out;
+ block = new;
+ edid->len = (valid_extensions + 1) * EDID_LENGTH;
+ }
+
+ edid->edid = block;
+ return 0;
+
+carp:
+ if (print_bad_edid)
+ DRM_DEBUG_KMS("[ERROR]: EDID block %d invalid.\n", j);
+
+out:
+ kfree(block);
+ return -ENOMEM;
+}
+
+
+int generic_drm_get_edid(struct i2c_adapter *adapter,
+ struct display_entity_edid *edid)
+{
+ int ret = -EINVAL;
+ if (drm_probe_ddc(adapter))
+ ret = generic_drm_do_get_edid(adapter, edid);
+
+ return ret;
+}
+EXPORT_SYMBOL(generic_drm_get_edid);
+
/**
* Probe DDC presence.
*
--
1.8.0
^ permalink raw reply related
* [RFC PATCH v2 3/5] drm/exynos: moved drm hdmi driver to cdf framework
From: Rahul Sharma @ 2013-02-07 11:52 UTC (permalink / raw)
To: linux-media, dri-devel, alsa-devel, linux-fbdev
Cc: tomi.valkeinen, laurent.pinchart, broonie, inki.dae,
kyungmin.park, r.sh.open, joshi
This patch implements exynos_hdmi_cdf.c which is a glue component between
exynos DRM and hdmi cdf panel. It is a platform driver register through
exynos_drm_drv.c. Exynos_hdmi.c is modified to register hdmi as display panel.
exynos_hdmi_cdf.c registers for exynos hdmi display entity and if successful,
proceeds for mode setting.
Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
---
drivers/gpu/drm/exynos/Kconfig | 6 +
drivers/gpu/drm/exynos/Makefile | 1 +
drivers/gpu/drm/exynos/exynos_drm_drv.c | 24 ++
drivers/gpu/drm/exynos/exynos_drm_drv.h | 1 +
drivers/gpu/drm/exynos/exynos_hdmi.c | 445 ++++++++++++++++---------------
drivers/gpu/drm/exynos/exynos_hdmi_cdf.c | 370 +++++++++++++++++++++++++
include/video/exynos_hdmi.h | 25 ++
7 files changed, 658 insertions(+), 214 deletions(-)
create mode 100644 drivers/gpu/drm/exynos/exynos_hdmi_cdf.c
create mode 100644 include/video/exynos_hdmi.h
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 1d1f1e5..309e62a 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -34,6 +34,12 @@ config DRM_EXYNOS_HDMI
help
Choose this option if you want to use Exynos HDMI for DRM.
+config DRM_EXYNOS_HDMI_CDF
+ bool "Exynos DRM HDMI using CDF"
+ depends on DRM_EXYNOS_HDMI && DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_TV
+ help
+ Choose this option if you want to use Exynos HDMI for DRM using CDF.
+
config DRM_EXYNOS_VIDI
bool "Exynos DRM Virtual Display"
depends on DRM_EXYNOS
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile
index 639b49e..e946ed6 100644
--- a/drivers/gpu/drm/exynos/Makefile
+++ b/drivers/gpu/drm/exynos/Makefile
@@ -20,5 +20,6 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o
exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC) += exynos_drm_fimc.o
exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR) += exynos_drm_rotator.o
exynosdrm-$(CONFIG_DRM_EXYNOS_GSC) += exynos_drm_gsc.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI_CDF) += exynos_hdmi_cdf.o
obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index 3da5c2d..7876c3c 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -40,6 +40,9 @@
/* platform device pointer for eynos drm device. */
static struct platform_device *exynos_drm_pdev;
+/* platform device pointer for eynos hdmi cdf device. */
+static struct platform_device *exynos_hdmi_cdf_pdev;
+
static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
{
struct exynos_drm_private *private;
@@ -331,6 +334,18 @@ static int __init exynos_drm_init(void)
#endif
#ifdef CONFIG_DRM_EXYNOS_HDMI
+
+ ret = platform_driver_register(&hdmi_cdf_driver);
+ if (ret < 0)
+ goto out_hdmi_cdf_driver;
+
+ exynos_hdmi_cdf_pdev = platform_device_register_simple(
+ "exynos-hdmi-cdf", -1, NULL, 0);
+ if (IS_ERR_OR_NULL(exynos_hdmi_cdf_pdev)) {
+ ret = PTR_ERR(exynos_hdmi_cdf_pdev);
+ goto out_hdmi_cdf_device;
+ }
+
ret = platform_driver_register(&hdmi_driver);
if (ret < 0)
goto out_hdmi;
@@ -438,6 +453,13 @@ out_common_hdmi:
out_mixer:
platform_driver_unregister(&hdmi_driver);
out_hdmi:
+
+out_hdmi_cdf_device:
+ platform_device_unregister(exynos_hdmi_cdf_pdev);
+
+out_hdmi_cdf_driver:
+ platform_driver_unregister(&hdmi_cdf_driver);
+
#endif
#ifdef CONFIG_DRM_EXYNOS_FIMD
@@ -480,6 +502,8 @@ static void __exit exynos_drm_exit(void)
platform_driver_unregister(&exynos_drm_common_hdmi_driver);
platform_driver_unregister(&mixer_driver);
platform_driver_unregister(&hdmi_driver);
+ platform_driver_unregister(&hdmi_cdf_driver);
+ platform_device_unregister(exynos_hdmi_cdf_pdev);
#endif
#ifdef CONFIG_DRM_EXYNOS_VIDI
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index b9e51bc..961fe14 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -332,6 +332,7 @@ void exynos_platform_device_hdmi_unregister(void);
extern struct platform_driver fimd_driver;
extern struct platform_driver hdmi_driver;
extern struct platform_driver mixer_driver;
+extern struct platform_driver hdmi_cdf_driver;
extern struct platform_driver exynos_drm_common_hdmi_driver;
extern struct platform_driver vidi_driver;
extern struct platform_driver g2d_driver;
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
index 6f844b1..548cd32 100755
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -34,13 +34,12 @@
#include <linux/regulator/consumer.h>
#include <linux/io.h>
#include <linux/of_gpio.h>
+#include <video/display.h>
+#include "video/exynos_hdmi.h"
#include <plat/gpio-cfg.h>
#include <drm/exynos_drm.h>
-#include "exynos_drm_drv.h"
-#include "exynos_drm_hdmi.h"
-
#include "exynos_hdmi.h"
#include <linux/gpio.h>
@@ -157,14 +156,12 @@ struct hdmi_v14_conf {
struct hdmi_context {
struct device *dev;
- struct drm_device *drm_dev;
bool hpd;
bool powered;
bool dvi_mode;
struct mutex hdmi_mutex;
void __iomem *regs;
- void *parent_ctx;
int external_irq;
int internal_irq;
@@ -180,6 +177,7 @@ struct hdmi_context {
int hpd_gpio;
enum hdmi_type type;
+ struct display_entity entity;
};
/* HDMI Version 1.3 */
@@ -973,39 +971,8 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata,
}
}
-static bool hdmi_is_connected(void *ctx)
-{
- struct hdmi_context *hdata = ctx;
-
- return hdata->hpd;
-}
-
-static int hdmi_get_edid(void *ctx, struct drm_connector *connector,
- u8 *edid, int len)
-{
- struct edid *raw_edid;
- struct hdmi_context *hdata = ctx;
-
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
- if (!hdata->ddc_port)
- return -ENODEV;
-
- raw_edid = drm_get_edid(connector, hdata->ddc_port->adapter);
- if (raw_edid) {
- hdata->dvi_mode = !drm_detect_hdmi_monitor(raw_edid);
- memcpy(edid, raw_edid, min((1 + raw_edid->extensions)
- * EDID_LENGTH, len));
- DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n",
- (hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"),
- raw_edid->width_cm, raw_edid->height_cm);
- kfree(raw_edid);
- } else {
- return -ENODEV;
- }
-
- return 0;
-}
+extern int generic_drm_get_edid(struct i2c_adapter *adapter,
+ struct display_entity_edid *edid);
static int hdmi_v13_check_timing(struct fb_videomode *check_timing)
{
@@ -1061,22 +1028,6 @@ static int hdmi_v14_check_timing(struct fb_videomode *check_timing)
return -EINVAL;
}
-static int hdmi_check_timing(void *ctx, struct fb_videomode *timing)
-{
- struct hdmi_context *hdata = ctx;
-
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
- DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", timing->xres,
- timing->yres, timing->refresh,
- timing->vmode);
-
- if (hdata->type = HDMI_TYPE13)
- return hdmi_v13_check_timing(timing);
- else
- return hdmi_v14_check_timing(timing);
-}
-
static void hdmi_set_acr(u32 freq, u8 *acr)
{
u32 n, cts;
@@ -1143,15 +1094,22 @@ static void hdmi_reg_acr(struct hdmi_context *hdata, u8 *acr)
hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
}
-static void hdmi_audio_init(struct hdmi_context *hdata)
+static void hdmi_spdif_audio_init(struct hdmi_context *hdata,
+ const struct display_entity_audio_params *params)
+{
+ DRM_ERROR("SPDIF AUDIO NOT IMPLEMENTED YET");
+}
+
+static void hdmi_i2s_audio_init(struct hdmi_context *hdata,
+ const struct display_entity_audio_params *params)
{
u32 sample_rate, bits_per_sample, frame_size_code;
u32 data_num, bit_ch, sample_frq;
u32 val;
u8 acr[7];
- sample_rate = 44100;
- bits_per_sample = 16;
+ sample_rate = params->sf;
+ bits_per_sample = params->bits_per_sample;
frame_size_code = 0;
switch (bits_per_sample) {
@@ -1685,8 +1643,6 @@ static void hdmi_conf_apply(struct hdmi_context *hdata)
hdmi_conf_init(hdata);
mutex_unlock(&hdata->hdmi_mutex);
- hdmi_audio_init(hdata);
-
/* setting core registers */
hdmi_timing_apply(hdata);
hdmi_audio_control(hdata, true);
@@ -1694,58 +1650,6 @@ static void hdmi_conf_apply(struct hdmi_context *hdata)
hdmi_regs_dump(hdata, "start");
}
-static void hdmi_mode_fixup(void *ctx, struct drm_connector *connector,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- struct drm_display_mode *m;
- struct hdmi_context *hdata = ctx;
- int index;
-
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
- drm_mode_set_crtcinfo(adjusted_mode, 0);
-
- if (hdata->type = HDMI_TYPE13)
- index = hdmi_v13_conf_index(adjusted_mode);
- else
- index = hdmi_v14_find_phy_conf(adjusted_mode->clock * 1000);
-
- /* just return if user desired mode exists. */
- if (index >= 0)
- return;
-
- /*
- * otherwise, find the most suitable mode among modes and change it
- * to adjusted_mode.
- */
- list_for_each_entry(m, &connector->modes, head) {
- if (hdata->type = HDMI_TYPE13)
- index = hdmi_v13_conf_index(m);
- else
- index = hdmi_v14_find_phy_conf(m->clock * 1000);
-
- if (index >= 0) {
- struct drm_mode_object base;
- struct list_head head;
-
- DRM_INFO("desired mode doesn't exist so\n");
- DRM_INFO("use the most suitable mode among modes.\n");
-
- DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n",
- m->hdisplay, m->vdisplay, m->vrefresh);
-
- /* preserve display mode header while copying. */
- head = adjusted_mode->head;
- base = adjusted_mode->base;
- memcpy(adjusted_mode, m, sizeof(*m));
- adjusted_mode->head = head;
- adjusted_mode->base = base;
- break;
- }
- }
-}
-
static void hdmi_set_reg(u8 *reg_pair, int num_bytes, u32 value)
{
int i;
@@ -1862,42 +1766,6 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,
}
-static void hdmi_mode_set(void *ctx, void *mode)
-{
- struct hdmi_context *hdata = ctx;
- int conf_idx;
-
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
- if (hdata->type = HDMI_TYPE13) {
- conf_idx = hdmi_v13_conf_index(mode);
- if (conf_idx >= 0)
- hdata->cur_conf = conf_idx;
- else
- DRM_DEBUG_KMS("not supported mode\n");
- } else {
- hdmi_v14_mode_set(hdata, mode);
- }
-}
-
-static void hdmi_get_max_resol(void *ctx, unsigned int *width,
- unsigned int *height)
-{
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
- *width = MAX_WIDTH;
- *height = MAX_HEIGHT;
-}
-
-static void hdmi_commit(void *ctx)
-{
- struct hdmi_context *hdata = ctx;
-
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
- hdmi_conf_apply(hdata);
-}
-
static void hdmi_poweron(struct hdmi_context *hdata)
{
struct hdmi_resources *res = &hdata->res;
@@ -1953,62 +1821,215 @@ out:
mutex_unlock(&hdata->hdmi_mutex);
}
-static void hdmi_dpms(void *ctx, int mode)
+int hdmi_get_size(struct display_entity *ent,
+ unsigned int *width, unsigned int *height)
{
- struct hdmi_context *hdata = ctx;
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
- DRM_DEBUG_KMS("[%d] %s mode %d\n", __LINE__, __func__, mode);
+ *width = MAX_WIDTH;
+ *height = MAX_HEIGHT;
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- if (pm_runtime_suspended(hdata->dev))
- pm_runtime_get_sync(hdata->dev);
- break;
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
+ return 0;
+}
+
+void hdmi_send_hpdevent(struct display_entity *entity, int hpd)
+{
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ display_entity_notify_event_subscriber(entity,
+ DISPLAY_ENTITY_HDMI_HOTPLUG, hpd);
+}
+
+int hdmi_get_hpdstate(struct display_entity *entity, unsigned int *hpd_state)
+{
+ struct hdmi_context *hdata + container_of(entity, struct hdmi_context, entity);
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ if (hpd_state) {
+ *hpd_state = hdata->hpd;
+ return 0;
+ }
+ return -1;
+}
+
+int hdmi_get_edid(struct display_entity *entity,
+ struct display_entity_edid *edid)
+{
+ struct hdmi_context *hdata + container_of(entity, struct hdmi_context, entity);
+ struct edid *raw_edid;
+ int ret;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ if (!hdata->ddc_port)
+ return -ENODEV;
+
+ ret = generic_drm_get_edid(hdata->ddc_port->adapter, edid);
+ if (ret) {
+ DRM_ERROR("[%d]%s, Edid Read Fail!!! ret = %d\n",
+ __LINE__, __func__, ret);
+ return -EINVAL;
+ }
+
+ raw_edid = (struct edid *)edid->edid;
+
+ if (raw_edid)
+ hdata->dvi_mode = !drm_detect_hdmi_monitor(raw_edid);
+ else
+ return -ENODEV;
+
+ return 0;
+}
+
+int hdmi_check_mode(struct display_entity *entity,
+ const struct videomode *mode)
+{
+ struct hdmi_context *hdata + container_of(entity, struct hdmi_context, entity);
+ struct fb_videomode *timing = (struct fb_videomode *)mode;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", timing->xres,
+ timing->yres, timing->refresh,
+ timing->vmode);
+
+ if (hdata->type = HDMI_TYPE13)
+ return hdmi_v13_check_timing(timing);
+ else
+ return hdmi_v14_check_timing(timing);
+}
+
+int hdmi_set_mode(struct display_entity *entity,
+ const struct videomode *mode)
+{
+ struct hdmi_context *hdata + container_of(entity, struct hdmi_context, entity);
+ struct drm_display_mode *m = (struct drm_display_mode *)mode;
+ int conf_idx;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ if (hdata->type = HDMI_TYPE13) {
+ conf_idx = hdmi_v13_conf_index(m);
+ if (conf_idx >= 0)
+ hdata->cur_conf = conf_idx;
+ else
+ DRM_DEBUG_KMS("not supported mode\n");
+ } else {
+ hdmi_v14_mode_set(hdata, m);
+ }
+
+ return 0;
+}
+
+int hdmi_update(struct display_entity *entity)
+{
+ struct hdmi_context *hdata + container_of(entity, struct hdmi_context, entity);
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ hdmi_conf_apply(hdata);
+ return 0;
+}
+
+int hdmi_set_state(struct display_entity *entity,
+ enum display_entity_state state)
+{
+ struct hdmi_context *hdata + container_of(entity, struct hdmi_context, entity);
+
+ DRM_DEBUG_KMS("[%d] %s %d\n", __LINE__, __func__, state);
+
+ switch (state) {
+ case DISPLAY_ENTITY_STATE_OFF:
+ case DISPLAY_ENTITY_STATE_STANDBY:
if (!pm_runtime_suspended(hdata->dev))
pm_runtime_put_sync(hdata->dev);
break;
- default:
- DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
+
+ case DISPLAY_ENTITY_STATE_ON:
+ if (pm_runtime_suspended(hdata->dev))
+ pm_runtime_get_sync(hdata->dev);
break;
+ default:
+ return -EINVAL;
}
+ return 0;
+}
+
+int hdmi_init_audio(struct display_entity *entity,
+ const struct display_entity_audio_params *params)
+{
+ struct hdmi_context *hdata + container_of(entity, struct hdmi_context, entity);
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ if (params->type = DISPLAY_ENTITY_AUDIO_I2S)
+ hdmi_i2s_audio_init(hdata, params);
+ else if (params->type = DISPLAY_ENTITY_AUDIO_SPDIF)
+ hdmi_spdif_audio_init(hdata, params);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+
+int hdmi_set_audiostate(struct display_entity *entity,
+ enum display_entity_audiostate state)
+{
+ struct hdmi_context *hdata + container_of(entity, struct hdmi_context, entity);
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ if (state = DISPLAY_ENTITY_AUDIOSTATE_ON)
+ hdmi_audio_control(hdata, true);
+ else
+ hdmi_audio_control(hdata, false);
+
+ return 0;
}
-static struct exynos_hdmi_ops hdmi_ops = {
- /* display */
- .is_connected = hdmi_is_connected,
- .get_edid = hdmi_get_edid,
- .check_timing = hdmi_check_timing,
-
- /* manager */
- .mode_fixup = hdmi_mode_fixup,
- .mode_set = hdmi_mode_set,
- .get_max_resol = hdmi_get_max_resol,
- .commit = hdmi_commit,
- .dpms = hdmi_dpms,
+struct display_entity_control_ops entity_ctrl_ops = {
+ .get_size = hdmi_get_size,
+ .update = hdmi_update,
+ .set_state = hdmi_set_state,
+ .set_mode = hdmi_set_mode,
+};
+
+struct display_entity_hdmi_control_ops hdmi_ctrl_ops = {
+ .get_edid = hdmi_get_edid,
+ .check_mode = hdmi_check_mode,
+ .init_audio = hdmi_init_audio,
+ .set_audiostate = hdmi_set_audiostate,
+};
+
+struct exynos_hdmi_control_ops exynos_hdmi_ctrl_ops = {
+ .get_hpdstate = hdmi_get_hpdstate,
};
static irqreturn_t hdmi_external_irq_thread(int irq, void *arg)
{
- struct exynos_drm_hdmi_context *ctx = arg;
- struct hdmi_context *hdata = ctx->ctx;
+ struct hdmi_context *hdata = arg;
mutex_lock(&hdata->hdmi_mutex);
hdata->hpd = gpio_get_value(hdata->hpd_gpio);
mutex_unlock(&hdata->hdmi_mutex);
- if (ctx->drm_dev)
- drm_helper_hpd_irq_event(ctx->drm_dev);
+ hdmi_send_hpdevent(&hdata->entity, hdata->hpd);
return IRQ_HANDLED;
}
static irqreturn_t hdmi_internal_irq_thread(int irq, void *arg)
{
- struct exynos_drm_hdmi_context *ctx = arg;
- struct hdmi_context *hdata = ctx->ctx;
+ struct hdmi_context *hdata = arg;
u32 intc_flag;
intc_flag = hdmi_reg_read(hdata, HDMI_INTC_FLAG);
@@ -2017,16 +2038,17 @@ static irqreturn_t hdmi_internal_irq_thread(int irq, void *arg)
DRM_DEBUG_KMS("unplugged\n");
hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0,
HDMI_INTC_FLAG_HPD_UNPLUG);
+ hdata->hpd = 0;
+ hdmi_send_hpdevent(&hdata->entity, hdata->hpd);
}
if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) {
DRM_DEBUG_KMS("plugged\n");
hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0,
HDMI_INTC_FLAG_HPD_PLUG);
+ hdata->hpd = 1;
+ hdmi_send_hpdevent(&hdata->entity, hdata->hpd);
}
- if (ctx->drm_dev)
- drm_helper_hpd_irq_event(ctx->drm_dev);
-
return IRQ_HANDLED;
}
@@ -2176,16 +2198,20 @@ static struct of_device_id hdmi_match_types[] = {
};
#endif
+static void hdmi_release(struct display_entity *entity)
+{
+ DRM_DEBUG_KMS("[%d][%s]\n", __LINE__, __func__);
+}
+
static int hdmi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct exynos_drm_hdmi_context *drm_hdmi_ctx;
struct hdmi_context *hdata;
struct s5p_hdmi_platform_data *pdata;
struct resource *res;
int ret;
- DRM_DEBUG_KMS("[%d]\n", __LINE__);
+ DRM_DEBUG_KMS("[%d][%s]\n", __LINE__, __func__);
if (pdev->dev.of_node) {
pdata = drm_hdmi_dt_parse_pdata(dev);
@@ -2202,13 +2228,6 @@ static int hdmi_probe(struct platform_device *pdev)
return -EINVAL;
}
- drm_hdmi_ctx = devm_kzalloc(&pdev->dev, sizeof(*drm_hdmi_ctx),
- GFP_KERNEL);
- if (!drm_hdmi_ctx) {
- DRM_ERROR("failed to allocate common hdmi context.\n");
- return -ENOMEM;
- }
-
hdata = devm_kzalloc(&pdev->dev, sizeof(struct hdmi_context),
GFP_KERNEL);
if (!hdata) {
@@ -2218,10 +2237,7 @@ static int hdmi_probe(struct platform_device *pdev)
mutex_init(&hdata->hdmi_mutex);
- drm_hdmi_ctx->ctx = (void *)hdata;
- hdata->parent_ctx = (void *)drm_hdmi_ctx;
-
- platform_set_drvdata(pdev, drm_hdmi_ctx);
+ platform_set_drvdata(pdev, hdata);
if (dev->of_node) {
const struct of_device_id *match;
@@ -2299,7 +2315,7 @@ static int hdmi_probe(struct platform_device *pdev)
ret = request_threaded_irq(hdata->external_irq, NULL,
hdmi_external_irq_thread, IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- "hdmi_external", drm_hdmi_ctx);
+ "hdmi_external", hdata);
if (ret) {
DRM_ERROR("failed to register hdmi external interrupt\n");
goto err_hdmiphy;
@@ -2307,24 +2323,31 @@ static int hdmi_probe(struct platform_device *pdev)
ret = request_threaded_irq(hdata->internal_irq, NULL,
hdmi_internal_irq_thread, IRQF_ONESHOT,
- "hdmi_internal", drm_hdmi_ctx);
+ "hdmi_internal", hdata);
if (ret) {
DRM_ERROR("failed to register hdmi internal interrupt\n");
goto err_free_irq;
}
- /* Attach HDMI Driver to common hdmi. */
- exynos_hdmi_drv_attach(drm_hdmi_ctx);
+ pm_runtime_enable(dev);
- /* register specific callbacks to common hdmi. */
- exynos_hdmi_ops_register(&hdmi_ops);
+ hdata->entity.dev = &pdev->dev;
+ hdata->entity.release = hdmi_release;
+ hdata->entity.ops.ctrl = &entity_ctrl_ops;
+ hdata->entity.opt_ctrl.hdmi = &hdmi_ctrl_ops;
- pm_runtime_enable(dev);
+ hdata->entity.private = &exynos_hdmi_ctrl_ops;
+
+ ret = display_entity_register(&hdata->entity);
+ if (ret < 0) {
+ DRM_ERROR("[%d][%s]\n", __LINE__, __func__);
+ return ret;
+ }
return 0;
err_free_irq:
- free_irq(hdata->external_irq, drm_hdmi_ctx);
+ free_irq(hdata->external_irq, hdata);
err_hdmiphy:
i2c_del_driver(&hdmiphy_driver);
err_ddc:
@@ -2335,8 +2358,7 @@ err_ddc:
static int hdmi_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct exynos_drm_hdmi_context *ctx = platform_get_drvdata(pdev);
- struct hdmi_context *hdata = ctx->ctx;
+ struct hdmi_context *hdata = platform_get_drvdata(pdev);
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
@@ -2357,8 +2379,7 @@ static int hdmi_remove(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
static int hdmi_suspend(struct device *dev)
{
- struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
- struct hdmi_context *hdata = ctx->ctx;
+ struct hdmi_context *hdata = get_hdmi_context(dev);
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
@@ -2366,8 +2387,7 @@ static int hdmi_suspend(struct device *dev)
disable_irq(hdata->external_irq);
hdata->hpd = false;
- if (ctx->drm_dev)
- drm_helper_hpd_irq_event(ctx->drm_dev);
+ hdmi_send_hpdevent(&hdata->entity, hdata->hpd);
if (pm_runtime_suspended(dev)) {
DRM_DEBUG_KMS("%s : Already suspended\n", __func__);
@@ -2381,8 +2401,7 @@ static int hdmi_suspend(struct device *dev)
static int hdmi_resume(struct device *dev)
{
- struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
- struct hdmi_context *hdata = ctx->ctx;
+ struct hdmi_context *hdata = get_hdmi_context(dev);
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
@@ -2405,8 +2424,7 @@ static int hdmi_resume(struct device *dev)
#ifdef CONFIG_PM_RUNTIME
static int hdmi_runtime_suspend(struct device *dev)
{
- struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
- struct hdmi_context *hdata = ctx->ctx;
+ struct hdmi_context *hdata = get_hdmi_context(dev);
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
hdmi_poweroff(hdata);
@@ -2416,8 +2434,7 @@ static int hdmi_runtime_suspend(struct device *dev)
static int hdmi_runtime_resume(struct device *dev)
{
- struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
- struct hdmi_context *hdata = ctx->ctx;
+ struct hdmi_context *hdata = get_hdmi_context(dev);
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
hdmi_poweron(hdata);
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi_cdf.c b/drivers/gpu/drm/exynos/exynos_hdmi_cdf.c
new file mode 100644
index 0000000..e1c862f
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_hdmi_cdf.c
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * Authors:
+ * Seung-Woo Kim <sw0312.kim@samsung.com>
+ * Inki Dae <inki.dae@samsung.com>
+ * Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * Based on drivers/media/video/s5p-tv/hdmi_drv.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "regs-hdmi.h"
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <video/display.h>
+#include "video/exynos_hdmi.h"
+
+#include <drm/exynos_drm.h>
+#include "exynos_drm_drv.h"
+#include "exynos_drm_hdmi.h"
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include "exynos_hdmi.h"
+
+#define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev))
+
+struct hdmi_cdf_context {
+ struct device *dev;
+ struct drm_device *drm_dev;
+ unsigned int hpd;
+ struct display_entity *entity;
+ struct display_entity_notifier notf;
+ struct display_event_subscriber subscriber;
+ void *parent_ctx;
+};
+
+extern bool hdmi_cdf_is_connected(void *ctx)
+{
+ struct hdmi_cdf_context *hdata = (struct hdmi_cdf_context *)ctx;
+
+ DRM_DEBUG_KMS("[%d] %s hpd %d\n", __LINE__, __func__, hdata->hpd);
+ return (bool)hdata->hpd;
+}
+
+extern int hdmi_cdf_get_edid(void *ctx, struct drm_connector *connector,
+ u8 *edid, int len)
+{
+ struct hdmi_cdf_context *hdata = (struct hdmi_cdf_context *)ctx;
+ struct display_entity_edid edid_st;
+ struct edid *raw_edid;
+ int ret;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ ret = display_entity_hdmi_get_edid(hdata->entity, &edid_st);
+ if (ret) {
+ DRM_ERROR("[%d]%s, Edid Read Fail!!! ret = %d\n",
+ __LINE__, __func__, ret);
+ return -EINVAL;
+ }
+
+ raw_edid = (struct edid *)edid_st.edid;
+
+ if (raw_edid) {
+ memcpy(edid, raw_edid, min(edid_st.len, len));
+ kfree(raw_edid);
+ } else {
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+extern int hdmi_cdf_check_timing(void *ctx, struct fb_videomode *timing)
+{
+ struct hdmi_cdf_context *hdata = (struct hdmi_cdf_context *)ctx;
+ int ret;
+
+ ret = display_entity_hdmi_check_mode(hdata->entity,
+ (struct videomode *)timing);
+ if (ret) {
+ DRM_DEBUG_KMS("[%d]%s, Mode NOT Supported! %dx%d@%d%s\n",
+ __LINE__, __func__, timing->xres, timing->yres,
+ timing->refresh, timing->flag
+ & FB_VMODE_INTERLACED ? "I" : "P");
+ return -EINVAL;
+ }
+
+ DRM_DEBUG_KMS("[%d]%s, Mode Supported! %dx%d@%d%s\n", __LINE__,
+ __func__, timing->xres, timing->yres, timing->refresh,
+ timing->flag & FB_VMODE_INTERLACED ? "I" : "P");
+
+ return 0;
+}
+
+extern int hdmi_cdf_power_on(void *ctx, int mode)
+{
+ return 0;
+}
+
+extern void hdmi_cdf_mode_fixup(void *ctx, struct drm_connector *connector,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct hdmi_cdf_context *hdata = (struct hdmi_cdf_context *)ctx;
+ struct drm_display_mode *m;
+ struct fb_videomode timing;
+ int index;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ drm_mode_set_crtcinfo(adjusted_mode, 0);
+
+ timing.xres = adjusted_mode->hdisplay;
+ timing.yres = adjusted_mode->vdisplay;
+ timing.refresh = adjusted_mode->vrefresh;
+ timing.pixclock = adjusted_mode->clock * 1000;
+ timing.flag = ((adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) ?
+ FB_VMODE_INTERLACED : 0);
+
+ index = display_entity_hdmi_check_mode(hdata->entity,
+ (struct videomode *)&timing);
+
+ /* just return if user desired mode exists. */
+ if (index = 0)
+ return;
+
+ /*
+ * otherwise, find the most suitable mode among modes and change it
+ * to adjusted_mode.
+ */
+ list_for_each_entry(m, &connector->modes, head) {
+
+ timing.xres = m->hdisplay;
+ timing.yres = m->vdisplay;
+ timing.refresh = m->vrefresh;
+ timing.pixclock = m->clock * 1000;
+ timing.flag = ((m->flags & DRM_MODE_FLAG_INTERLACE) ?
+ FB_VMODE_INTERLACED : 0);
+
+ index = display_entity_hdmi_check_mode(hdata->entity,
+ (struct videomode *)&timing);
+
+ if (index = 0) {
+ struct drm_mode_object base;
+ struct list_head head;
+
+ DRM_INFO("desired mode doesn't exist so\n");
+ DRM_INFO("use most suitable mode.\n");
+
+ DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d][%d]Hz\n",
+ m->hdisplay, m->vdisplay, m->vrefresh);
+
+ /* preserve display mode header while copying. */
+ head = adjusted_mode->head;
+ base = adjusted_mode->base;
+ memcpy(adjusted_mode, m, sizeof(*m));
+ adjusted_mode->head = head;
+ adjusted_mode->base = base;
+ break;
+ }
+ }
+}
+
+extern void hdmi_cdf_mode_set(void *ctx, void *mode)
+{
+ struct hdmi_cdf_context *hdata = (struct hdmi_cdf_context *)ctx;
+ struct drm_display_mode *m = mode;
+ int ret;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ ret = display_entity_set_mode(hdata->entity, (struct videomode *)m);
+ if (ret) {
+ DRM_DEBUG_KMS("[%d]%s, Mode NOT Set! %dx%d@%d%s\n",
+ __LINE__, __func__, m->hdisplay, m->vdisplay,
+ m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE)
+ ? "I" : "P");
+ }
+}
+
+extern void hdmi_cdf_get_max_resol(void *ctx, unsigned int *width,
+ unsigned int *height)
+{
+ struct hdmi_cdf_context *hdata = (struct hdmi_cdf_context *)ctx;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ display_entity_get_size(hdata->entity, width, height);
+}
+
+extern void hdmi_cdf_commit(void *ctx)
+{
+ struct hdmi_cdf_context *hdata = (struct hdmi_cdf_context *)ctx;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ display_entity_update(hdata->entity);
+}
+
+extern void hdmi_cdf_dpms(void *ctx, int mode)
+{
+ struct hdmi_cdf_context *hdata = (struct hdmi_cdf_context *)ctx;
+ enum display_entity_state state;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ state = DISPLAY_ENTITY_STATE_ON;
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ state = DISPLAY_ENTITY_STATE_STANDBY;
+ break;
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ state = DISPLAY_ENTITY_STATE_STANDBY;
+ break;
+ default:
+ DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
+ return;
+ }
+
+ display_entity_set_state(hdata->entity, state);
+}
+
+void event_notify(struct display_entity *entity,
+ enum display_entity_event_type type, unsigned int value,
+ void *context)
+{
+ struct hdmi_cdf_context *hdata = (struct hdmi_cdf_context *)context;
+ struct exynos_drm_hdmi_context *ctx = get_hdmi_context(hdata->dev);
+
+ if (type = DISPLAY_ENTITY_HDMI_HOTPLUG) {
+ DRM_DEBUG_KMS("[%d][%s] hpd(%d)\n",
+ __LINE__, __func__, value);
+ hdata->hpd = value;
+
+ if (ctx->drm_dev)
+ drm_helper_hpd_irq_event(ctx->drm_dev);
+ }
+}
+
+int display_entity_notification(struct display_entity_notifier *notf,
+ struct display_entity *entity, int status)
+{
+ struct hdmi_cdf_context *hdata = container_of(notf,
+ struct hdmi_cdf_context, notf);
+ struct exynos_hdmi_control_ops *exynos_ops + (struct exynos_hdmi_control_ops *)entity->private;
+
+ if (status != DISPLAY_ENTITY_NOTIFIER_CONNECT && entity)
+ return -EINVAL;
+
+ DRM_DEBUG_KMS("[%d][%s] NOTIFIER_CONNECT\n", __LINE__, __func__);
+
+ hdata->entity = entity;
+
+ hdata->subscriber.context = hdata;
+ hdata->subscriber.notify = event_notify;
+ display_entity_subscribe_event(entity, &hdata->subscriber);
+
+ exynos_ops->get_hpdstate(entity, &hdata->hpd);
+ return 0;
+}
+
+static struct exynos_hdmi_ops hdmi_ops = {
+ /* display */
+ .is_connected = hdmi_cdf_is_connected,
+ .get_edid = hdmi_cdf_get_edid,
+ .check_timing = hdmi_cdf_check_timing,
+
+ /* manager */
+ .mode_fixup = hdmi_cdf_mode_fixup,
+ .mode_set = hdmi_cdf_mode_set,
+ .get_max_resol = hdmi_cdf_get_max_resol,
+ .commit = hdmi_cdf_commit,
+ .dpms = hdmi_cdf_dpms,
+};
+
+static int hdmi_cdf_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *dev_node;
+ struct platform_device *disp_pdev;
+ struct exynos_drm_hdmi_context *drm_hdmi_ctx;
+ struct hdmi_cdf_context *hdata;
+ int ret;
+
+ DRM_DEBUG_KMS("[%d][%s]\n", __LINE__, __func__);
+
+ dev_node = of_find_compatible_node(NULL, NULL,
+ "samsung,exynos5-hdmi");
+ if (!dev_node) {
+ DRM_DEBUG_KMS("[ERROR][%d][%s] dt node not found.\n",
+ __LINE__, __func__);
+ return -EINVAL;
+ }
+
+ disp_pdev = of_find_device_by_node(dev_node);
+ if (!disp_pdev) {
+ DRM_DEBUG_KMS("[ERROR][%d][%s] No pdev\n",
+ __LINE__, __func__);
+ return -EINVAL;
+ }
+
+ drm_hdmi_ctx = devm_kzalloc(&pdev->dev, sizeof(*drm_hdmi_ctx),
+ GFP_KERNEL);
+ if (!drm_hdmi_ctx) {
+ DRM_ERROR("failed to allocate common hdmi context.\n");
+ return -ENOMEM;
+ }
+
+ hdata = devm_kzalloc(&pdev->dev, sizeof(struct hdmi_cdf_context),
+ GFP_KERNEL);
+ if (!hdata) {
+ DRM_ERROR("out of memory\n");
+ return -ENOMEM;
+ }
+
+ drm_hdmi_ctx->ctx = (void *)hdata;
+ hdata->parent_ctx = (void *)drm_hdmi_ctx;
+
+ platform_set_drvdata(pdev, drm_hdmi_ctx);
+
+ /* Attach HDMI Driver to common hdmi. */
+ exynos_hdmi_drv_attach(drm_hdmi_ctx);
+
+ /* register specific callbacks to common hdmi. */
+ exynos_hdmi_ops_register(&hdmi_ops);
+
+ hdata->dev = dev;
+ hdata->notf.dev = &disp_pdev->dev;
+ hdata->notf.notify = display_entity_notification;
+
+ ret = display_entity_register_notifier(&hdata->notf);
+ if (ret) {
+ DRM_DEBUG_KMS("[ERROR][%d][%s] entity registe failed.\n",
+ __LINE__, __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hdmi_cdf_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+struct platform_driver hdmi_cdf_driver = {
+ .probe = hdmi_cdf_probe,
+ .remove = hdmi_cdf_remove,
+ .driver = {
+ .name = "exynos-hdmi-cdf",
+ .owner = THIS_MODULE,
+ },
+};
+
diff --git a/include/video/exynos_hdmi.h b/include/video/exynos_hdmi.h
new file mode 100644
index 0000000..cc8d613
--- /dev/null
+++ b/include/video/exynos_hdmi.h
@@ -0,0 +1,25 @@
+/*
+ * Display Core
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __EXYNOS_HDMI_H__
+#define __EXYNOS_HDMI_H__
+
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
+struct exynos_hdmi_control_ops {
+ int (*get_hpdstate)(struct display_entity *entity,
+ unsigned int *hpd_state);
+};
+
+#endif /* __EXYNOS_HDMI_H__ */
--
1.8.0
^ permalink raw reply related
* [RFC PATCH v2 4/5] alsa/soc: add hdmi audio codec based on cdf
From: Rahul Sharma @ 2013-02-07 11:53 UTC (permalink / raw)
To: linux-media, dri-devel, alsa-devel, linux-fbdev
Cc: tomi.valkeinen, laurent.pinchart, broonie, inki.dae,
kyungmin.park, r.sh.open, joshi
In-Reply-To: <1360239132-15557-1-git-send-email-rahul.sharma@samsung.com>
V2:
- DAPM and JACK control to hdmi codec.
This patch registers hdmi-audio codec to the ALSA framework. This is the second
client to the hdmi panel. Once notified by the CDF Core it proceeds towards
audio setting and audio control. It also subscribes for hpd notification to
implement hpd related audio requirements.
Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
---
sound/soc/codecs/Kconfig | 3 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/exynos_hdmi_audio.c | 424 +++++++++++++++++++++++++++++++++++
3 files changed, 429 insertions(+)
create mode 100644 sound/soc/codecs/exynos_hdmi_audio.c
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 3a84782..d3e0874 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -512,3 +512,6 @@ config SND_SOC_ML26124
config SND_SOC_TPA6130A2
tristate
+
+config SND_SOC_EXYNOS_HDMI_CODEC
+ tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index f6e8e36..388da28 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -115,6 +115,7 @@ snd-soc-wm9705-objs := wm9705.o
snd-soc-wm9712-objs := wm9712.o
snd-soc-wm9713-objs := wm9713.o
snd-soc-wm-hubs-objs := wm_hubs.o
+snd-soc-exynos-hdmi-audio-objs := exynos_hdmi_audio.o
# Amp
snd-soc-max9877-objs := max9877.o
@@ -236,6 +237,7 @@ obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
+obj-$(CONFIG_SND_SOC_EXYNOS_HDMI_CODEC) += snd-soc-exynos-hdmi-audio.o
# Amp
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
diff --git a/sound/soc/codecs/exynos_hdmi_audio.c b/sound/soc/codecs/exynos_hdmi_audio.c
new file mode 100644
index 0000000..e2cf94c
--- /dev/null
+++ b/sound/soc/codecs/exynos_hdmi_audio.c
@@ -0,0 +1,424 @@
+/*
+ * ALSA SoC codec driver for HDMI audio on Samsung Exynos processors.
+ * Copyright (C) 2013 Samsung corp.
+ * Author: Rahul Sharma <rahul.sharma@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include <video/display.h>
+#include <video/exynos_hdmi.h>
+
+#define get_ctx(dev) ((struct hdmi_audio_context *)platform_get_drvdata\
+ (to_platform_device((struct device *)dev)))
+
+/* platform device pointer for eynos hdmi audio codec device. */
+static struct platform_device *exynos_hdmi_codec_pdev;
+
+struct hdmi_audio_context {
+ struct platform_device *pdev;
+ atomic_t plugged;
+ atomic_t enabled;
+ struct workqueue_struct *event_wq;
+ struct delayed_work hotplug_work;
+ struct display_entity_audio_params audio_params;
+ struct display_entity *entity;
+ struct display_entity_notifier notf;
+ struct display_event_subscriber subscriber;
+ struct snd_soc_jack hdmi_jack;
+};
+
+static int exynos_hdmi_audio_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct hdmi_audio_context *ctx = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ dev_dbg(codec->dev, "[%d] %s\n", __LINE__, __func__);
+
+ /* report failure if hdmi sink is unplugged. */
+ if (!atomic_read(&ctx->plugged))
+ return -ENODEV;
+
+ ctx->audio_params.type = DISPLAY_ENTITY_AUDIO_I2S;
+
+ switch (params_channels(params)) {
+ case 6:
+ case 4:
+ case 2:
+ case 1:
+ ctx->audio_params.channels = params_channels(params);
+ break;
+ default:
+ dev_err(codec->dev, "%d channels not supported\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ ctx->audio_params.bits_per_sample = 8;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ ctx->audio_params.bits_per_sample = 12;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ctx->audio_params.bits_per_sample = 16;
+ break;
+ default:
+ dev_err(codec->dev, "Format(%d) not supported\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ switch (params_rate(params)) {
+ case 32000:
+ case 44100:
+ case 88200:
+ case 176400:
+ case 48000:
+ case 96000:
+ case 192000:
+ ctx->audio_params.sf = params_rate(params);
+ break;
+ default:
+ dev_err(codec->dev, "%d Rate supported\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ ret + display_entity_hdmi_init_audio(ctx->entity, &ctx->audio_params);
+ if (ret)
+ dev_err(codec->dev, "initaudio failed ret %d\n", ret);
+ return ret;
+}
+
+static int exynos_hdmi_audio_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct hdmi_audio_context *ctx = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ dev_dbg(codec->dev, "[%d] %s\n", __LINE__, __func__);
+
+ /* report failure if hdmi sink is unplugged. */
+ if (!atomic_read(&ctx->plugged))
+ return -EINVAL;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ /* Don't start if hdmi audio is disabled. return Success. */
+ if (!atomic_read(&ctx->enabled))
+ return 0;
+
+ ret = display_entity_hdmi_set_audiostate(ctx->entity,
+ DISPLAY_ENTITY_AUDIOSTATE_ON);
+ if (ret) {
+ dev_err(codec->dev, "audio enable failed.\n");
+ return -EINVAL;
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ ret = display_entity_hdmi_set_audiostate(ctx->entity,
+ DISPLAY_ENTITY_AUDIOSTATE_OFF);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops exynos_hdmi_audio_dai_ops = {
+ .hw_params = exynos_hdmi_audio_hw_params,
+ .trigger = exynos_hdmi_audio_trigger,
+};
+
+static struct snd_soc_dai_driver hdmi_codec_dai = {
+ .name = "exynos-hdmi-audio-dai",
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &exynos_hdmi_audio_dai_ops,
+};
+
+void hdmi_audio_event_notify(struct display_entity *entity,
+ enum display_entity_event_type type,
+ unsigned int value, void *context)
+{
+ struct hdmi_audio_context *ctx = (struct hdmi_audio_context *)context;
+
+ if (type = DISPLAY_ENTITY_HDMI_HOTPLUG) {
+ dev_dbg(&ctx->pdev->dev, "[%d][%s] hpd(%d)\n", __LINE__,
+ __func__, value);
+ atomic_set(&ctx->plugged, !!value);
+
+ /* should set audio regs after ip, phy got stable. 5ms suff */
+ queue_delayed_work(ctx->event_wq, &ctx->hotplug_work,
+ msecs_to_jiffies(5));
+ }
+}
+
+static void hotplug_event_handler(struct work_struct *work)
+{
+ struct hdmi_audio_context *ctx = container_of(work,
+ struct hdmi_audio_context, hotplug_work.work);
+
+ if (atomic_read(&ctx->plugged)) {
+ display_entity_hdmi_init_audio(ctx->entity,
+ &ctx->audio_params);
+
+ if (atomic_read(&ctx->enabled))
+ display_entity_hdmi_set_audiostate(ctx->entity,
+ DISPLAY_ENTITY_AUDIOSTATE_ON);
+ else
+ display_entity_hdmi_set_audiostate(ctx->entity,
+ DISPLAY_ENTITY_AUDIOSTATE_OFF);
+ }
+
+ snd_soc_jack_report(&ctx->hdmi_jack,
+ atomic_read(&ctx->plugged) ? SND_JACK_AVOUT
+ : 0, SND_JACK_AVOUT);
+}
+
+int exynos_hdmi_audio_notification(struct display_entity_notifier *notf,
+ struct display_entity *entity, int status)
+{
+ struct hdmi_audio_context *ctx = container_of(notf,
+ struct hdmi_audio_context, notf);
+ struct exynos_hdmi_control_ops *exynos_ops + (struct exynos_hdmi_control_ops *)entity->private;
+ int hpd;
+
+ if (status != DISPLAY_ENTITY_NOTIFIER_CONNECT && entity)
+ return -EINVAL;
+
+ dev_dbg(&ctx->pdev->dev, "[%d][%s]\n", __LINE__, __func__);
+
+ ctx->entity = entity;
+
+ ctx->subscriber.context = ctx;
+ ctx->subscriber.notify = hdmi_audio_event_notify;
+
+ display_entity_subscribe_event(entity, &ctx->subscriber);
+
+ exynos_ops->get_hpdstate(entity, &hpd);
+ atomic_set(&ctx->plugged, !!hpd);
+
+ return 0;
+}
+
+static int get_hdmi(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdmi_audio_context *ctx = get_ctx(kcontrol->private_value);
+
+ if (!ctx) {
+ dev_err(&ctx->pdev->dev, "invalid context.\n");
+ return 0;
+ }
+
+ ucontrol->value.integer.value[0] = atomic_read(&ctx->enabled);
+ return 1;
+}
+
+static int put_hdmi(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdmi_audio_context *ctx = get_ctx(kcontrol->private_value);
+ int enable = (int)ucontrol->value.integer.value[0];
+
+ if (!ctx) {
+ dev_err(&ctx->pdev->dev, "invalid context.\n");
+ return 0;
+ }
+
+ atomic_set(&ctx->enabled, !!enable);
+
+ if (!atomic_read(&ctx->plugged))
+ return 1;
+ else if (enable)
+ display_entity_hdmi_set_audiostate(ctx->entity,
+ DISPLAY_ENTITY_AUDIOSTATE_ON);
+ else
+ display_entity_hdmi_set_audiostate(ctx->entity,
+ DISPLAY_ENTITY_AUDIOSTATE_OFF);
+
+ return 1;
+}
+
+static struct snd_kcontrol_new hdmi_dapm_controls[] = {
+ SOC_SINGLE_BOOL_EXT("HDMI Playback Switch", 0, get_hdmi, put_hdmi),
+};
+
+static int hdmi_codec_init(struct snd_soc_codec *codec)
+{
+ struct hdmi_audio_context *ctx = get_ctx(codec->dev);
+ int ret;
+
+ ret = snd_soc_jack_new(codec, "HDMI Jack",
+ SND_JACK_AVOUT, &ctx->hdmi_jack);
+ if (ret) {
+ dev_err(codec->dev, "audio enable failed.\n");
+ return ret;
+ }
+
+ hdmi_dapm_controls[0].private_value = (unsigned long)codec->dev;
+ return 0;
+}
+
+static struct snd_soc_codec_driver hdmi_codec = {
+ .probe = hdmi_codec_init,
+ .controls = hdmi_dapm_controls,
+ .num_controls = ARRAY_SIZE(hdmi_dapm_controls),
+};
+
+static int hdmi_codec_probe(struct platform_device *pdev)
+{
+ struct hdmi_audio_context *ctx;
+ struct device_node *dev_node;
+ struct platform_device *disp_pdev;
+ int ret;
+
+ dev_dbg(&pdev->dev, "[%d][%s]\n", __LINE__, __func__);
+
+ ret = snd_soc_register_codec(&pdev->dev, &hdmi_codec,
+ &hdmi_codec_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "register_codec failed (%d)\n", ret);
+ return ret;
+ }
+
+ ctx = devm_kzalloc(&pdev->dev, sizeof(struct hdmi_audio_context),
+ GFP_KERNEL);
+ if (ctx = NULL)
+ return -ENOMEM;
+
+ ctx->pdev = pdev;
+ atomic_set(&ctx->enabled, 1);
+ platform_set_drvdata(pdev, ctx);
+
+ /* create workqueue and hotplug work */
+ ctx->event_wq = alloc_workqueue("hdmi-audio-event",
+ WQ_UNBOUND | WQ_NON_REENTRANT, 1);
+ if (ctx->event_wq = NULL) {
+ dev_err(&pdev->dev, "failed to create workqueue\n");
+ ret = -ENOMEM;
+ }
+ INIT_DELAYED_WORK(&ctx->hotplug_work, hotplug_event_handler);
+
+ dev_node = of_find_compatible_node(NULL, NULL,
+ "samsung,exynos5-hdmi");
+ if (!dev_node) {
+ dev_err(&pdev->dev, "[%d][%s] dt node not found.\n",
+ __LINE__, __func__);
+ return -EINVAL;
+ }
+
+ disp_pdev = of_find_device_by_node(dev_node);
+ if (!disp_pdev) {
+ dev_err(&pdev->dev, "[ERROR][%d][%s] No pdev\n",
+ __LINE__, __func__);
+ return -EINVAL;
+ }
+
+ ctx->notf.dev = &disp_pdev->dev;
+ ctx->notf.notify = exynos_hdmi_audio_notification;
+
+ ret = display_entity_register_notifier(&ctx->notf);
+ if (ret) {
+ dev_err(&pdev->dev, "[%d][%s] entity registe failed.\n",
+ __LINE__, __func__);
+ ret = -EINVAL;
+ goto err_workq;
+ }
+
+ return 0;
+
+err_workq:
+ destroy_workqueue(ctx->event_wq);
+ return ret;
+}
+
+static int hdmi_codec_remove(struct platform_device *pdev)
+{
+ struct hdmi_audio_context *ctx = get_ctx(&pdev->dev);
+ dev_dbg(&pdev->dev, "[%d][%s]\n", __LINE__, __func__);
+ mdelay(1000);
+
+ display_entity_register_notifier(&ctx->notf);
+ snd_soc_unregister_codec(&pdev->dev);
+ destroy_workqueue(ctx->event_wq);
+ return 0;
+}
+
+static struct platform_driver hdmi_codec_driver = {
+ .driver = {
+ .name = "exynos-hdmi-audio-codec",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = hdmi_codec_probe,
+ .remove = hdmi_codec_remove,
+};
+
+static int __init hdmi_audio_codec_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&hdmi_codec_driver);
+ if (ret < 0)
+ return -EINVAL;
+
+ exynos_hdmi_codec_pdev = platform_device_register_simple
+ ("exynos-hdmi-audio-codec", -1, NULL, 0);
+ if (IS_ERR_OR_NULL(exynos_hdmi_codec_pdev)) {
+ ret = PTR_ERR(exynos_hdmi_codec_pdev);
+ platform_driver_unregister(&hdmi_codec_driver);
+ return ret;
+ }
+
+ return 0;
+}
+static void __exit hdmi_audio_codec_exit(void)
+{
+ platform_driver_unregister(&hdmi_codec_driver);
+ platform_device_unregister(exynos_hdmi_codec_pdev);
+}
+
+module_init(hdmi_audio_codec_init);
+module_exit(hdmi_audio_codec_exit);
+
+MODULE_AUTHOR("Rahul Sharma <rahul.sharma@samsung.com>");
+MODULE_DESCRIPTION("ASoC EXYNOS HDMI codec driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:exynos-hdmi-codec");
--
1.8.0
^ permalink raw reply related
* [RFC PATCH v2 5/5] alsa/soc: add hdmi audio card using cdf based hdmi codec
From: Rahul Sharma @ 2013-02-07 11:54 UTC (permalink / raw)
To: linux-media, dri-devel, alsa-devel, linux-fbdev
Cc: tomi.valkeinen, laurent.pinchart, broonie, inki.dae,
kyungmin.park, r.sh.open, joshi
In-Reply-To: <1360239132-15557-1-git-send-email-rahul.sharma@samsung.com>
It registers hdmi-audio card to ALSA framework which associates i2s dai and
cdf based hdmi audio codec.
Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
---
sound/soc/samsung/Kconfig | 8 ++
sound/soc/samsung/Makefile | 2 +
sound/soc/samsung/hdmi.c | 260 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 270 insertions(+)
create mode 100644 sound/soc/samsung/hdmi.c
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index 90e7e66..d5b92ab 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -185,6 +185,14 @@ config SND_SOC_SMDK_WM8994_PCM
help
Say Y if you want to add support for SoC audio on the SMDK
+config SND_SOC_EXYNOS_HDMI_AUDIO
+ tristate "SoC I2S Audio support for HDMI"
+ depends on SND_SOC_SAMSUNG && DRM_EXYNOS_HDMI_CDF
+ select SND_SAMSUNG_I2S
+ select SND_SOC_EXYNOS_HDMI_CODEC
+ help
+ Say Y if you want to add support for hdmi audio on the Exynos.
+
config SND_SOC_SPEYSIDE
tristate "Audio support for Wolfson Speyside"
depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410
diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile
index 709f605..ab1c151 100644
--- a/sound/soc/samsung/Makefile
+++ b/sound/soc/samsung/Makefile
@@ -8,6 +8,7 @@ snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
snd-soc-samsung-spdif-objs := spdif.o
snd-soc-pcm-objs := pcm.o
snd-soc-i2s-objs := i2s.o
+snd-soc-hdmi-objs := hdmi.o
obj-$(CONFIG_SND_SOC_SAMSUNG) += snd-soc-s3c24xx.o
obj-$(CONFIG_SND_S3C24XX_I2S) += snd-soc-s3c24xx-i2s.o
@@ -18,6 +19,7 @@ obj-$(CONFIG_SND_SAMSUNG_SPDIF) += snd-soc-samsung-spdif.o
obj-$(CONFIG_SND_SAMSUNG_PCM) += snd-soc-pcm.o
obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-i2s.o
obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-idma.o
+obj-$(CONFIG_SND_SOC_EXYNOS_HDMI_AUDIO) += snd-soc-hdmi.o
# S3C24XX Machine Support
snd-soc-jive-wm8750-objs := jive_wm8750.o
diff --git a/sound/soc/samsung/hdmi.c b/sound/soc/samsung/hdmi.c
new file mode 100644
index 0000000..a600a84
--- /dev/null
+++ b/sound/soc/samsung/hdmi.c
@@ -0,0 +1,260 @@
+/*
+ * ALSA SoC Card driver for HDMI audio on Samsung Exynos processors.
+ * Copyright (C) 2013 Samsung corp.
+ * Author: Rahul Sharma <rahul.sharma@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "i2s.h"
+
+/* platform device pointer for eynos hdmi audio device. */
+static struct platform_device *exynos_hdmi_audio_pdev;
+
+static int set_epll_rate(unsigned long rate)
+{
+ int ret;
+ struct clk *fout_epll;
+
+ fout_epll = clk_get(NULL, "fout_epll");
+
+ if (IS_ERR(fout_epll))
+ return PTR_ERR(fout_epll);
+
+ if (rate = clk_get_rate(fout_epll))
+ goto out;
+
+ ret = clk_set_rate(fout_epll, rate);
+ if (ret < 0)
+ goto out;
+
+out:
+ clk_put(fout_epll);
+
+ return 0;
+}
+
+static int hdmi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int bfs, psr, rfs, ret;
+ unsigned long rclk;
+ unsigned long xtal;
+ struct clk *xtal_clk;
+
+ dev_dbg(rtd->dev, "[%d][%s]\n", __LINE__, __func__);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U24:
+ case SNDRV_PCM_FORMAT_S24:
+ bfs = 48;
+ break;
+ case SNDRV_PCM_FORMAT_U16_LE:
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bfs = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_rate(params)) {
+ case 16000:
+ case 22050:
+ case 24000:
+ case 32000:
+ case 44100:
+ case 48000:
+ case 88200:
+ case 96000:
+ if (bfs = 48)
+ rfs = 384;
+ else
+ rfs = 256;
+ break;
+ case 64000:
+ rfs = 384;
+ break;
+ case 8000:
+ case 11025:
+ case 12000:
+ if (bfs = 48)
+ rfs = 768;
+ else
+ rfs = 512;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rclk = params_rate(params) * rfs;
+
+ switch (rclk) {
+ case 4096000:
+ case 5644800:
+ case 6144000:
+ case 8467200:
+ case 9216000:
+ psr = 8;
+ break;
+ case 8192000:
+ case 11289600:
+ case 12288000:
+ case 16934400:
+ case 18432000:
+ psr = 4;
+ break;
+ case 22579200:
+ case 24576000:
+ case 33868800:
+ case 36864000:
+ psr = 2;
+ break;
+ case 67737600:
+ case 73728000:
+ psr = 1;
+ break;
+ default:
+ dev_err(rtd->dev, "rclk = %lu is not yet supported!\n", rclk);
+ return -EINVAL;
+ }
+
+ ret = set_epll_rate(rclk * psr);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
+ return ret;
+
+ xtal_clk = clk_get(NULL, "xtal"); /*xtal clk is input to codec MCLK1*/
+ if (IS_ERR(xtal_clk)) {
+ dev_err(rtd->dev, "%s: failed to get xtal clock\n", __func__);
+ return PTR_ERR(xtal_clk);
+ }
+
+ xtal = clk_get_rate(xtal_clk);
+ clk_put(xtal_clk);
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
+ 0, SND_SOC_CLOCK_OUT);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, SAMSUNG_I2S_DIV_BCLK, bfs);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * HDMI DAI operations.
+ */
+static struct snd_soc_ops hdmi_ops = {
+ .hw_params = hdmi_hw_params,
+};
+
+static struct snd_soc_dai_link hdmi_dai[] = {
+ { /* HDMI Playback i/f */
+ .name = "HDMI Playback",
+ .stream_name = "i2s_Dai",
+ .cpu_dai_name = "samsung-i2s.0",
+ .codec_dai_name = "exynos-hdmi-audio-dai",
+ .platform_name = "samsung-i2s.0",
+ .codec_name = "exynos-hdmi-audio-codec",
+ .ops = &hdmi_ops,
+ },
+};
+
+static struct snd_soc_card hdmi = {
+ .name = "HDMI-AUDIO",
+ .owner = THIS_MODULE,
+ .dai_link = hdmi_dai,
+ .num_links = ARRAY_SIZE(hdmi_dai),
+};
+
+static int hdmi_audio_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct snd_soc_card *card = &hdmi;
+
+ card->dev = &pdev->dev;
+ dev_dbg(&pdev->dev, "[%d][%s]\n", __LINE__, __func__);
+
+ ret = snd_soc_register_card(card);
+ if (ret)
+ dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret);
+
+ return ret;
+}
+
+static int hdmi_audio_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(card);
+
+ return 0;
+}
+
+static struct platform_driver hdmi_audio_driver = {
+ .driver = {
+ .name = "exynos-hdmi-audio",
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = hdmi_audio_probe,
+ .remove = hdmi_audio_remove,
+};
+
+static int __init hdmi_audio_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&hdmi_audio_driver);
+ if (ret < 0)
+ return -EINVAL;
+
+ exynos_hdmi_audio_pdev = platform_device_register_simple
+ ("exynos-hdmi-audio", -1, NULL, 0);
+ if (IS_ERR_OR_NULL(exynos_hdmi_audio_pdev)) {
+ ret = PTR_ERR(exynos_hdmi_audio_pdev);
+ platform_driver_unregister(&hdmi_audio_driver);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit hdmi_audio_exit(void)
+{
+ platform_driver_unregister(&hdmi_audio_driver);
+ platform_device_unregister(exynos_hdmi_audio_pdev);
+}
+
+module_init(hdmi_audio_init);
+module_exit(hdmi_audio_exit);
+
+MODULE_AUTHOR("Rahul Sharma <rahul.sharma@samsung.com>");
+MODULE_DESCRIPTION("ALSA SoC HDMI AUDIO Card");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:hdmi-audio");
--
1.8.0
^ permalink raw reply related
* [RFC PATCH v2 0/5] exynos-hdmi to CDF compliant display driver
From: Rahul Sharma @ 2013-02-07 11:59 UTC (permalink / raw)
To: linux-media, dri-devel, alsa-devel, linux-fbdev
Cc: tomi.valkeinen, laurent.pinchart, broonie, inki.dae,
kyungmin.park, r.sh.open, joshi
V2:
1) Adding hdmi sound card using cdf based hdmi audio codec.
2) DAPM and JACK control to hdmi codec.
3) Offload event handler by adding work queue.
4) Rework based on v1 comments.
Tested for:
1) Mode setting and switching using modetest.
2) Video with HPD and Power related scenarios.
3) Audio playback with Hotplug-Scenarios.
4) DAPM control for hdmi playback.
Pending:
1) Not moved exynos_hdmi to driver/video/display since it will make it
difficult to analyze the code changes.
V1:
This patch set is a proposal to change Exynos Drm Hdmi driver to a CDF
complaint panel driver. This migration serves 2 purposes. One is to eliminate
duplication due to v4l and drm hdmi drivers. Second is to add support for Hdmi
audio ALSA codec which is not possible with drm/v4l hdmi driver (specially for
exynos as hdmi audio and hdmi core registers are intermixed.).
This patch series is based on the Second RFC of CDF from Laurent Pinchart,
(http://lists.freedesktop.org/archives/dri-devel/2012-November/030888.html)
applied to 'for-next' branch at
git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung.git.
[PATCH 1/4] video: display: add event handling, set mode and hdmi ops to cdf
core
This patch adds:
1) Event Notification to CDF Core:
Adds simple event notification mechanism supports multiple
subscribers. This is used for hot-plug notification to the clients
of hdmi display i.e. exynos-drm and alsa-codec. CDF Core maintains
multiple subscriber list. When entity reports a event Core will
route it to all of them. Un-subscription is not implemented which
can be done if notification callback is Null.
2) set_mode to generic ops:
It is meaningful for a panel like hdmi which supports multiple
resolutions.
HDMI needs conversion of Display Modes to Standard Display Timings.
Though, it can be done within the driver but seems more meaningful
if set_mode is called with Timing Details provided by CDF Core.
3) Provision for platform specific interfaces through void *private in display
entity:
It has added void *private to display entity which can be used to
expose interfaces which are very much specific to a particular platform.
In exynos, hpd is connected to the soc via gpio bus. During initial
hdmi poweron, hpd interrupt is not raised as there is no change in the
gpio status. This is solved by providing a platform specific interface
which is queried by the drm to get the hpd state. This interface may
not be required by all platforms.
4) hdmi ops:
get_edid: to query raw EDID data and length from the panel.
check_mode: To check if a given mode is supported by exynos HDMI IP
"AND" Connected HDMI Sink (tv/monitor).
init_audio: Configure hdmi audio registers for Audio interface type
(i2s/ spdif), SF, Audio Channels, BPS.
set_audiostate: enable disable audio.
[PATCH 2/4] drm/edid: temporarily exposing generic edid-read interface from drm
It exposes generic interface from drm_edid.c to get the edid data and length
by any display entity. Once I get clear idea about edid handling in CDF, I need
to revert these temporary changes.
[PATCH 3/4] drm/exynos: moved drm hdmi driver to cdf framework
This patch implements exynos_hdmi_cdf.c which is a glue component between
exynos DRM and hdmi cdf panel. It is a platform driver register through
exynos_drm_drv.c. Exynos_hdmi.c is modified to register hdmi as display panel.
exynos_hdmi_cdf.c registers for exynos hdmi display entity and if successful,
proceeds for mode setting.
[PATCH 4/4] alsa/soc: add hdmi audio codec based on cdf
It registers hdmi-audio codec to the ALSA framework. This is the second client
to the hdmi panel. Once notified by the CDF Core it proceeds towards audio
setting and audio control. It also subscribes for hpd notification to implement
hpd related audio requirements.
Rahul Sharma (5):
video: display: add event handling, set mode and hdmi ops to cdf core
drm/edid: temporarily exposing generic edid-read interface from drm
drm/exynos: moved drm hdmi driver to cdf framework
alsa/soc: add hdmi audio codec based on cdf
alsa/soc: add hdmi audio card using cdf based hdmi codec
drivers/gpu/drm/drm_edid.c | 88 ++++++
drivers/gpu/drm/exynos/Kconfig | 6 +
drivers/gpu/drm/exynos/Makefile | 1 +
drivers/gpu/drm/exynos/exynos_drm_drv.c | 24 ++
drivers/gpu/drm/exynos/exynos_drm_drv.h | 1 +
drivers/gpu/drm/exynos/exynos_hdmi.c | 445 ++++++++++++++++---------------
drivers/gpu/drm/exynos/exynos_hdmi_cdf.c | 370 +++++++++++++++++++++++++
drivers/video/display/display-core.c | 85 ++++++
include/video/display.h | 111 +++++++-
include/video/exynos_hdmi.h | 25 ++
sound/soc/codecs/Kconfig | 3 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/exynos_hdmi_audio.c | 424 +++++++++++++++++++++++++++++
sound/soc/samsung/Kconfig | 8 +
sound/soc/samsung/Makefile | 2 +
sound/soc/samsung/hdmi.c | 260 ++++++++++++++++++
16 files changed, 1638 insertions(+), 217 deletions(-)
create mode 100644 drivers/gpu/drm/exynos/exynos_hdmi_cdf.c
create mode 100644 include/video/exynos_hdmi.h
create mode 100644 sound/soc/codecs/exynos_hdmi_audio.c
create mode 100644 sound/soc/samsung/hdmi.c
--
1.8.0
^ permalink raw reply
* [RFC PATCH v2 1/5] video: display: add event handling, set mode and hdmi ops to cdf core
From: Rahul Sharma @ 2013-02-07 11:59 UTC (permalink / raw)
To: linux-media, dri-devel, alsa-devel, linux-fbdev
Cc: tomi.valkeinen, laurent.pinchart, broonie, inki.dae,
kyungmin.park, r.sh.open, joshi
In-Reply-To: <1360238377-14806-1-git-send-email-rahul.sharma@samsung.com>
This patch adds
1) Event Notification to CDF Core:
Adds simple event notification mechanism supports multiple
subscribers. This is used for hot-plug notification to the clients
of hdmi display i.e. exynos-drm and alsa-codec. CDF Core maintains
multiple subscriber list. When entity reports a event Core will
route it to all of them. Un-superscription is not implemented which
can be done if notification callback is Null.
2) set_mode to generic ops:
It is meaningful for a panel like hdmi which supports multiple
resolutions.
HDMI needs conversion of Display Modes to Standard Display Timings.
Though, it can be done within the driver but seems more meaningful
if set_mode is called with Timing Details provided by CDF Core.
3) Provision for platform specific interfaces through void *private in display
entity:
It has added void *private to display entity which can be used to
expose interfaces which are very much specific to a particular platform.
In exynos, hpd is connected to the soc via gpio bus. During initial
hdmi poweron, hpd interrupt is not raised as there is no change in the
gpio status. This is solved by providing a platform specific interface
which is queried by the drm to get the hpd state. This interface may
not be required by all platforms.
4) hdmi ops:
get_edid: to query raw EDID data and length from the panel.
check_mode: To check if a given mode is supported by exynos HDMI IP
"AND" Connected HDMI Sink (tv/monitor).
init_audio: Configure hdmi audio registers for Audio interface type
(i2s/ spdif), SF, Audio Channels, BPS.
set_audiostate: enable disable audio.
Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com>
---
drivers/video/display/display-core.c | 85 +++++++++++++++++++++++++++
include/video/display.h | 111 ++++++++++++++++++++++++++++++++++-
2 files changed, 193 insertions(+), 3 deletions(-)
diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c
index 55a7399..12fb882 100644
--- a/drivers/video/display/display-core.c
+++ b/drivers/video/display/display-core.c
@@ -99,6 +99,14 @@ int display_entity_get_modes(struct display_entity *entity,
}
EXPORT_SYMBOL_GPL(display_entity_get_modes);
+int display_entity_set_mode(struct display_entity *entity,
+ const struct videomode *mode)
+{
+ if (!entity->opt_ctrl.hdmi || !entity->ops.ctrl->set_mode)
+ return 0;
+ return entity->ops.ctrl->set_mode(entity, mode);
+}
+EXPORT_SYMBOL_GPL(display_entity_set_mode);
/**
* display_entity_get_size - Get display entity physical size
* @entity: The display entity
@@ -140,6 +148,37 @@ int display_entity_get_params(struct display_entity *entity,
}
EXPORT_SYMBOL_GPL(display_entity_get_params);
+int display_entity_subscribe_event(struct display_entity *entity,
+ struct display_event_subscriber *subscriber)
+{
+ if (!entity || !subscriber || !subscriber->notify)
+ return -EINVAL;
+
+ mutex_lock(&entity->entity_mutex);
+ list_add_tail(&subscriber->list, &entity->list_subscribers);
+ mutex_unlock(&entity->entity_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(display_entity_subscribe_event);
+
+int display_entity_notify_event_subscriber(struct display_entity *entity,
+ enum display_entity_event_type type, unsigned int value)
+{
+ struct display_event_subscriber *subscriber;
+
+ if (!entity || type < 0)
+ return -EINVAL;
+
+ mutex_lock(&entity->entity_mutex);
+ list_for_each_entry(subscriber, &entity->list_subscribers, list) {
+ subscriber->notify(entity, type, value, subscriber->context);
+ }
+ mutex_unlock(&entity->entity_mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(display_entity_notify_event_subscriber);
+
/* -----------------------------------------------------------------------------
* Video operations
*/
@@ -312,6 +351,9 @@ int __must_check __display_entity_register(struct display_entity *entity,
kref_init(&entity->ref);
entity->owner = owner;
entity->state = DISPLAY_ENTITY_STATE_OFF;
+ entity->list_subscribers.next = &entity->list_subscribers;
+ entity->list_subscribers.prev = &entity->list_subscribers;
+ mutex_init(&entity->entity_mutex);
mutex_lock(&display_entity_mutex);
list_add(&entity->list, &display_entity_list);
@@ -357,6 +399,49 @@ void display_entity_unregister(struct display_entity *entity)
}
EXPORT_SYMBOL_GPL(display_entity_unregister);
+/* -----------------------------------------------------------------------------
+ * Display Entity Hdmi ops
+ */
+
+int display_entity_hdmi_get_edid(struct display_entity *entity,
+ struct display_entity_edid *edid)
+{
+ if (!entity->opt_ctrl.hdmi || !entity->opt_ctrl.hdmi->get_edid)
+ return 0;
+
+ return entity->opt_ctrl.hdmi->get_edid(entity, edid);
+}
+EXPORT_SYMBOL_GPL(display_entity_hdmi_get_edid);
+
+int display_entity_hdmi_check_mode(struct display_entity *entity,
+ const struct videomode *mode)
+{
+ if (!entity->opt_ctrl.hdmi || !entity->opt_ctrl.hdmi->check_mode)
+ return 0;
+
+ return entity->opt_ctrl.hdmi->check_mode(entity, mode);
+}
+EXPORT_SYMBOL_GPL(display_entity_hdmi_check_mode);
+
+int display_entity_hdmi_init_audio(struct display_entity *entity,
+ const struct display_entity_audio_params *params)
+{
+ if (!entity->opt_ctrl.hdmi || !entity->opt_ctrl.hdmi->init_audio)
+ return 0;
+
+ return entity->opt_ctrl.hdmi->init_audio(entity, params);
+}
+EXPORT_SYMBOL_GPL(display_entity_hdmi_init_audio);
+
+int display_entity_hdmi_set_audiostate(struct display_entity *entity,
+ enum display_entity_audiostate state)
+{
+ if (!entity->opt_ctrl.hdmi || !entity->opt_ctrl.hdmi->set_audiostate)
+ return 0;
+
+ return entity->opt_ctrl.hdmi->set_audiostate(entity, state);
+}
+EXPORT_SYMBOL_GPL(display_entity_hdmi_set_audiostate);
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
MODULE_DESCRIPTION("Display Core");
MODULE_LICENSE("GPL");
diff --git a/include/video/display.h b/include/video/display.h
index 817f4ae..eae373f 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -66,22 +66,64 @@ enum display_entity_stream_state {
DISPLAY_ENTITY_STREAM_CONTINUOUS,
};
+enum display_entity_event_type {
+ DISPLAY_ENTITY_HDMI_HOTPLUG,
+};
+
enum display_entity_interface_type {
DISPLAY_ENTITY_INTERFACE_DPI,
+ DISPLAY_ENTITY_INTERFACE_HDMI,
+};
+
+enum display_entity_audio_interface {
+ DISPLAY_ENTITY_AUDIO_I2S,
+ DISPLAY_ENTITY_AUDIO_SPDIF,
+};
+
+enum display_entity_audiostate {
+ DISPLAY_ENTITY_AUDIOSTATE_OFF,
+ DISPLAY_ENTITY_AUDIOSTATE_ON,
};
struct display_entity_interface_params {
enum display_entity_interface_type type;
};
+struct display_event_subscriber {
+ struct list_head list;
+ void(*notify)(struct display_entity *ent,
+ enum display_entity_event_type type,
+ unsigned int value, void *context);
+ void *context;
+};
+
+struct display_entity_edid {
+ u8 *edid;
+ int len;
+};
+
+struct display_entity_audio_params {
+ enum display_entity_audio_interface type;
+ int channels;
+ int sf;
+ int bits_per_sample;
+};
+
struct display_entity_control_ops {
int (*set_state)(struct display_entity *ent,
enum display_entity_state state);
+
int (*update)(struct display_entity *ent);
+
int (*get_modes)(struct display_entity *ent,
const struct videomode **modes);
+
+ int (*set_mode)(struct display_entity *entity,
+ const struct videomode *modes);
+
int (*get_params)(struct display_entity *ent,
struct display_entity_interface_params *params);
+
int (*get_size)(struct display_entity *ent,
unsigned int *width, unsigned int *height);
};
@@ -91,8 +133,29 @@ struct display_entity_video_ops {
enum display_entity_stream_state state);
};
+struct display_entity_hdmi_control_ops {
+
+ int (*get_edid)(struct display_entity *ent,
+ struct display_entity_edid *edid);
+
+ int (*check_mode)(struct display_entity *entity,
+ const struct videomode *modes);
+
+ int (*init_audio)(struct display_entity *entity,
+ const struct display_entity_audio_params *params);
+
+ int (*set_audiostate)(struct display_entity *entity,
+ enum display_entity_audiostate state);
+};
+
+struct display_entity_hdmi_video_ops {
+ int (*get_edid)(struct display_entity *ent,
+ enum display_entity_stream_state state);
+};
+
struct display_entity {
struct list_head list;
+ struct list_head list_subscribers;
struct device *dev;
struct module *owner;
struct kref ref;
@@ -104,26 +167,51 @@ struct display_entity {
const struct display_entity_video_ops *video;
} ops;
+ union {
+ const struct display_entity_hdmi_control_ops *hdmi;
+ } opt_ctrl;
+
+ union {
+ const struct display_entity_hdmi_video_ops *hdmi;
+ } opt_video;
+
void(*release)(struct display_entity *ent);
enum display_entity_state state;
+ struct mutex entity_mutex;
+ void *private;
};
+/* generic display entity ops */
+
int display_entity_set_state(struct display_entity *entity,
enum display_entity_state state);
+
int display_entity_update(struct display_entity *entity);
+
int display_entity_get_modes(struct display_entity *entity,
const struct videomode **modes);
+
+int display_entity_set_mode(struct display_entity *entity,
+ const struct videomode *modes);
+
int display_entity_get_params(struct display_entity *entity,
- struct display_entity_interface_params *params);
+ struct display_entity_interface_params *params);
+
int display_entity_get_size(struct display_entity *entity,
- unsigned int *width, unsigned int *height);
+ unsigned int *width, unsigned int *height);
int display_entity_set_stream(struct display_entity *entity,
enum display_entity_stream_state state);
+int display_entity_subscribe_event(struct display_entity *entity,
+ struct display_event_subscriber *subscriber);
+
+int display_entity_notify_event_subscriber(struct display_entity *entity,
+ enum display_entity_event_type type, unsigned int value);
+
static inline void display_entity_connect(struct display_entity *source,
- struct display_entity *sink)
+ struct display_entity *sink)
{
sink->source = source;
}
@@ -147,4 +235,21 @@ void display_entity_unregister_notifier(struct display_entity_notifier *notifier
#define display_entity_register(display_entity) \
__display_entity_register(display_entity, THIS_MODULE)
+/* hdmi ops */
+
+int display_entity_hdmi_get_edid(struct display_entity *entity,
+ struct display_entity_edid *edid);
+
+int display_entity_hdmi_check_mode(struct display_entity *entity,
+ const struct videomode *modes);
+
+int display_entity_hdmi_get_hpdstate(struct display_entity *entity,
+ unsigned int *hpd_state);
+
+int display_entity_hdmi_init_audio(struct display_entity *entity,
+ const struct display_entity_audio_params *params);
+
+int display_entity_hdmi_set_audiostate(struct display_entity *entity,
+ enum display_entity_audiostate state);
+
#endif /* __DISPLAY_H__ */
--
1.8.0
^ permalink raw reply related
* Re: [RFC PATCH v2 2/5] drm/edid: temporarily exposing generic edid-read interface from drm
From: Daniel Vetter @ 2013-02-07 13:28 UTC (permalink / raw)
To: Rahul Sharma
Cc: linux-media, dri-devel, alsa-devel, linux-fbdev, broonie, joshi,
kyungmin.park, tomi.valkeinen, laurent.pinchart
In-Reply-To: <1360238951-7022-1-git-send-email-rahul.sharma@samsung.com>
On Thu, Feb 7, 2013 at 1:09 PM, Rahul Sharma <rahul.sharma@samsung.com> wrote:
> It exposes generic interface from drm_edid.c to get the edid data and length
> by any display entity. Once I get clear idea about edid handling in CDF, I need
> to revert these temporary changes.
Just a quick reply about edid reading: One of the key results (at
least imo) of the fosdem cdf discussion was that we need to split up
the different parts of it clearly (i.e. abstract panel interface, dsi
support, discovery/dev matching, ...) to have more flexibility. One
idea is also to not use the panel interface for e.g. hdmi transcoders,
but only use the bus support (like dsi), since transcoders which
connect to external devices like hdmi need to expose _much_ more
features to the master driver and so it's better to have tighter
integration. Some of the things which need close cooperation between
drivers are e.g. edid reading, hotplug handling,
bpp/colorspace/restricted range stuff, ... I didn't read through the
patch which requires the exported drm edid stuff, but maybe this helps
a bit.
-Daniel
--
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
^ permalink raw reply
* [PATCH 0/1] OMAP4: DSS: Add panel for Blaze Tablet boards
From: Ruslan Bilovol @ 2013-02-07 14:40 UTC (permalink / raw)
To: tomi.valkeinen, FlorianSchandinat, linux-fbdev, linux-kernel,
linux-omap
Hi,
This patch adds support for TC358765 DSI-to-LVDS transmitter
from Toshiba, that is used in OMAP4 Blaze Tablet development
platform. It was originally developed a long time ago and
was used internally survived few kernel migrations.
Different people worked in this driver during last two
years changing its sources to what each new version of
kernel expects.
Current version is ported from internal kernel v3.4 to 3.8.0-rc6
Tested basic functioning under busybox.
-
Ruslan
Tomi Valkeinen (1):
OMAP4: DSS: Add panel for Blaze Tablet boards
drivers/video/omap2/displays/Kconfig | 15 +
drivers/video/omap2/displays/Makefile | 1 +
drivers/video/omap2/displays/panel-tc358765.c | 1001 +++++++++++++++++++++++++
drivers/video/omap2/displays/panel-tc358765.h | 170 +++++
include/video/omap-panel-tc358765.h | 53 ++
5 files changed, 1240 insertions(+)
create mode 100644 drivers/video/omap2/displays/panel-tc358765.c
create mode 100644 drivers/video/omap2/displays/panel-tc358765.h
create mode 100644 include/video/omap-panel-tc358765.h
--
1.7.9.5
^ permalink raw reply
* [PATCH 1/1] OMAP4: DSS: Add panel for Blaze Tablet boards
From: Ruslan Bilovol @ 2013-02-07 14:40 UTC (permalink / raw)
To: tomi.valkeinen, FlorianSchandinat, linux-fbdev, linux-kernel,
linux-omap
In-Reply-To: <1360248051-16541-1-git-send-email-ruslan.bilovol@ti.com>
From: Tomi Valkeinen <tomi.valkeinen@ti.com>
TC358765 is DSI-to-LVDS transmitter from Toshiba, used in
OMAP44XX Blaze Tablet and Blaze Tablet2 boards.
Signed-off-by: Dan Murphy <dmurphy@ti.com>
Signed-off-by: Sergiy Kibrik <sergiy.kibrik@globallogic.com>
Signed-off-by: Volodymyr Riazantsev <v.riazantsev@ti.com>
Signed-off-by: Ruslan Bilovol <ruslan.bilovol@ti.com>
---
drivers/video/omap2/displays/Kconfig | 15 +
drivers/video/omap2/displays/Makefile | 1 +
drivers/video/omap2/displays/panel-tc358765.c | 1001 +++++++++++++++++++++++++
drivers/video/omap2/displays/panel-tc358765.h | 170 +++++
include/video/omap-panel-tc358765.h | 53 ++
5 files changed, 1240 insertions(+)
create mode 100644 drivers/video/omap2/displays/panel-tc358765.c
create mode 100644 drivers/video/omap2/displays/panel-tc358765.h
create mode 100644 include/video/omap-panel-tc358765.h
diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig
index c3853c9..c6ab261 100644
--- a/drivers/video/omap2/displays/Kconfig
+++ b/drivers/video/omap2/displays/Kconfig
@@ -72,4 +72,19 @@ config PANEL_N8X0
depends on BACKLIGHT_CLASS_DEVICE
help
This is the LCD panel used on Nokia N8x0
+
+config PANEL_TC358765
+ tristate "Toshiba TC358765 DSI-2-LVDS bridge"
+ depends on OMAP2_DSS_DSI && I2C
+ select BACKLIGHT_CLASS_DEVICE
+ help
+ Toshiba TC358765 DSI-2-LVDS chip with 1024x768 panel,
+ which can be found in OMAP4-based Blaze Tablet boards
+ and some other boards.
+
+config TC358765_DEBUG
+ bool "Toshiba TC358765 DSI-2-LVDS chip debug"
+ depends on PANEL_TC358765 && DEBUG_FS
+ help
+ Support of TC358765 DSI-2-LVDS chip register access via debugfs
endmenu
diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile
index 58a5176..b9f2ab6 100644
--- a/drivers/video/omap2/displays/Makefile
+++ b/drivers/video/omap2/displays/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_PANEL_PICODLP) += panel-picodlp.o
obj-$(CONFIG_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
obj-$(CONFIG_PANEL_ACX565AKM) += panel-acx565akm.o
obj-$(CONFIG_PANEL_N8X0) += panel-n8x0.o
+obj-$(CONFIG_PANEL_TC358765) += panel-tc358765.o
diff --git a/drivers/video/omap2/displays/panel-tc358765.c b/drivers/video/omap2/displays/panel-tc358765.c
new file mode 100644
index 0000000..e9d7e96
--- /dev/null
+++ b/drivers/video/omap2/displays/panel-tc358765.c
@@ -0,0 +1,1001 @@
+/*
+ * Toshiba TC358765 DSI-to-LVDS chip driver
+ *
+ * Copyright (C) Texas Instruments
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> (3.0)
+ * Author: Sergii Kibrik <sergiikibrik@ti.com> (3.4)
+ * Author: Ruslan Bilovol <ruslan.bilovol@ti.com> (3.8+)
+ *
+ * Based on original version from Jerry Alexander <x0135174@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/mutex.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+
+#include <video/omapdss.h>
+#include <video/omap-panel-tc358765.h>
+
+#include "panel-tc358765.h"
+
+#define A_RO 0x1
+#define A_WO 0x2
+#define A_RW (A_RO|A_WO)
+
+#define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end))
+#define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end))
+#define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end))
+#define FLD_MOD(orig, val, start, end) \
+ (((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end))
+
+static struct omap_video_timings tc358765_timings;
+static struct tc358765_board_data *get_board_data(struct omap_dss_device
+ *dssdev) __attribute__ ((unused));
+
+/* device private data structure */
+struct tc358765_data {
+ struct mutex lock;
+
+ struct omap_dss_device *dssdev;
+
+ int channel0;
+ int channel1;
+
+ struct omap_dsi_pin_config pin_config;
+};
+
+static struct {
+ struct i2c_client *client;
+ struct mutex xfer_lock;
+} *tc358765_i2c;
+
+
+#ifdef CONFIG_TC358765_DEBUG
+
+struct {
+ struct device *dev;
+ struct dentry *dir;
+} tc358765_debug;
+
+struct tc358765_reg {
+ const char *name;
+ u16 reg;
+ u8 perm:2;
+} tc358765_regs[] = {
+ /* DSI D-PHY Layer Registers */
+ { "D0W_DPHYCONTTX", D0W_DPHYCONTTX, A_RW },
+ { "CLW_DPHYCONTRX", CLW_DPHYCONTRX, A_RW },
+ { "D0W_DPHYCONTRX", D0W_DPHYCONTRX, A_RW },
+ { "D1W_DPHYCONTRX", D1W_DPHYCONTRX, A_RW },
+ { "D2W_DPHYCONTRX", D2W_DPHYCONTRX, A_RW },
+ { "D3W_DPHYCONTRX", D3W_DPHYCONTRX, A_RW },
+ { "COM_DPHYCONTRX", COM_DPHYCONTRX, A_RW },
+ { "CLW_CNTRL", CLW_CNTRL, A_RW },
+ { "D0W_CNTRL", D0W_CNTRL, A_RW },
+ { "D1W_CNTRL", D1W_CNTRL, A_RW },
+ { "D2W_CNTRL", D2W_CNTRL, A_RW },
+ { "D3W_CNTRL", D3W_CNTRL, A_RW },
+ { "DFTMODE_CNTRL", DFTMODE_CNTRL, A_RW },
+ /* DSI PPI Layer Registers */
+ { "PPI_STARTPPI", PPI_STARTPPI, A_RW },
+ { "PPI_BUSYPPI", PPI_BUSYPPI, A_RO },
+ { "PPI_LINEINITCNT", PPI_LINEINITCNT, A_RW },
+ { "PPI_LPTXTIMECNT", PPI_LPTXTIMECNT, A_RW },
+ { "PPI_LANEENABLE", PPI_LANEENABLE, A_RW },
+ { "PPI_TX_RX_TA", PPI_TX_RX_TA, A_RW },
+ { "PPI_CLS_ATMR", PPI_CLS_ATMR, A_RW },
+ { "PPI_D0S_ATMR", PPI_D0S_ATMR, A_RW },
+ { "PPI_D1S_ATMR", PPI_D1S_ATMR, A_RW },
+ { "PPI_D2S_ATMR", PPI_D2S_ATMR, A_RW },
+ { "PPI_D3S_ATMR", PPI_D3S_ATMR, A_RW },
+ { "PPI_D0S_CLRSIPOCOUNT", PPI_D0S_CLRSIPOCOUNT, A_RW },
+ { "PPI_D1S_CLRSIPOCOUNT", PPI_D1S_CLRSIPOCOUNT, A_RW },
+ { "PPI_D2S_CLRSIPOCOUNT", PPI_D2S_CLRSIPOCOUNT, A_RW },
+ { "PPI_D3S_CLRSIPOCOUNT", PPI_D3S_CLRSIPOCOUNT, A_RW },
+ { "CLS_PRE", CLS_PRE, A_RW },
+ { "D0S_PRE", D0S_PRE, A_RW },
+ { "D1S_PRE", D1S_PRE, A_RW },
+ { "D2S_PRE", D2S_PRE, A_RW },
+ { "D3S_PRE", D3S_PRE, A_RW },
+ { "CLS_PREP", CLS_PREP, A_RW },
+ { "D0S_PREP", D0S_PREP, A_RW },
+ { "D1S_PREP", D1S_PREP, A_RW },
+ { "D2S_PREP", D2S_PREP, A_RW },
+ { "D3S_PREP", D3S_PREP, A_RW },
+ { "CLS_ZERO", CLS_ZERO, A_RW },
+ { "D0S_ZERO", D0S_ZERO, A_RW },
+ { "D1S_ZERO", D1S_ZERO, A_RW },
+ { "D2S_ZERO", D2S_ZERO, A_RW },
+ { "D3S_ZERO", D3S_ZERO, A_RW },
+ { "PPI_CLRFLG", PPI_CLRFLG, A_RW },
+ { "PPI_CLRSIPO", PPI_CLRSIPO, A_RW },
+ { "PPI_HSTimeout", PPI_HSTimeout, A_RW },
+ { "PPI_HSTimeoutEnable", PPI_HSTimeoutEnable, A_RW },
+ /* DSI Protocol Layer Registers */
+ { "DSI_STARTDSI", DSI_STARTDSI, A_WO },
+ { "DSI_BUSYDSI", DSI_BUSYDSI, A_RO },
+ { "DSI_LANEENABLE", DSI_LANEENABLE, A_RW },
+ { "DSI_LANESTATUS0", DSI_LANESTATUS0, A_RO },
+ { "DSI_LANESTATUS1", DSI_LANESTATUS1, A_RO },
+ { "DSI_INTSTATUS", DSI_INTSTATUS, A_RO },
+ { "DSI_INTMASK", DSI_INTMASK, A_RW },
+ { "DSI_INTCLR", DSI_INTCLR, A_WO },
+ { "DSI_LPTXTO", DSI_LPTXTO, A_RW },
+ /* DSI General Registers */
+ { "DSIERRCNT", DSIERRCNT, A_RW },
+ /* DSI Application Layer Registers */
+ { "APLCTRL", APLCTRL, A_RW },
+ { "RDPKTLN", RDPKTLN, A_RW },
+ /* Video Path Registers */
+ { "VPCTRL", VPCTRL, A_RW },
+ { "HTIM1", HTIM1, A_RW },
+ { "HTIM2", HTIM2, A_RW },
+ { "VTIM1", VTIM1, A_RW },
+ { "VTIM2", VTIM2, A_RW },
+ { "VFUEN", VFUEN, A_RW },
+ /* LVDS Registers */
+ { "LVMX0003", LVMX0003, A_RW },
+ { "LVMX0407", LVMX0407, A_RW },
+ { "LVMX0811", LVMX0811, A_RW },
+ { "LVMX1215", LVMX1215, A_RW },
+ { "LVMX1619", LVMX1619, A_RW },
+ { "LVMX2023", LVMX2023, A_RW },
+ { "LVMX2427", LVMX2427, A_RW },
+ { "LVCFG", LVCFG, A_RW },
+ { "LVPHY0", LVPHY0, A_RW },
+ { "LVPHY1", LVPHY1, A_RW },
+ /* System Registers */
+ { "SYSSTAT", SYSSTAT, A_RO },
+ { "SYSRST", SYSRST, A_WO },
+ /* GPIO Registers */
+ { "GPIOC", GPIOC, A_RW },
+ { "GPIOO", GPIOO, A_RW },
+ { "GPIOI", GPIOI, A_RO },
+ /* I2C Registers */
+ { "I2CTIMCTRL", I2CTIMCTRL, A_RW },
+ { "I2CMADDR", I2CMADDR, A_RW },
+ { "WDATAQ", WDATAQ, A_WO },
+ { "RDATAQ", RDATAQ, A_WO },
+ /* Chip/Rev Registers */
+ { "IDREG", IDREG, A_RO },
+ /* Debug Registers */
+ { "DEBUG00", DEBUG00, A_RW },
+ { "DEBUG01", DEBUG01, A_RW },
+};
+#endif
+
+static int tc358765_read_block(u16 reg, u8 *data, int len)
+{
+ unsigned char wb[2];
+ struct i2c_msg msg[2];
+ int r;
+ mutex_lock(&tc358765_i2c->xfer_lock);
+ wb[0] = (reg & 0xff00) >> 8;
+ wb[1] = reg & 0xff;
+ msg[0].addr = tc358765_i2c->client->addr;
+ msg[0].len = 2;
+ msg[0].flags = 0;
+ msg[0].buf = wb;
+ msg[1].addr = tc358765_i2c->client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = len;
+ msg[1].buf = data;
+
+ r = i2c_transfer(tc358765_i2c->client->adapter, msg, ARRAY_SIZE(msg));
+ mutex_unlock(&tc358765_i2c->xfer_lock);
+
+ if (r = ARRAY_SIZE(msg))
+ return len;
+
+ return r;
+}
+
+static int tc358765_i2c_read(u16 reg, u32 *val)
+{
+ int r;
+ u8 data[4];
+ data[0] = data[1] = data[2] = data[3] = 0;
+
+ r = tc358765_read_block(reg, data, ARRAY_SIZE(data));
+ if (r != ARRAY_SIZE(data))
+ return r;
+
+ *val = ((int)data[3] << 24) | ((int)(data[2]) << 16) |
+ ((int)(data[1]) << 8) | ((int)(data[0]));
+ return 0;
+}
+
+static int tc358765_dsi_read(struct omap_dss_device *dssdev, u16 reg, u32 *val)
+{
+ struct tc358765_data *d2d = dev_get_drvdata(&dssdev->dev);
+ u8 buf[4];
+ int r;
+
+ r = dsi_vc_generic_read_2(dssdev, d2d->channel1, ((u8 *)®)[0],
+ ((u8 *)®)[1], buf, 4);
+ if (r < 0) {
+ dev_err(&dssdev->dev, "0x%x read failed with status %d\n",
+ reg, r);
+ return r;
+ }
+
+ *val = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+ return 0;
+}
+
+static int tc358765_read_register(struct omap_dss_device *dssdev,
+ u16 reg, u32 *val)
+{
+ int ret = 0;
+ pm_runtime_get_sync(&dssdev->dev);
+ /* I2C is preferred way of reading, but fall back to DSI
+ * if I2C didn't got initialized
+ */
+ if (tc358765_i2c)
+ ret = tc358765_i2c_read(reg, val);
+ else
+ ret = tc358765_dsi_read(dssdev, reg, val);
+ pm_runtime_put_sync(&dssdev->dev);
+
+ return ret;
+}
+
+static int tc358765_write_register(struct omap_dss_device *dssdev, u16 reg,
+ u32 value)
+{
+ struct tc358765_data *d2d = dev_get_drvdata(&dssdev->dev);
+ u8 buf[6];
+ int r;
+
+ buf[0] = (reg >> 0) & 0xff;
+ buf[1] = (reg >> 8) & 0xff;
+ buf[2] = (value >> 0) & 0xff;
+ buf[3] = (value >> 8) & 0xff;
+ buf[4] = (value >> 16) & 0xff;
+ buf[5] = (value >> 24) & 0xff;
+
+ r = dsi_vc_generic_write_nosync(dssdev, d2d->channel1, buf, 6);
+ if (r)
+ dev_err(&dssdev->dev, "reg write reg(%x) val(%x) failed: %d\n",
+ reg, value, r);
+ return r;
+}
+
+/****************************
+********* DEBUG *************
+****************************/
+#ifdef CONFIG_TC358765_DEBUG
+static int tc358765_write_register_i2c(u16 reg, u32 val)
+{
+ int ret = -ENODEV;
+ unsigned char buf[6];
+ struct i2c_msg msg;
+
+ if (!tc358765_i2c) {
+ dev_err(tc358765_debug.dev, "%s: I2C not initilized\n",
+ __func__);
+ return ret;
+ }
+
+ buf[0] = (reg >> 8) & 0xff;
+ buf[1] = (reg >> 0) & 0xff;
+ buf[2] = (val >> 0) & 0xff;
+ buf[3] = (val >> 8) & 0xff;
+ buf[4] = (val >> 16) & 0xff;
+ buf[5] = (val >> 24) & 0xff;
+ msg.addr = tc358765_i2c->client->addr;
+ msg.len = sizeof(buf);
+ msg.flags = 0;
+ msg.buf = buf;
+
+ mutex_lock(&tc358765_i2c->xfer_lock);
+ ret = i2c_transfer(tc358765_i2c->client->adapter, &msg, 1);
+ mutex_unlock(&tc358765_i2c->xfer_lock);
+
+ if (ret != 1)
+ return ret;
+ return 0;
+}
+
+
+static int tc358765_registers_show(struct seq_file *seq, void *pos)
+{
+ struct device *dev = tc358765_debug.dev;
+ unsigned i, reg_count;
+ uint value;
+
+ if (!tc358765_i2c) {
+ dev_warn(dev,
+ "failed to read register: I2C not initialized\n");
+ return -ENODEV;
+ }
+
+ reg_count = sizeof(tc358765_regs) / sizeof(tc358765_regs[0]);
+ pm_runtime_get_sync(dev);
+ for (i = 0; i < reg_count; i++) {
+ if (tc358765_regs[i].perm & A_RO) {
+ tc358765_i2c_read(tc358765_regs[i].reg, &value);
+ seq_printf(seq, "%-20s = 0x%02X\n",
+ tc358765_regs[i].name, value);
+ }
+ }
+
+ pm_runtime_put_sync(dev);
+ return 0;
+}
+static ssize_t tc358765_seq_write(struct file *filp, const char __user *ubuf,
+ size_t size, loff_t *ppos)
+{
+ struct device *dev = tc358765_debug.dev;
+ unsigned i, reg_count;
+ u32 value = 0;
+ int error = 0;
+ /* kids, don't use register names that long */
+ char name[30];
+ char buf[50];
+
+ if (size >= sizeof(buf))
+ size = sizeof(buf);
+
+ if (copy_from_user(&buf, ubuf, size))
+ return -EFAULT;
+
+ buf[size-1] = '\0';
+ if (sscanf(buf, "%s %x", name, &value) != 2) {
+ dev_err(dev, "%s: unable to parse input\n", __func__);
+ return -1;
+ }
+
+ if (!tc358765_i2c) {
+ dev_warn(dev,
+ "failed to write register: I2C not initialized\n");
+ return -ENODEV;
+ }
+
+ reg_count = sizeof(tc358765_regs) / sizeof(tc358765_regs[0]);
+ for (i = 0; i < reg_count; i++) {
+ if (!strcmp(name, tc358765_regs[i].name)) {
+ if (!(tc358765_regs[i].perm & A_WO)) {
+ dev_err(dev, "%s is write-protected\n", name);
+ return -EACCES;
+ }
+
+ error = tc358765_write_register_i2c(
+ tc358765_regs[i].reg, value);
+ if (error) {
+ dev_err(dev, "%s: failed to write %s\n",
+ __func__, name);
+ return -1;
+ }
+
+ return size;
+ }
+ }
+
+ dev_err(dev, "%s: no such register %s\n", __func__, name);
+
+ return size;
+}
+
+static ssize_t tc358765_seq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, tc358765_registers_show, inode->i_private);
+}
+
+static const struct file_operations tc358765_debug_fops = {
+ .open = tc358765_seq_open,
+ .read = seq_read,
+ .write = tc358765_seq_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int tc358765_initialize_debugfs(struct omap_dss_device *dssdev)
+{
+ tc358765_debug.dir = debugfs_create_dir("tc358765", NULL);
+ if (IS_ERR(tc358765_debug.dir)) {
+ int ret = PTR_ERR(tc358765_debug.dir);
+ tc358765_debug.dir = NULL;
+ return ret;
+ }
+
+ tc358765_debug.dev = &dssdev->dev;
+ debugfs_create_file("registers", S_IRWXU, tc358765_debug.dir,
+ dssdev, &tc358765_debug_fops);
+ return 0;
+}
+
+static void tc358765_uninitialize_debugfs(void)
+{
+ if (tc358765_debug.dir)
+ debugfs_remove_recursive(tc358765_debug.dir);
+ tc358765_debug.dir = NULL;
+ tc358765_debug.dev = NULL;
+}
+
+#else
+static int tc358765_initialize_debugfs(struct omap_dss_device *dssdev)
+{
+ return 0;
+}
+
+static void tc358765_uninitialize_debugfs(void)
+{
+}
+#endif
+
+static struct tc358765_board_data *get_board_data(struct omap_dss_device
+ *dssdev)
+{
+ return (struct tc358765_board_data *)dssdev->data;
+}
+
+static void tc358765_get_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ *timings = dssdev->panel.timings;
+}
+
+static void tc358765_set_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ dev_info(&dssdev->dev, "set_timings() not implemented\n");
+}
+
+static int tc358765_check_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ if (unlikely(!timings)) {
+ WARN(true, "%s: timings NULL pointer was passed\n", __func__);
+ return -EINVAL;
+ }
+
+ if (tc358765_timings.x_res != timings->x_res ||
+ tc358765_timings.y_res != timings->y_res ||
+ tc358765_timings.pixel_clock != timings->pixel_clock ||
+ tc358765_timings.hsw != timings->hsw ||
+ tc358765_timings.hfp != timings->hfp ||
+ tc358765_timings.hbp != timings->hbp ||
+ tc358765_timings.vsw != timings->vsw ||
+ tc358765_timings.vfp != timings->vfp ||
+ tc358765_timings.vbp != timings->vbp)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void tc358765_get_resolution(struct omap_dss_device *dssdev,
+ u16 *xres, u16 *yres)
+{
+ *xres = tc358765_timings.x_res;
+ *yres = tc358765_timings.y_res;
+}
+
+static int tc358765_hw_reset(struct omap_dss_device *dssdev)
+{
+
+ if (dssdev = NULL || dssdev->reset_gpio = -1)
+ return 0;
+
+ gpio_set_value(dssdev->reset_gpio, 1);
+ udelay(200);
+ /* reset the panel */
+ gpio_set_value(dssdev->reset_gpio, 0);
+ /* assert reset */
+ udelay(200);
+ gpio_set_value(dssdev->reset_gpio, 1);
+ /* wait after releasing reset */
+ msleep(200);
+
+ return 0;
+}
+
+static void tc358765_probe_pdata(struct tc358765_data *d2d,
+ const struct tc358765_board_data *pdata)
+{
+ d2d->pin_config = pdata->pin_config;
+}
+
+static int tc358765_probe(struct omap_dss_device *dssdev)
+{
+ struct tc358765_data *d2d;
+ int r = 0;
+
+ dev_dbg(&dssdev->dev, "tc358765_probe\n");
+
+ dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888;
+ tc358765_timings = dssdev->panel.timings;
+
+ d2d = kzalloc(sizeof(*d2d), GFP_KERNEL);
+ if (!d2d) {
+ r = -ENOMEM;
+ goto err;
+ }
+
+ d2d->dssdev = dssdev;
+
+ mutex_init(&d2d->lock);
+
+ dev_set_drvdata(&dssdev->dev, d2d);
+
+ if (dssdev->data) {
+ const struct tc358765_board_data *pdata = dssdev->data;
+
+ tc358765_probe_pdata(d2d, pdata);
+ } else {
+ return -ENODEV;
+ }
+
+ /* channel0 used for video packets */
+ r = omap_dsi_request_vc(dssdev, &d2d->channel0);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to get virtual channel0\n");
+ goto err;
+ }
+
+ r = omap_dsi_set_vc_id(dssdev, d2d->channel0, 0);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to set VC_ID0\n");
+ goto err_ch0;
+ }
+
+ /* channel1 used for registers access in LP mode */
+ r = omap_dsi_request_vc(dssdev, &d2d->channel1);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to get virtual channel1\n");
+ goto err_ch0;
+ }
+
+ r = omap_dsi_set_vc_id(dssdev, d2d->channel1, 0);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to set VC_ID1\n");
+ goto err_ch1;
+ }
+ r = tc358765_initialize_debugfs(dssdev);
+ if (r)
+ dev_warn(&dssdev->dev, "failed to create sysfs files\n");
+
+ dev_dbg(&dssdev->dev, "tc358765_probe done\n");
+ return 0;
+
+err_ch1:
+ omap_dsi_release_vc(dssdev, d2d->channel1);
+err_ch0:
+ omap_dsi_release_vc(dssdev, d2d->channel0);
+err:
+ mutex_destroy(&d2d->lock);
+ kfree(d2d);
+ return r;
+}
+
+static void tc358765_remove(struct omap_dss_device *dssdev)
+{
+ struct tc358765_data *d2d = dev_get_drvdata(&dssdev->dev);
+
+ tc358765_uninitialize_debugfs();
+
+ omap_dsi_release_vc(dssdev, d2d->channel0);
+ omap_dsi_release_vc(dssdev, d2d->channel1);
+ mutex_destroy(&d2d->lock);
+
+ kfree(d2d);
+}
+
+static int tc358765_init_ppi(struct omap_dss_device *dssdev)
+{
+ u32 go_cnt, sure_cnt, val = 0;
+ u8 lanes = 0;
+ int ret = 0;
+ struct tc358765_board_data *board_data = get_board_data(dssdev);
+ const int *pins = board_data->pin_config.pins;
+
+ /*
+ * This register setting is required only if host wishes to
+ * perform DSI read transactions
+ */
+ go_cnt = (board_data->lp_time * 5 - 3) / 4;
+ sure_cnt = DIV_ROUND_UP(board_data->lp_time * 3, 2);
+ val = FLD_MOD(val, go_cnt, 26, 16);
+ val = FLD_MOD(val, sure_cnt, 10, 0);
+ ret |= tc358765_write_register(dssdev, PPI_TX_RX_TA, val);
+
+ /* SYSLPTX Timing Generation Counter */
+ ret |= tc358765_write_register(dssdev, PPI_LPTXTIMECNT,
+ board_data->lp_time);
+
+ /* D*S_CLRSIPOCOUNT = [(THS-SETTLE + THS-ZERO) /
+ HS_byte_clock_period ] */
+
+ if ((pins[0] & 1) || (pins[1] & 1))
+ lanes |= (1 << 0);
+
+ if ((pins[2] & 1) || (pins[3] & 1)) {
+ lanes |= (1 << 1);
+ ret |= tc358765_write_register(dssdev, PPI_D0S_CLRSIPOCOUNT,
+ board_data->clrsipo);
+ }
+ if ((pins[4] & 1) || (pins[5] & 1)) {
+ lanes |= (1 << 2);
+ ret |= tc358765_write_register(dssdev, PPI_D1S_CLRSIPOCOUNT,
+ board_data->clrsipo);
+ }
+ if ((pins[6] & 1) || (pins[7] & 1)) {
+ lanes |= (1 << 3);
+ ret |= tc358765_write_register(dssdev, PPI_D2S_CLRSIPOCOUNT,
+ board_data->clrsipo);
+ }
+ if ((pins[8] & 1) || (pins[9] & 1)) {
+ lanes |= (1 << 4);
+ ret |= tc358765_write_register(dssdev, PPI_D3S_CLRSIPOCOUNT,
+ board_data->clrsipo);
+ }
+
+ ret |= tc358765_write_register(dssdev, PPI_LANEENABLE, lanes);
+ ret |= tc358765_write_register(dssdev, DSI_LANEENABLE, lanes);
+
+ return ret;
+}
+
+static int tc358765_init_video_timings(struct omap_dss_device *dssdev)
+{
+ u32 val;
+ struct tc358765_board_data *board_data = get_board_data(dssdev);
+ int ret;
+ ret = tc358765_read_register(dssdev, VPCTRL, &val);
+ if (ret < 0) {
+ dev_warn(&dssdev->dev,
+ "couldn't access VPCTRL, going on with reset value\n");
+ val = 0;
+ }
+
+ if (dssdev->ctrl.pixel_size = 18) {
+ /* Magic Square FRC available for RGB666 only */
+ val = FLD_MOD(val, board_data->msf, 0, 0);
+ val = FLD_MOD(val, 0, 8, 8);
+ } else {
+ val = FLD_MOD(val, 1, 8, 8);
+ }
+
+ val = FLD_MOD(val, board_data->vtgen, 4, 4);
+ val = FLD_MOD(val, board_data->evtmode, 5, 5);
+ val = FLD_MOD(val, board_data->vsdelay, 31, 20);
+
+ ret = tc358765_write_register(dssdev, VPCTRL, val);
+
+ ret |= tc358765_write_register(dssdev, HTIM1,
+ (tc358765_timings.hbp << 16) | tc358765_timings.hsw);
+ ret |= tc358765_write_register(dssdev, HTIM2,
+ ((tc358765_timings.hfp << 16) | tc358765_timings.x_res));
+ ret |= tc358765_write_register(dssdev, VTIM1,
+ ((tc358765_timings.vbp << 16) | tc358765_timings.vsw));
+ ret |= tc358765_write_register(dssdev, VTIM2,
+ ((tc358765_timings.vfp << 16) | tc358765_timings.y_res));
+ return ret;
+}
+
+static int tc358765_write_init_config(struct omap_dss_device *dssdev)
+{
+ struct tc358765_board_data *board_data = get_board_data(dssdev);
+ u32 val;
+ int r;
+
+ /* HACK: dummy read: if we read via DSI, first reads always fail */
+ tc358765_read_register(dssdev, DSI_INTSTATUS, &val);
+
+ r = tc358765_init_ppi(dssdev);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to initialize PPI layer\n");
+ return r;
+ }
+
+ r = tc358765_write_register(dssdev, PPI_STARTPPI, 0x1);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to start PPI-TX\n");
+ return r;
+ }
+
+ r = tc358765_write_register(dssdev, DSI_STARTDSI, 0x1);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to start DSI-RX\n");
+ return r;
+ }
+
+ /* reset LVDS-PHY */
+ tc358765_write_register(dssdev, LVPHY0, (1 << 22));
+ mdelay(2);
+
+ r = tc358765_read_register(dssdev, LVPHY0, &val);
+ if (r < 0) {
+ dev_warn(&dssdev->dev, "couldn't access LVPHY0, going on with reset value\n");
+ val = 0;
+ }
+ val = FLD_MOD(val, 0, LV_RST_E, LV_RST_B);
+ val = FLD_MOD(val, board_data->lv_is, LV_IS_E, LV_IS_B);
+ val = FLD_MOD(val, board_data->lv_nd, LV_ND_E, LV_ND_B);
+ r = tc358765_write_register(dssdev, LVPHY0, val);
+
+ if (r) {
+ dev_err(&dssdev->dev, "failed to initialize LVDS-PHY\n");
+ return r;
+ }
+
+ r = tc358765_init_video_timings(dssdev);
+
+ if (r) {
+ dev_err(&dssdev->dev, "failed to initialize video path layer\n");
+ return r;
+ }
+
+ r = tc358765_read_register(dssdev, LVCFG, &val);
+ if (r < 0) {
+ dev_warn(&dssdev->dev,
+ "couldn't access LVCFG, going on with reset value\n");
+ val = 0;
+ }
+
+ val = FLD_MOD(val, board_data->pclkdiv, 9, 8);
+ val = FLD_MOD(val, board_data->pclksel, 11, 10);
+ val = FLD_MOD(val, board_data->lvdlink, 1, 1);
+ /* enable LVDS transmitter */
+ val = FLD_MOD(val, 1, 0, 0);
+ r = tc358765_write_register(dssdev, LVCFG, val);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to start LVDS transmitter\n");
+ return r;
+ }
+
+ /* Issue a soft reset to LCD Controller for a clean start */
+ r = tc358765_write_register(dssdev, SYSRST, (1 << 2));
+ /* commit video configuration */
+ r |= tc358765_write_register(dssdev, VFUEN, 0x1);
+ if (r)
+ dev_err(&dssdev->dev, "failed to latch video timings\n");
+ return r;
+}
+
+static int tc358765_power_on(struct omap_dss_device *dssdev)
+{
+ struct tc358765_data *d2d = dev_get_drvdata(&dssdev->dev);
+ int r;
+
+ /* At power on the first vsync has not been received yet */
+
+ dev_dbg(&dssdev->dev, "power_on\n");
+
+ if (dssdev->platform_enable)
+ dssdev->platform_enable(dssdev);
+
+ r = omapdss_dsi_configure_pins(dssdev, &d2d->pin_config);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to configure DSI pins\n");
+ goto err_disp_enable;
+ };
+
+ omapdss_dsi_set_size(dssdev, dssdev->panel.timings.x_res,
+ dssdev->panel.timings.y_res);
+ omapdss_dsi_set_pixel_format(dssdev, dssdev->panel.dsi_pix_fmt);
+ omapdss_dsi_set_operation_mode(dssdev, dssdev->panel.dsi_mode);
+ omapdss_dsi_set_timings(dssdev, &dssdev->panel.timings);
+ omapdss_dsi_set_videomode_timings(dssdev,
+ &dssdev->panel.dsi_vm_timings);
+
+ r = omapdss_dsi_set_clocks(dssdev, 187200000, 10000000);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to set HS and LP clocks\n");
+ goto err_disp_enable;
+ }
+
+ r = omapdss_dsi_display_enable(dssdev);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to enable DSI\n");
+ goto err_disp_enable;
+ }
+
+ /* reset tc358765 bridge */
+ tc358765_hw_reset(dssdev);
+
+ /*turn on HS clock to bring up bridge i2c slave */
+ omapdss_dsi_vc_enable_hs(dssdev, d2d->channel0, true);
+
+ /* configure D2L chip DSI-RX configuration registers */
+
+ r = tc358765_write_init_config(dssdev);
+ if (r)
+ goto err_write_init;
+
+ r = dsi_enable_video_output(dssdev, d2d->channel0);
+
+ dev_dbg(&dssdev->dev, "power_on done\n");
+
+ return r;
+
+err_write_init:
+ omapdss_dsi_display_disable(dssdev, false, false);
+err_disp_enable:
+ if (dssdev->platform_disable)
+ dssdev->platform_disable(dssdev);
+
+ return r;
+}
+
+static void tc358765_power_off(struct omap_dss_device *dssdev)
+{
+ struct tc358765_data *d2d = dev_get_drvdata(&dssdev->dev);
+
+ dsi_disable_video_output(dssdev, d2d->channel0);
+ dsi_disable_video_output(dssdev, d2d->channel1);
+
+ omapdss_dsi_display_disable(dssdev, false, false);
+
+ if (dssdev->platform_disable)
+ dssdev->platform_disable(dssdev);
+}
+
+static void tc358765_disable(struct omap_dss_device *dssdev)
+{
+ struct tc358765_data *d2d = dev_get_drvdata(&dssdev->dev);
+
+ dev_dbg(&dssdev->dev, "disable\n");
+
+ if (dssdev->state = OMAP_DSS_DISPLAY_ACTIVE) {
+ mutex_lock(&d2d->lock);
+ dsi_bus_lock(dssdev);
+
+ tc358765_power_off(dssdev);
+
+ dsi_bus_unlock(dssdev);
+ mutex_unlock(&d2d->lock);
+ }
+ dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+}
+
+static int tc358765_enable(struct omap_dss_device *dssdev)
+{
+ struct tc358765_data *d2d = dev_get_drvdata(&dssdev->dev);
+ int r = 0;
+
+ dev_dbg(&dssdev->dev, "enable\n");
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)
+ return -EINVAL;
+
+ mutex_lock(&d2d->lock);
+ dsi_bus_lock(dssdev);
+
+ r = tc358765_power_on(dssdev);
+
+ dsi_bus_unlock(dssdev);
+
+ if (r) {
+ dev_dbg(&dssdev->dev, "enable failed\n");
+ dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+ } else {
+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+ }
+
+ mutex_unlock(&d2d->lock);
+
+ return r;
+}
+
+static struct omap_dss_driver tc358765_driver = {
+ .probe = tc358765_probe,
+ .remove = tc358765_remove,
+
+ .enable = tc358765_enable,
+ .disable = tc358765_disable,
+
+ .get_resolution = tc358765_get_resolution,
+ .get_recommended_bpp = omapdss_default_get_recommended_bpp,
+
+ .get_timings = tc358765_get_timings,
+ .set_timings = tc358765_set_timings,
+ .check_timings = tc358765_check_timings,
+
+ .driver = {
+ .name = "tc358765",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int tc358765_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ tc358765_i2c = kzalloc(sizeof(*tc358765_i2c), GFP_KERNEL);
+ if (tc358765_i2c = NULL)
+ return -ENOMEM;
+
+ /* store i2c_client pointer on private data structure */
+ tc358765_i2c->client = client;
+
+ /* store private data structure pointer on i2c_client structure */
+ i2c_set_clientdata(client, tc358765_i2c);
+
+ /* init mutex */
+ mutex_init(&tc358765_i2c->xfer_lock);
+ dev_err(&client->dev, "D2L i2c initialized\n");
+
+ return 0;
+}
+
+/* driver remove function */
+static int __exit tc358765_i2c_remove(struct i2c_client *client)
+{
+ /* remove client data */
+ i2c_set_clientdata(client, NULL);
+
+ /* destroy mutex */
+ mutex_destroy(&tc358765_i2c->xfer_lock);
+
+ /* free private data memory */
+ kfree(tc358765_i2c);
+
+ return 0;
+}
+
+static const struct i2c_device_id tc358765_i2c_idtable[] = {
+ {"tc358765_i2c_driver", 0},
+ {},
+};
+
+static struct i2c_driver tc358765_i2c_driver = {
+ .probe = tc358765_i2c_probe,
+ .remove = __exit_p(tc358765_i2c_remove),
+ .id_table = tc358765_i2c_idtable,
+ .driver = {
+ .name = "d2l",
+ .owner = THIS_MODULE,
+ },
+};
+
+
+static int __init tc358765_init(void)
+{
+ int r;
+ tc358765_i2c = NULL;
+ r = i2c_add_driver(&tc358765_i2c_driver);
+ if (r < 0) {
+ printk(KERN_WARNING "d2l i2c driver registration failed\n");
+ return r;
+ }
+
+ omap_dss_register_driver(&tc358765_driver);
+ return 0;
+}
+
+static void __exit tc358765_exit(void)
+{
+ omap_dss_unregister_driver(&tc358765_driver);
+ i2c_del_driver(&tc358765_i2c_driver);
+}
+
+module_init(tc358765_init);
+module_exit(tc358765_exit);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
+MODULE_DESCRIPTION("TC358765 DSI-2-LVDS Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays/panel-tc358765.h b/drivers/video/omap2/displays/panel-tc358765.h
new file mode 100644
index 0000000..ffc105d
--- /dev/null
+++ b/drivers/video/omap2/displays/panel-tc358765.h
@@ -0,0 +1,170 @@
+/*
+ * Header for DSI-to-LVDS bridge driver
+ *
+ * Copyright (C) 2012 Texas Instruments Inc
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ * Author: Sergii Kibrik <sergiikibrik@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PANEL_TC358765_H__
+#define __PANEL_TC358765_H__
+
+/* DSI D-PHY Layer Registers */
+#define D0W_DPHYCONTTX 0x0004 /* Data Lane 0 DPHY TX */
+#define CLW_DPHYCONTRX 0x0020 /* Clock Lane DPHY RX */
+#define D0W_DPHYCONTRX 0x0024 /* Data Land 0 DPHY Rx */
+#define D1W_DPHYCONTRX 0x0028 /* Data Lane 1 DPHY Rx */
+#define D2W_DPHYCONTRX 0x002c /* Data Lane 2 DPHY Rx */
+#define D3W_DPHYCONTRX 0x0030 /* Data Lane 3 DPHY Rx */
+#define COM_DPHYCONTRX 0x0038 /* DPHY Rx Common */
+#define CLW_CNTRL 0x0040 /* Clock Lane */
+#define D0W_CNTRL 0x0044 /* Data Lane 0 */
+#define D1W_CNTRL 0x0048 /* Data Lane 1 */
+#define D2W_CNTRL 0x004c /* Data Lane 2 */
+#define D3W_CNTRL 0x0050 /* Data Lane 3 */
+#define DFTMODE_CNTRL 0x0054 /* DFT Mode */
+
+/* DSI PPI Layer Registers */
+#define PPI_STARTPPI 0x0104 /* Start control bit */
+#define PPI_BUSYPPI 0x0108 /* Busy bit */
+#define PPI_LINEINITCNT 0x0110 /* Line In initialization */
+#define PPI_LPTXTIMECNT 0x0114 /* LPTX timing signal */
+#define PPI_LANEENABLE 0x0134 /* Lane Enable */
+#define PPI_TX_RX_TA 0x013c /* BTA timing param */
+#define PPI_CLS_ATMR 0x0140 /* Analog timer fcn */
+#define PPI_D0S_ATMR 0x0144 /* Analog timer fcn Lane 0 */
+#define PPI_D1S_ATMR 0x0148 /* Analog timer fcn Lane 1 */
+#define PPI_D2S_ATMR 0x014c /* Analog timer fcn Lane 2 */
+#define PPI_D3S_ATMR 0x0150 /* Analog timer fcn Lane 3 */
+#define PPI_D0S_CLRSIPOCOUNT 0x0164 /* Assertion timer Lane 0 */
+#define PPI_D1S_CLRSIPOCOUNT 0x0168 /* Assertion timer Lane 1 */
+#define PPI_D2S_CLRSIPOCOUNT 0x016c /* Assertion timer Lane 1 */
+#define PPI_D3S_CLRSIPOCOUNT 0x0170 /* Assertion timer Lane 1 */
+#define CLS_PRE 0x0180 /* PHY IO cntr */
+#define D0S_PRE 0x0184 /* PHY IO cntr */
+#define D1S_PRE 0x0188 /* PHY IO cntr */
+#define D2S_PRE 0x018c /* PHY IO cntr */
+#define D3S_PRE 0x0190 /* PHY IO cntr */
+#define CLS_PREP 0x01a0 /* PHY IO cntr */
+#define D0S_PREP 0x01a4 /* PHY IO cntr */
+#define D1S_PREP 0x01a8 /* PHY IO cntr */
+#define D2S_PREP 0x01ac /* PHY IO cntr */
+#define D3S_PREP 0x01b0 /* PHY IO cntr */
+#define CLS_ZERO 0x01c0 /* PHY IO cntr */
+#define D0S_ZERO 0x01c4 /* PHY IO cntr */
+#define D1S_ZERO 0x01c8 /* PHY IO cntr */
+#define D2S_ZERO 0x01cc /* PHY IO cntr */
+#define D3S_ZERO 0x01d0 /* PHY IO cntr */
+#define PPI_CLRFLG 0x01e0 /* PRE cntrs */
+#define PPI_CLRSIPO 0x01e4 /* Clear SIPO */
+#define PPI_HSTimeout 0x01f0 /* HS RX timeout */
+#define PPI_HSTimeoutEnable 0x01f4 /* Enable HS Rx Timeout */
+
+/* DSI Protocol Layer Registers */
+#define DSI_STARTDSI 0x0204 /* DSI TX start bit */
+#define DSI_BUSYDSI 0x0208 /* DSI busy bit */
+#define DSI_LANEENABLE 0x0210 /* Lane enable */
+#define DSI_LANESTATUS0 0x0214 /* HS Rx mode */
+#define DSI_LANESTATUS1 0x0218 /* ULPS or STOP state */
+#define DSI_INTSTATUS 0x0220 /* Interrupt status */
+#define DSI_INTMASK 0x0224 /* Interrupt mask */
+#define DSI_INTCLR 0x0228 /* Interrupt clear */
+#define DSI_LPTXTO 0x0230 /* LP Tx Cntr */
+
+/* DSI General Registers */
+#define DSIERRCNT 0x0300 /* DSI Error Count */
+
+/* DSI Application Layer Registers */
+#define APLCTRL 0x0400 /* Application Layer Cntrl */
+#define RDPKTLN 0x0404 /* Packet length */
+
+/* Video Path Registers */
+#define VPCTRL 0x0450 /* Video Path */
+#define HTIM1 0x0454 /* Horizontal Timing */
+#define HTIM2 0x0458 /* Horizontal Timing */
+#define VTIM1 0x045c /* Vertical Timing */
+#define VTIM2 0x0460 /* Vertical Timing */
+#define VFUEN 0x0464 /* Video Frame Timing */
+
+
+/* LVDS Registers - LVDS Mux Input */
+#define LVMX0003 0x0480 /* Bit 0 to 3*/
+#define LVMX0407 0x0484 /* Bit 4 to 7 */
+#define LVMX0811 0x0488 /* Bit 8 to 11 */
+#define LVMX1215 0x048c /* Bit 12 to 15 */
+#define LVMX1619 0x0490 /* Bit 16 to 19 */
+#define LVMX2023 0x0494 /* Bit 20 to 23 */
+#define LVMX2427 0x0498 /* Bit 24 to 27 */
+
+#define LVCFG 0x049c /* LVDS Config */
+#define LVPHY0 0x04a0 /* LVDS PHY Reg 0 */
+#define LVPHY1 0x04a1 /* LVDS PHY Reg 1 */
+
+/* LVDS PHY Register 0 (LVPHY0) entries */
+#define LV_RST_B 22 /* LV PHY reset */
+#define LV_RST_E 22
+#define LV_IS_B 14 /* Charge pump current control */
+#define LV_IS_E 15 /* pin for PLL portion */
+#define LV_ND_B 0 /* Frequency Range Select */
+#define LV_ND_E 4
+
+/* System Registers */
+#define SYSSTAT 0x0500 /* System Status */
+#define SYSRST 0x0504 /* System Reset */
+
+/* GPIO Registers */
+#define GPIOC 0x0520 /* GPIO Control */
+#define GPIOO 0x0520 /* GPIO Output */
+#define GPIOI 0x0520 /* GPIO Input */
+
+/* I2C Registers */
+#define I2CTIMCTRL 0x0540
+#define I2CMADDR 0x0544
+#define WDATAQ 0x0548
+#define RDATAQ 0x054C
+
+/* Chip Revision Registers */
+#define IDREG 0x0580 /* Chip and Revision ID */
+
+/* Debug Register */
+#define DEBUG00 0x05a0 /* Debug */
+#define DEBUG01 0x05a4 /* LVDS Data */
+
+/*DSI DCS commands */
+#define DCS_READ_NUM_ERRORS 0x05
+#define DCS_READ_POWER_MODE 0x0a
+#define DCS_READ_MADCTL 0x0b
+#define DCS_READ_PIXEL_FORMAT 0x0c
+#define DCS_RDDSDR 0x0f
+#define DCS_SLEEP_IN 0x10
+#define DCS_SLEEP_OUT 0x11
+#define DCS_DISPLAY_OFF 0x28
+#define DCS_DISPLAY_ON 0x29
+#define DCS_COLUMN_ADDR 0x2a
+#define DCS_PAGE_ADDR 0x2b
+#define DCS_MEMORY_WRITE 0x2c
+#define DCS_TEAR_OFF 0x34
+#define DCS_TEAR_ON 0x35
+#define DCS_MEM_ACC_CTRL 0x36
+#define DCS_PIXEL_FORMAT 0x3a
+#define DCS_BRIGHTNESS 0x51
+#define DCS_CTRL_DISPLAY 0x53
+#define DCS_WRITE_CABC 0x55
+#define DCS_READ_CABC 0x56
+#define DCS_GET_ID1 0xda
+#define DCS_GET_ID2 0xdb
+#define DCS_GET_ID3 0xdc
+
+#endif
diff --git a/include/video/omap-panel-tc358765.h b/include/video/omap-panel-tc358765.h
new file mode 100644
index 0000000..c58e081
--- /dev/null
+++ b/include/video/omap-panel-tc358765.h
@@ -0,0 +1,53 @@
+/*
+ * Header for DSI-to-LVDS bridge driver
+ *
+ * Copyright (C) 2012 Texas Instruments Inc
+ * Author: Sergii Kibrik <sergiikibrik@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __VIDEO_TC358765_BOARD_DATA_H__
+#define __VIDEO_TC358765_BOARD_DATA_H__
+
+/**
+ * struct tc358765_board_data - represent DSI-to-LVDS bridge configuration
+ * @lp_time: Timing Generation Counter
+ * @clrsipo: CLRSIPO counter (one value for all lanes)
+ * @lv_is: charge pump control pin
+ * @lv_nd: Feed Back Divider Ratio
+ * @pclkdiv: PCLK Divide Option
+ * @pclksel: PCLK Selection: HSRCK/HbyteHSClkx2/ByteHsClk
+ * @vsdelay: VSYNC Delay
+ * @lvdlink: is single or dual link
+ * @vtgen: drive video timing signals by the on-chip Video Timing Gen module
+ * @msf: enable/disable Magic Square
+ * @evtmode: event/pulse mode of video timing information transmission
+ * @pin_config: DSI pin configuration
+*/
+struct tc358765_board_data {
+ u16 lp_time;
+ u8 clrsipo;
+ u8 lv_is;
+ u8 lv_nd;
+ u8 pclkdiv;
+ u8 pclksel;
+ u16 vsdelay;
+ bool lvdlink;
+ bool vtgen;
+ bool msf;
+ bool evtmode;
+ struct omap_dsi_pin_config pin_config;
+};
+
+#endif
--
1.7.9.5
^ permalink raw reply related
* Re: [PATCH 1/1] OMAP4: DSS: Add panel for Blaze Tablet boards
From: Andi Shyti @ 2013-02-07 14:59 UTC (permalink / raw)
To: Ruslan Bilovol
Cc: tomi.valkeinen, FlorianSchandinat, linux-fbdev, linux-kernel,
linux-omap
In-Reply-To: <1360248051-16541-2-git-send-email-ruslan.bilovol@ti.com>
Hi,
> TC358765 is DSI-to-LVDS transmitter from Toshiba, used in
> OMAP44XX Blaze Tablet and Blaze Tablet2 boards.
I had a really fast look and I have few comments
> +static int tc358765_read_register(struct omap_dss_device *dssdev,
> + u16 reg, u32 *val)
> +{
> + int ret = 0;
> + pm_runtime_get_sync(&dssdev->dev);
> + /* I2C is preferred way of reading, but fall back to DSI
> + * if I2C didn't got initialized
> + */
> + if (tc358765_i2c)
> + ret = tc358765_i2c_read(reg, val);
> + else
> + ret = tc358765_dsi_read(dssdev, reg, val);
> + pm_runtime_put_sync(&dssdev->dev);
> +
> + return ret;
> +}
> +
> +static int tc358765_write_register(struct omap_dss_device *dssdev, u16 reg,
> + u32 value)
> +{
> + struct tc358765_data *d2d = dev_get_drvdata(&dssdev->dev);
> + u8 buf[6];
> + int r;
> +
> + buf[0] = (reg >> 0) & 0xff;
> + buf[1] = (reg >> 8) & 0xff;
> + buf[2] = (value >> 0) & 0xff;
> + buf[3] = (value >> 8) & 0xff;
> + buf[4] = (value >> 16) & 0xff;
> + buf[5] = (value >> 24) & 0xff;
> +
> + r = dsi_vc_generic_write_nosync(dssdev, d2d->channel1, buf, 6);
> + if (r)
> + dev_err(&dssdev->dev, "reg write reg(%x) val(%x) failed: %d\n",
> + reg, value, r);
> + return r;
> +}
> +
> +/****************************
> +********* DEBUG *************
> +****************************/
> +#ifdef CONFIG_TC358765_DEBUG
> +static int tc358765_write_register_i2c(u16 reg, u32 val)
> +{
> + int ret = -ENODEV;
> + unsigned char buf[6];
> + struct i2c_msg msg;
> +
> + if (!tc358765_i2c) {
> + dev_err(tc358765_debug.dev, "%s: I2C not initilized\n",
> + __func__);
> + return ret;
> + }
> +
> + buf[0] = (reg >> 8) & 0xff;
> + buf[1] = (reg >> 0) & 0xff;
> + buf[2] = (val >> 0) & 0xff;
> + buf[3] = (val >> 8) & 0xff;
> + buf[4] = (val >> 16) & 0xff;
> + buf[5] = (val >> 24) & 0xff;
> + msg.addr = tc358765_i2c->client->addr;
> + msg.len = sizeof(buf);
> + msg.flags = 0;
> + msg.buf = buf;
> +
> + mutex_lock(&tc358765_i2c->xfer_lock);
> + ret = i2c_transfer(tc358765_i2c->client->adapter, &msg, 1);
> + mutex_unlock(&tc358765_i2c->xfer_lock);
> +
> + if (ret != 1)
> + return ret;
> + return 0;
> +}
What about using smbus?
> +
> + if (copy_from_user(&buf, ubuf, size))
> + return -EFAULT;
> +
> + buf[size-1] = '\0';
> + if (sscanf(buf, "%s %x", name, &value) != 2) {
> + dev_err(dev, "%s: unable to parse input\n", __func__);
> + return -1;
> + }
> +
> + if (!tc358765_i2c) {
> + dev_warn(dev,
> + "failed to write register: I2C not initialized\n");
> + return -ENODEV;
> + }
> +
> + reg_count = sizeof(tc358765_regs) / sizeof(tc358765_regs[0]);
> + for (i = 0; i < reg_count; i++) {
> + if (!strcmp(name, tc358765_regs[i].name)) {
> + if (!(tc358765_regs[i].perm & A_WO)) {
> + dev_err(dev, "%s is write-protected\n", name);
> + return -EACCES;
> + }
> +
> + error = tc358765_write_register_i2c(
> + tc358765_regs[i].reg, value);
> + if (error) {
> + dev_err(dev, "%s: failed to write %s\n",
> + __func__, name);
> + return -1;
Could avoid returning -1 instead of returning a correct errno?
> + gpio_set_value(dssdev->reset_gpio, 1);
> + udelay(200);
> + /* reset the panel */
> + gpio_set_value(dssdev->reset_gpio, 0);
> + /* assert reset */
> + udelay(200);
> + gpio_set_value(dssdev->reset_gpio, 1);
> + /* wait after releasing reset */
> + msleep(200);
I invite you to have a look at
Documentation/timers/timers-howto.txt
> + /* reset LVDS-PHY */
> + tc358765_write_register(dssdev, LVPHY0, (1 << 22));
> + mdelay(2);
You should give me a really good reason for using mdelay.
> + if (r) {
> + dev_err(&dssdev->dev, "failed to configure DSI pins\n");
> + goto err_disp_enable;
> + };
^^^
???
> +static int tc358765_i2c_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + tc358765_i2c = kzalloc(sizeof(*tc358765_i2c), GFP_KERNEL);
what about devm_kzalloc?
^ permalink raw reply
* [PATCH v2 0/3] ARM: at91/avr32/atmel_lcdfb: remove cpu_is macros
From: Johan Hovold @ 2013-02-07 15:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20130205201145.GE30595@game.jcrosoft.org>
Here's a v2 replacing the last two patches in the series (the first three are
unchanged since the first post). If preferred, I can repost the whole series
when these patches have been acked.
v2:
- use clkdev to handle the lcdc bus clock
- use -lcdfb suffix for device ids (e.g. "at91sam9g45-lcdfb")
Thanks,
Johan
Johan Hovold (3):
ARM: at91/avr32/atmel_lcdfb: add bus-clock entry
atmel_lcdfb: move lcdcon2 register access to compute_hozval
ARM: at91/avr32/atmel_lcdfb: add platform device-id table
arch/arm/mach-at91/at91sam9261.c | 2 +
arch/arm/mach-at91/at91sam9261_devices.c | 6 +-
arch/arm/mach-at91/at91sam9263.c | 1 +
arch/arm/mach-at91/at91sam9263_devices.c | 2 +-
arch/arm/mach-at91/at91sam9g45.c | 2 +
arch/arm/mach-at91/at91sam9g45_devices.c | 6 +-
arch/arm/mach-at91/at91sam9rl.c | 1 +
arch/arm/mach-at91/at91sam9rl_devices.c | 2 +-
arch/avr32/mach-at32ap/at32ap700x.c | 6 +-
drivers/video/atmel_lcdfb.c | 120 +++++++++++++++++++++++--------
include/video/atmel_lcdc.h | 4 +-
11 files changed, 117 insertions(+), 35 deletions(-)
--
1.8.1.1
^ permalink raw reply
* [PATCH v2 1/3] ARM: at91/avr32/atmel_lcdfb: add bus-clock entry
From: Johan Hovold @ 2013-02-07 15:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1360251118-12715-1-git-send-email-jhovold@gmail.com>
Add hclk entry for the atmel_lcdfb bus clock.
On at91sam9261, at91sam9g10 and at32ap the bus clock has to be enabled
as well as the peripheral clock. Add the appropriate lookup entries to
these SOCs and fake clocks to the SOCs that do not use it.
This allows us to get rid of the conditional enabling of the clocks in
the driver which relied on the cpu_is macros.
Tested on at91sam9263 and at91sam9g45, compile-tested for other
AT91-SOCs, and untested for AVR32.
Signed-off-by: Johan Hovold <jhovold@gmail.com>
---
arch/arm/mach-at91/at91sam9261.c | 1 +
arch/arm/mach-at91/at91sam9263.c | 1 +
arch/arm/mach-at91/at91sam9g45.c | 1 +
arch/arm/mach-at91/at91sam9rl.c | 1 +
arch/avr32/mach-at32ap/at32ap700x.c | 4 ++--
drivers/video/atmel_lcdfb.c | 23 ++++++++---------------
6 files changed, 14 insertions(+), 17 deletions(-)
diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c
index 2998a08..5838f12 100644
--- a/arch/arm/mach-at91/at91sam9261.c
+++ b/arch/arm/mach-at91/at91sam9261.c
@@ -169,6 +169,7 @@ static struct clk *periph_clocks[] __initdata = {
};
static struct clk_lookup periph_clocks_lookups[] = {
+ CLKDEV_CON_DEV_ID("hclk", "atmel_lcdfb.0", &hck1),
CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk),
CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.1", &spi1_clk),
CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk),
diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c
index 4618776..53d6123 100644
--- a/arch/arm/mach-at91/at91sam9263.c
+++ b/arch/arm/mach-at91/at91sam9263.c
@@ -190,6 +190,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk),
CLKDEV_CON_DEV_ID("pclk", "fff98000.ssc", &ssc0_clk),
CLKDEV_CON_DEV_ID("pclk", "fff9c000.ssc", &ssc1_clk),
+ CLKDEV_CON_DEV_ID("hclk", "atmel_lcdfb.0", &lcdc_clk),
CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.0", &mmc0_clk),
CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.1", &mmc1_clk),
CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk),
diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c
index b6f1342..1def8c9 100644
--- a/arch/arm/mach-at91/at91sam9g45.c
+++ b/arch/arm/mach-at91/at91sam9g45.c
@@ -228,6 +228,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_ID("hclk", &macb_clk),
/* One additional fake clock for ohci */
CLKDEV_CON_ID("ohci_clk", &uhphs_clk),
+ CLKDEV_CON_DEV_ID("hclk", "atmel_lcdfb.0", &lcdc_clk),
CLKDEV_CON_DEV_ID("ehci_clk", "atmel-ehci", &uhphs_clk),
CLKDEV_CON_DEV_ID("hclk", "atmel_usba_udc", &utmi_clk),
CLKDEV_CON_DEV_ID("pclk", "atmel_usba_udc", &udphs_clk),
diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c
index eb98704..4cd4fa9 100644
--- a/arch/arm/mach-at91/at91sam9rl.c
+++ b/arch/arm/mach-at91/at91sam9rl.c
@@ -179,6 +179,7 @@ static struct clk *periph_clocks[] __initdata = {
};
static struct clk_lookup periph_clocks_lookups[] = {
+ CLKDEV_CON_DEV_ID("hclk", "atmel_lcdfb.0", &lcdc_clk),
CLKDEV_CON_DEV_ID("hclk", "atmel_usba_udc", &utmi_clk),
CLKDEV_CON_DEV_ID("pclk", "atmel_usba_udc", &udphs_clk),
CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk),
diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c
index b323d8d..cd25b01 100644
--- a/arch/avr32/mach-at32ap/at32ap700x.c
+++ b/arch/avr32/mach-at32ap/at32ap700x.c
@@ -1453,7 +1453,7 @@ static struct resource atmel_lcdfb0_resource[] = {
},
};
DEFINE_DEV_DATA(atmel_lcdfb, 0);
-DEV_CLK(hck1, atmel_lcdfb0, hsb, 7);
+DEV_CLK(hclk, atmel_lcdfb0, hsb, 7);
static struct clk atmel_lcdfb0_pixclk = {
.name = "lcdc_clk",
.dev = &atmel_lcdfb0_device.dev,
@@ -2246,7 +2246,7 @@ static __initdata struct clk *init_clocks[] = {
&atmel_twi0_pclk,
&atmel_mci0_pclk,
#if defined(CONFIG_CPU_AT32AP7000) || defined(CONFIG_CPU_AT32AP7002)
- &atmel_lcdfb0_hck1,
+ &atmel_lcdfb0_hclk,
&atmel_lcdfb0_pixclk,
#endif
&ssc0_pclk,
diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
index 93910e3..86cafae 100644
--- a/drivers/video/atmel_lcdfb.c
+++ b/drivers/video/atmel_lcdfb.c
@@ -820,15 +820,13 @@ static int __init atmel_lcdfb_init_fbinfo(struct atmel_lcdfb_info *sinfo)
static void atmel_lcdfb_start_clock(struct atmel_lcdfb_info *sinfo)
{
- if (sinfo->bus_clk)
- clk_enable(sinfo->bus_clk);
+ clk_enable(sinfo->bus_clk);
clk_enable(sinfo->lcdc_clk);
}
static void atmel_lcdfb_stop_clock(struct atmel_lcdfb_info *sinfo)
{
- if (sinfo->bus_clk)
- clk_disable(sinfo->bus_clk);
+ clk_disable(sinfo->bus_clk);
clk_disable(sinfo->lcdc_clk);
}
@@ -887,13 +885,10 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
info->fix = atmel_lcdfb_fix;
/* Enable LCDC Clocks */
- if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()
- || cpu_is_at32ap7000()) {
- sinfo->bus_clk = clk_get(dev, "hck1");
- if (IS_ERR(sinfo->bus_clk)) {
- ret = PTR_ERR(sinfo->bus_clk);
- goto free_info;
- }
+ sinfo->bus_clk = clk_get(dev, "hclk");
+ if (IS_ERR(sinfo->bus_clk)) {
+ ret = PTR_ERR(sinfo->bus_clk);
+ goto free_info;
}
sinfo->lcdc_clk = clk_get(dev, "lcdc_clk");
if (IS_ERR(sinfo->lcdc_clk)) {
@@ -1054,8 +1049,7 @@ stop_clk:
atmel_lcdfb_stop_clock(sinfo);
clk_put(sinfo->lcdc_clk);
put_bus_clk:
- if (sinfo->bus_clk)
- clk_put(sinfo->bus_clk);
+ clk_put(sinfo->bus_clk);
free_info:
framebuffer_release(info);
out:
@@ -1080,8 +1074,7 @@ static int __exit atmel_lcdfb_remove(struct platform_device *pdev)
unregister_framebuffer(info);
atmel_lcdfb_stop_clock(sinfo);
clk_put(sinfo->lcdc_clk);
- if (sinfo->bus_clk)
- clk_put(sinfo->bus_clk);
+ clk_put(sinfo->bus_clk);
fb_dealloc_cmap(&info->cmap);
free_irq(sinfo->irq_base, info);
iounmap(sinfo->mmio);
--
1.8.1.1
^ permalink raw reply related
* [PATCH v2 2/3] atmel_lcdfb: move lcdcon2 register access to compute_hozval
From: Johan Hovold @ 2013-02-07 15:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1360251118-12715-1-git-send-email-jhovold@gmail.com>
Pass atmel_lcd_info structure to compute_hozval and only do the register
access on SOCs that actually use it.
This will also simplify the removal of the cpu_is macros.
Signed-off-by: Johan Hovold <jhovold@gmail.com>
---
drivers/video/atmel_lcdfb.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
index 86cafae..c707130 100644
--- a/drivers/video/atmel_lcdfb.c
+++ b/drivers/video/atmel_lcdfb.c
@@ -193,14 +193,17 @@ static struct fb_fix_screeninfo atmel_lcdfb_fix __initdata = {
.accel = FB_ACCEL_NONE,
};
-static unsigned long compute_hozval(unsigned long xres, unsigned long lcdcon2)
+static unsigned long compute_hozval(struct atmel_lcdfb_info *sinfo,
+ unsigned long xres)
{
+ unsigned long lcdcon2;
unsigned long value;
if (!(cpu_is_at91sam9261() || cpu_is_at91sam9g10()
|| cpu_is_at32ap7000()))
return xres;
+ lcdcon2 = lcdc_readl(sinfo, ATMEL_LCDC_LCDCON2);
value = xres;
if ((lcdcon2 & ATMEL_LCDC_DISTYPE) != ATMEL_LCDC_DISTYPE_TFT) {
/* STN display */
@@ -590,8 +593,7 @@ static int atmel_lcdfb_set_par(struct fb_info *info)
lcdc_writel(sinfo, ATMEL_LCDC_TIM2, value);
/* Horizontal value (aka line size) */
- hozval_linesz = compute_hozval(info->var.xres,
- lcdc_readl(sinfo, ATMEL_LCDC_LCDCON2));
+ hozval_linesz = compute_hozval(sinfo, info->var.xres);
/* Display size */
value = (hozval_linesz - 1) << ATMEL_LCDC_HOZVAL_OFFSET;
--
1.8.1.1
^ permalink raw reply related
* [PATCH v2 3/3] ARM: at91/avr32/atmel_lcdfb: add platform device-id table
From: Johan Hovold @ 2013-02-07 15:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1360251118-12715-1-git-send-email-jhovold@gmail.com>
Add platform device-id table in order to identify the controller and
determine its configuration.
The currently used configuration parameters are:
have_alt_pixclock
- SOC uses an alternate pixel-clock calculation formula (at91sam9g45
non-ES)
have_hozval
- SOC has a HOZVAL field in LCDFRMCFG which is used to determine the
linesize for STN displays (at91sam9261, at921sam9g10 and at32ap)
have_intensity_bit
- SOC uses IBGR:555 rather than BGR:565 16-bit pixel layout
(at91sam9261, at91sam9263 and at91sam9rl)
This allows us to remove all the remaining uses of cpu_is macros from
the driver.
Tested on at91sam9263 and at91sam9g45, compile-tested for other
AT91-SOCs, and untested for AVR32.
Signed-off-by: Johan Hovold <jhovold@gmail.com>
---
arch/arm/mach-at91/at91sam9261.c | 3 +-
arch/arm/mach-at91/at91sam9261_devices.c | 6 ++-
arch/arm/mach-at91/at91sam9263.c | 2 +-
arch/arm/mach-at91/at91sam9263_devices.c | 2 +-
arch/arm/mach-at91/at91sam9g45.c | 3 +-
arch/arm/mach-at91/at91sam9g45_devices.c | 6 ++-
arch/arm/mach-at91/at91sam9rl.c | 2 +-
arch/arm/mach-at91/at91sam9rl_devices.c | 2 +-
arch/avr32/mach-at32ap/at32ap700x.c | 2 +
drivers/video/atmel_lcdfb.c | 89 ++++++++++++++++++++++++++++----
include/video/atmel_lcdc.h | 4 +-
11 files changed, 102 insertions(+), 19 deletions(-)
diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c
index 5838f12..0204f4c 100644
--- a/arch/arm/mach-at91/at91sam9261.c
+++ b/arch/arm/mach-at91/at91sam9261.c
@@ -169,7 +169,8 @@ static struct clk *periph_clocks[] __initdata = {
};
static struct clk_lookup periph_clocks_lookups[] = {
- CLKDEV_CON_DEV_ID("hclk", "atmel_lcdfb.0", &hck1),
+ CLKDEV_CON_DEV_ID("hclk", "at91sam9261-lcdfb.0", &hck1),
+ CLKDEV_CON_DEV_ID("hclk", "at91sam9g10-lcdfb.0", &hck1),
CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk),
CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.1", &spi1_clk),
CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk),
diff --git a/arch/arm/mach-at91/at91sam9261_devices.c b/arch/arm/mach-at91/at91sam9261_devices.c
index 92e0f86..629ea5f 100644
--- a/arch/arm/mach-at91/at91sam9261_devices.c
+++ b/arch/arm/mach-at91/at91sam9261_devices.c
@@ -488,7 +488,6 @@ static struct resource lcdc_resources[] = {
};
static struct platform_device at91_lcdc_device = {
- .name = "atmel_lcdfb",
.id = 0,
.dev = {
.dma_mask = &lcdc_dmamask,
@@ -505,6 +504,11 @@ void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data)
return;
}
+ if (cpu_is_at91sam9g10())
+ at91_lcdc_device.name = "at91sam9g10-lcdfb";
+ else
+ at91_lcdc_device.name = "at91sam9261-lcdfb";
+
#if defined(CONFIG_FB_ATMEL_STN)
at91_set_A_periph(AT91_PIN_PB0, 0); /* LCDVSYNC */
at91_set_A_periph(AT91_PIN_PB1, 0); /* LCDHSYNC */
diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c
index 53d6123..c0cbb81 100644
--- a/arch/arm/mach-at91/at91sam9263.c
+++ b/arch/arm/mach-at91/at91sam9263.c
@@ -190,7 +190,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk),
CLKDEV_CON_DEV_ID("pclk", "fff98000.ssc", &ssc0_clk),
CLKDEV_CON_DEV_ID("pclk", "fff9c000.ssc", &ssc1_clk),
- CLKDEV_CON_DEV_ID("hclk", "atmel_lcdfb.0", &lcdc_clk),
+ CLKDEV_CON_DEV_ID("hclk", "at91sam9263-lcdfb.0", &lcdc_clk),
CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.0", &mmc0_clk),
CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.1", &mmc1_clk),
CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk),
diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c
index ed666f5..858c8aa 100644
--- a/arch/arm/mach-at91/at91sam9263_devices.c
+++ b/arch/arm/mach-at91/at91sam9263_devices.c
@@ -848,7 +848,7 @@ static struct resource lcdc_resources[] = {
};
static struct platform_device at91_lcdc_device = {
- .name = "atmel_lcdfb",
+ .name = "at91sam9263-lcdfb",
.id = 0,
.dev = {
.dma_mask = &lcdc_dmamask,
diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c
index 1def8c9..b4968aa 100644
--- a/arch/arm/mach-at91/at91sam9g45.c
+++ b/arch/arm/mach-at91/at91sam9g45.c
@@ -228,7 +228,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_ID("hclk", &macb_clk),
/* One additional fake clock for ohci */
CLKDEV_CON_ID("ohci_clk", &uhphs_clk),
- CLKDEV_CON_DEV_ID("hclk", "atmel_lcdfb.0", &lcdc_clk),
+ CLKDEV_CON_DEV_ID("hclk", "at91sam9g45-lcdfb.0", &lcdc_clk),
+ CLKDEV_CON_DEV_ID("hclk", "at91sam9g45es-lcdfb.0", &lcdc_clk),
CLKDEV_CON_DEV_ID("ehci_clk", "atmel-ehci", &uhphs_clk),
CLKDEV_CON_DEV_ID("hclk", "atmel_usba_udc", &utmi_clk),
CLKDEV_CON_DEV_ID("pclk", "atmel_usba_udc", &udphs_clk),
diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
index 827c9f2..fe626d4 100644
--- a/arch/arm/mach-at91/at91sam9g45_devices.c
+++ b/arch/arm/mach-at91/at91sam9g45_devices.c
@@ -981,7 +981,6 @@ static struct resource lcdc_resources[] = {
};
static struct platform_device at91_lcdc_device = {
- .name = "atmel_lcdfb",
.id = 0,
.dev = {
.dma_mask = &lcdc_dmamask,
@@ -997,6 +996,11 @@ void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data)
if (!data)
return;
+ if (cpu_is_at91sam9g45es())
+ at91_lcdc_device.name = "at91sam9g45es-lcdfb";
+ else
+ at91_lcdc_device.name = "at91sam9g45-lcdfb";
+
at91_set_A_periph(AT91_PIN_PE0, 0); /* LCDDPWR */
at91_set_A_periph(AT91_PIN_PE2, 0); /* LCDCC */
diff --git a/arch/arm/mach-at91/at91sam9rl.c b/arch/arm/mach-at91/at91sam9rl.c
index 4cd4fa9..3de3e04 100644
--- a/arch/arm/mach-at91/at91sam9rl.c
+++ b/arch/arm/mach-at91/at91sam9rl.c
@@ -179,7 +179,7 @@ static struct clk *periph_clocks[] __initdata = {
};
static struct clk_lookup periph_clocks_lookups[] = {
- CLKDEV_CON_DEV_ID("hclk", "atmel_lcdfb.0", &lcdc_clk),
+ CLKDEV_CON_DEV_ID("hclk", "at91sam9rl-lcdfb.0", &lcdc_clk),
CLKDEV_CON_DEV_ID("hclk", "atmel_usba_udc", &utmi_clk),
CLKDEV_CON_DEV_ID("pclk", "atmel_usba_udc", &udphs_clk),
CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk),
diff --git a/arch/arm/mach-at91/at91sam9rl_devices.c b/arch/arm/mach-at91/at91sam9rl_devices.c
index ddf223f..352468f 100644
--- a/arch/arm/mach-at91/at91sam9rl_devices.c
+++ b/arch/arm/mach-at91/at91sam9rl_devices.c
@@ -514,7 +514,7 @@ static struct resource lcdc_resources[] = {
};
static struct platform_device at91_lcdc_device = {
- .name = "atmel_lcdfb",
+ .name = "at91sam9rl-lcdfb",
.id = 0,
.dev = {
.dma_mask = &lcdc_dmamask,
diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c
index cd25b01..7c2f668 100644
--- a/arch/avr32/mach-at32ap/at32ap700x.c
+++ b/arch/avr32/mach-at32ap/at32ap700x.c
@@ -1530,6 +1530,8 @@ at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data,
memcpy(info, data, sizeof(struct atmel_lcdfb_info));
info->default_monspecs = monspecs;
+ pdev->name = "at32ap-lcdfb";
+
platform_device_register(pdev);
return pdev;
diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
index c707130..b68bfe6 100644
--- a/drivers/video/atmel_lcdfb.c
+++ b/drivers/video/atmel_lcdfb.c
@@ -34,6 +34,77 @@
#define ATMEL_LCDC_DMA_BURST_LEN 8 /* words */
#define ATMEL_LCDC_FIFO_SIZE 512 /* words */
+struct atmel_lcdfb_config {
+ bool have_alt_pixclock;
+ bool have_hozval;
+ bool have_intensity_bit;
+};
+
+static struct atmel_lcdfb_config at91sam9261_config = {
+ .have_hozval = true,
+ .have_intensity_bit = true,
+};
+
+static struct atmel_lcdfb_config at91sam9263_config = {
+ .have_intensity_bit = true,
+};
+
+static struct atmel_lcdfb_config at91sam9g10_config = {
+ .have_hozval = true,
+};
+
+static struct atmel_lcdfb_config at91sam9g45_config = {
+ .have_alt_pixclock = true,
+};
+
+static struct atmel_lcdfb_config at91sam9g45es_config = {
+};
+
+static struct atmel_lcdfb_config at91sam9rl_config = {
+ .have_intensity_bit = true,
+};
+
+static struct atmel_lcdfb_config at32ap_config = {
+ .have_hozval = true,
+};
+
+static const struct platform_device_id atmel_lcdfb_devtypes[] = {
+ {
+ .name = "at91sam9261-lcdfb",
+ .driver_data = (unsigned long)&at91sam9261_config,
+ }, {
+ .name = "at91sam9263-lcdfb",
+ .driver_data = (unsigned long)&at91sam9263_config,
+ }, {
+ .name = "at91sam9g10-lcdfb",
+ .driver_data = (unsigned long)&at91sam9g10_config,
+ }, {
+ .name = "at91sam9g45-lcdfb",
+ .driver_data = (unsigned long)&at91sam9g45_config,
+ }, {
+ .name = "at91sam9g45es-lcdfb",
+ .driver_data = (unsigned long)&at91sam9g45es_config,
+ }, {
+ .name = "at91sam9rl-lcdfb",
+ .driver_data = (unsigned long)&at91sam9rl_config,
+ }, {
+ .name = "at32ap-lcdfb",
+ .driver_data = (unsigned long)&at32ap_config,
+ }, {
+ /* terminator */
+ }
+};
+
+static struct atmel_lcdfb_config *
+atmel_lcdfb_get_config(struct platform_device *pdev)
+{
+ unsigned long data;
+
+ data = platform_get_device_id(pdev)->driver_data;
+
+ return (struct atmel_lcdfb_config *)data;
+}
+
#if defined(CONFIG_ARCH_AT91)
#define ATMEL_LCDFB_FBINFO_DEFAULT (FBINFO_DEFAULT \
| FBINFO_PARTIAL_PAN_OK \
@@ -199,8 +270,7 @@ static unsigned long compute_hozval(struct atmel_lcdfb_info *sinfo,
unsigned long lcdcon2;
unsigned long value;
- if (!(cpu_is_at91sam9261() || cpu_is_at91sam9g10()
- || cpu_is_at32ap7000()))
+ if (!sinfo->config->have_hozval)
return xres;
lcdcon2 = lcdc_readl(sinfo, ATMEL_LCDC_LCDCON2);
@@ -426,7 +496,7 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var,
break;
case 16:
/* Older SOCs use IBGR:555 rather than BGR:565. */
- if (sinfo->have_intensity_bit)
+ if (sinfo->config->have_intensity_bit)
var->green.length = 5;
else
var->green.length = 6;
@@ -534,7 +604,7 @@ static int atmel_lcdfb_set_par(struct fb_info *info)
/* Now, the LCDC core... */
/* Set pixel clock */
- if (cpu_is_at91sam9g45() && !cpu_is_at91sam9g45es())
+ if (sinfo->config->have_alt_pixclock)
pix_factor = 1;
clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000;
@@ -685,7 +755,7 @@ static int atmel_lcdfb_setcolreg(unsigned int regno, unsigned int red,
case FB_VISUAL_PSEUDOCOLOR:
if (regno < 256) {
- if (sinfo->have_intensity_bit) {
+ if (sinfo->config->have_intensity_bit) {
/* old style I+BGR:555 */
val = ((red >> 11) & 0x001f);
val |= ((green >> 6) & 0x03e0);
@@ -873,10 +943,9 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
}
sinfo->info = info;
sinfo->pdev = pdev;
- if (cpu_is_at91sam9261() || cpu_is_at91sam9263() ||
- cpu_is_at91sam9rl()) {
- sinfo->have_intensity_bit = true;
- }
+ sinfo->config = atmel_lcdfb_get_config(pdev);
+ if (!sinfo->config)
+ goto free_info;
strcpy(info->fix.id, sinfo->pdev->name);
info->flags = ATMEL_LCDFB_FBINFO_DEFAULT;
@@ -1145,7 +1214,7 @@ static struct platform_driver atmel_lcdfb_driver = {
.remove = __exit_p(atmel_lcdfb_remove),
.suspend = atmel_lcdfb_suspend,
.resume = atmel_lcdfb_resume,
-
+ .id_table = atmel_lcdfb_devtypes,
.driver = {
.name = "atmel_lcdfb",
.owner = THIS_MODULE,
diff --git a/include/video/atmel_lcdc.h b/include/video/atmel_lcdc.h
index 8deb226..0f5a2fc 100644
--- a/include/video/atmel_lcdc.h
+++ b/include/video/atmel_lcdc.h
@@ -31,6 +31,7 @@
#define ATMEL_LCDC_WIRING_BGR 0
#define ATMEL_LCDC_WIRING_RGB 1
+struct atmel_lcdfb_config;
/* LCD Controller info data structure, stored in device platform_data */
struct atmel_lcdfb_info {
@@ -61,7 +62,8 @@ struct atmel_lcdfb_info {
void (*atmel_lcdfb_power_control)(int on);
struct fb_monspecs *default_monspecs;
u32 pseudo_palette[16];
- bool have_intensity_bit;
+
+ struct atmel_lcdfb_config *config;
};
#define ATMEL_LCDC_DMABADDR1 0x00
--
1.8.1.1
^ permalink raw reply related
* Re: Unmerged patches for 3.9
From: Andrew Morton @ 2013-02-07 21:41 UTC (permalink / raw)
To: linux-fbdev
In-Reply-To: <CAK9yfHwOanv4n3gE-E1gopyTaEjL0km1qMmGLGM2h2WLSMppYA@mail.gmail.com>
On Thu, 7 Feb 2013 12:34:16 +0530
Sachin Kamat <sachin.kamat@linaro.org> wrote:
> Hi Florian,
>
> I have the following 'Acked' patches missing in your tree.
>
> https://patchwork.kernel.org/patch/1864681/
> https://patchwork.kernel.org/patch/1923041/
> https://patchwork.kernel.org/patch/1926501/
>
> Could you please pick them up in your tree as they have been pending
> almost since a couple of months now.
Those aren't terribly urgent patches so I'll skip them for now.
It appears that Florian is still offline. If anyone has urgent fbdev
patches (ie: bugfixes) then please send them to me ASAP and I'll start
working them towards 3.8 and perhaps -stable.
Thanks.
^ permalink raw reply
* Re: [PATCH 2/3 v2] fb: udlfb: fix hang at disconnect
From: Dave Airlie @ 2013-02-08 4:07 UTC (permalink / raw)
To: Alexander Holler
Cc: Greg KH, Andrew Morton, linux-kernel, linux-fbdev,
Florian Tobias Schandinat, Bernie Thompson, Steve Glendinning,
stable
In-Reply-To: <5111431D.8070906@ahsoftware.de>
>
> But I've just switched to udl (instead of udlfb) and will see if I can fix
> the bugs there to make it usable as a console. udl is a rewrite of udlfb
> with some additional features (e.g. drm), so hopefully fixing the remaining
> problems there will require less work.
I may have fixed the major udl problem with being a console, patch was
posted to dri-devel yesterday, and I've pushed it into -next.
Dave.
^ permalink raw reply
* [PATCH RESEND v2 1/2] drivers/video: fsl-diu-fb: fix pixel formats for 24 and 16 bpp
From: Anatolij Gustschin @ 2013-02-08 7:35 UTC (permalink / raw)
To: linux-fbdev
Framebuffer colors for 24 and 16 bpp are currently wrong. The order
of the color component arguments in the MAKE_PF() is not natural
and causes some confusion. The generated pixel format values for 24
and 16 bpp depths do not much the values in the comments.
Fix the macro arguments to be in the natural RGB order and adjust
the arguments for all depths to generate correct pixel format values
(equal to the values mentioned in the comments).
Signed-off-by: Anatolij Gustschin <agust@denx.de>
Cc: Timur Tabi <timur@tabi.org>
Acked-by: Timur Tabi <timur@freescale.com>
---
Changes in v2:
- none, only added Timur's Acked-by
This patch is not a very important fix but it would be good to
have it in v3.8
drivers/video/fsl-diu-fb.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c
index d3fc92e..1ca32c5 100644
--- a/drivers/video/fsl-diu-fb.c
+++ b/drivers/video/fsl-diu-fb.c
@@ -944,7 +944,7 @@ static u32 fsl_diu_get_pixel_format(unsigned int bits_per_pixel)
#define PF_COMP_0_MASK 0x0000000F
#define PF_COMP_0_SHIFT 0
-#define MAKE_PF(alpha, red, blue, green, size, c0, c1, c2, c3) \
+#define MAKE_PF(alpha, red, green, blue, size, c0, c1, c2, c3) \
cpu_to_le32(PF_BYTE_F | (alpha << PF_ALPHA_C_SHIFT) | \
(blue << PF_BLUE_C_SHIFT) | (green << PF_GREEN_C_SHIFT) | \
(red << PF_RED_C_SHIFT) | (c3 << PF_COMP_3_SHIFT) | \
@@ -954,10 +954,10 @@ static u32 fsl_diu_get_pixel_format(unsigned int bits_per_pixel)
switch (bits_per_pixel) {
case 32:
/* 0x88883316 */
- return MAKE_PF(3, 2, 0, 1, 3, 8, 8, 8, 8);
+ return MAKE_PF(3, 2, 1, 0, 3, 8, 8, 8, 8);
case 24:
/* 0x88082219 */
- return MAKE_PF(4, 0, 1, 2, 2, 0, 8, 8, 8);
+ return MAKE_PF(4, 0, 1, 2, 2, 8, 8, 8, 0);
case 16:
/* 0x65053118 */
return MAKE_PF(4, 2, 1, 0, 1, 5, 6, 5, 0);
--
1.7.5.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