* [RFC PATCH 0/4] exynos-drm-hdmi driver to CDF complaint display driver
@ 2013-01-13 12:52 Rahul Sharma
2013-01-13 12:52 ` [RFC PATCH 1/4] video: display: add event handling, set mode and hdmi ops to cdf core Rahul Sharma
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: Rahul Sharma @ 2013-01-13 12:52 UTC (permalink / raw)
To: linux-media, dri-devel
Cc: tomi.valkeinen, laurent.pinchart, inki.dae, r.sh.open, joshi
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.
[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-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.
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.
Tested for:
1) hdmi mode negotiation.
2) Hpd Scenarios for hdmi and hdmi-audio.
3) hdmi mode switching.
4) hdmi-audio static audio configuration enable/ disable.
Pending:
1) Registering hdmi-audio sound card.
2) Moving exynos_hdmi to driver/video/display
Rahul Sharma (4):
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
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 | 446 ++++++++++++++++---------------
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 | 4 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/exynos_hdmi_audio.c | 307 +++++++++++++++++++++
13 files changed, 1253 insertions(+), 217 deletions(-)
create mode 100644 drivers/gpu/drm/exynos/exynos_hdmi_cdf.c
mode change 100755 => 100644 drivers/video/display/display-core.c
mode change 100755 => 100644 include/video/display.h
create mode 100644 include/video/exynos_hdmi.h
create mode 100644 sound/soc/codecs/exynos_hdmi_audio.c
--
1.8.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [RFC PATCH 1/4] video: display: add event handling, set mode and hdmi ops to cdf core
2013-01-13 12:52 [RFC PATCH 0/4] exynos-drm-hdmi driver to CDF complaint display driver Rahul Sharma
@ 2013-01-13 12:52 ` Rahul Sharma
2013-01-13 12:52 ` [RFC PATCH 2/4] drm/edid: temporarily exposing generic edid-read interface from drm Rahul Sharma
` (2 subsequent siblings)
3 siblings, 0 replies; 7+ messages in thread
From: Rahul Sharma @ 2013-01-13 12:52 UTC (permalink / raw)
To: linux-media, dri-devel
Cc: tomi.valkeinen, laurent.pinchart, inki.dae, r.sh.open, joshi
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.
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(-)
mode change 100755 => 100644 drivers/video/display/display-core.c
mode change 100755 => 100644 include/video/display.h
diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c
old mode 100755
new mode 100644
index 55a7399..12fb882
--- 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
old mode 100755
new mode 100644
index 817f4ae..eae373f
--- 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 [flat|nested] 7+ messages in thread
* [RFC PATCH 2/4] drm/edid: temporarily exposing generic edid-read interface from drm
2013-01-13 12:52 [RFC PATCH 0/4] exynos-drm-hdmi driver to CDF complaint display driver Rahul Sharma
2013-01-13 12:52 ` [RFC PATCH 1/4] video: display: add event handling, set mode and hdmi ops to cdf core Rahul Sharma
@ 2013-01-13 12:52 ` Rahul Sharma
2013-01-13 12:52 ` [RFC PATCH 3/4] drm/exynos: moved drm hdmi driver to cdf framework Rahul Sharma
2013-01-13 12:52 ` [RFC PATCH 4/4] alsa/soc: add hdmi audio codec based on cdf Rahul Sharma
3 siblings, 0 replies; 7+ messages in thread
From: Rahul Sharma @ 2013-01-13 12:52 UTC (permalink / raw)
To: linux-media, dri-devel
Cc: tomi.valkeinen, laurent.pinchart, inki.dae, 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 [flat|nested] 7+ messages in thread
* [RFC PATCH 3/4] drm/exynos: moved drm hdmi driver to cdf framework
2013-01-13 12:52 [RFC PATCH 0/4] exynos-drm-hdmi driver to CDF complaint display driver Rahul Sharma
2013-01-13 12:52 ` [RFC PATCH 1/4] video: display: add event handling, set mode and hdmi ops to cdf core Rahul Sharma
2013-01-13 12:52 ` [RFC PATCH 2/4] drm/edid: temporarily exposing generic edid-read interface from drm Rahul Sharma
@ 2013-01-13 12:52 ` Rahul Sharma
2013-01-13 12:52 ` [RFC PATCH 4/4] alsa/soc: add hdmi audio codec based on cdf Rahul Sharma
3 siblings, 0 replies; 7+ messages in thread
From: Rahul Sharma @ 2013-01-13 12:52 UTC (permalink / raw)
To: linux-media, dri-devel
Cc: tomi.valkeinen, laurent.pinchart, inki.dae, 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 | 446 ++++++++++++++++---------------
drivers/gpu/drm/exynos/exynos_hdmi_cdf.c | 370 +++++++++++++++++++++++++
include/video/exynos_hdmi.h | 25 ++
7 files changed, 659 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 56e9a412..423e09c 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 f5eb986..ce22e69 100644
--- 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;
}
-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,
+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;
+}
+
+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,21 @@ 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 __devinit 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 +2229,6 @@ static int __devinit 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 +2238,7 @@ static int __devinit 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 +2316,7 @@ static int __devinit 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 +2324,31 @@ static int __devinit 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 +2359,7 @@ err_ddc:
static int __devexit 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 +2380,7 @@ static int __devexit 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 +2388,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 +2402,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 +2425,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 +2435,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..f61cf7e
--- /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 __devinit 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 __devexit hdmi_cdf_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+struct platform_driver hdmi_cdf_driver = {
+ .probe = hdmi_cdf_probe,
+ .remove = __devexit_p(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 [flat|nested] 7+ messages in thread
* [RFC PATCH 4/4] alsa/soc: add hdmi audio codec based on cdf
2013-01-13 12:52 [RFC PATCH 0/4] exynos-drm-hdmi driver to CDF complaint display driver Rahul Sharma
` (2 preceding siblings ...)
2013-01-13 12:52 ` [RFC PATCH 3/4] drm/exynos: moved drm hdmi driver to cdf framework Rahul Sharma
@ 2013-01-13 12:52 ` Rahul Sharma
2013-01-14 5:43 ` Sachin Kamat
3 siblings, 1 reply; 7+ messages in thread
From: Rahul Sharma @ 2013-01-13 12:52 UTC (permalink / raw)
To: linux-media, dri-devel
Cc: tomi.valkeinen, laurent.pinchart, inki.dae, r.sh.open, joshi
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 | 4 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/exynos_hdmi_audio.c | 307 +++++++++++++++++++++++++++++++++++
3 files changed, 313 insertions(+)
create mode 100644 sound/soc/codecs/exynos_hdmi_audio.c
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index b92759a..93f3f6b 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -496,3 +496,7 @@ config SND_SOC_ML26124
config SND_SOC_TPA6130A2
tristate
+
+config SND_SOC_EXYNOS_HDMI_AUDIO
+ tristate
+ default y
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 9bd4d95..bfe93e6 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -112,6 +112,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
@@ -230,6 +231,7 @@ obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
+obj-$(CONFIG_SND_SOC_EXYNOS_HDMI_AUDIO) += 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..50e8564
--- /dev/null
+++ b/sound/soc/codecs/exynos_hdmi_audio.c
@@ -0,0 +1,307 @@
+/*
+ * ALSA SoC codec driver for HDMI audio on Samsung Exynos processors.
+ * Copyright (C) 2012 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 <sound/soc.h>
+#include <video/display.h>
+#include <video/exynos_hdmi.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#undef dev_info
+
+#define dev_info(dev, format, arg...) \
+ dev_printk(KERN_CRIT, dev, format, ##arg)
+
+static struct snd_soc_codec_driver hdmi_codec;
+
+/* platform device pointer for eynos hdmi audio device. */
+static struct platform_device *exynos_hdmi_audio_pdev;
+
+struct hdmi_audio_context {
+ struct platform_device *pdev;
+ atomic_t plugged;
+ struct display_entity_audio_params audio_params;
+ struct display_entity *entity;
+ struct display_entity_notifier notf;
+ struct display_event_subscriber subscriber;
+};
+
+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_info(codec->dev, "[%d] %s\n", __LINE__, __func__);
+
+ 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;
+ }
+
+ /* checking here to cache audioparms for hpd plug handling */
+ if (!atomic_read(&ctx->plugged))
+ return -EINVAL;
+
+ ret =
+ display_entity_hdmi_init_audio(ctx->entity, &ctx->audio_params);
+ 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_info(codec->dev, "[%d] %s\n", __LINE__, __func__);
+
+ /* checking here to cache audioparms for hpd plug handling */
+ if (!atomic_read(&ctx->plugged))
+ return -EINVAL;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ 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",
+ .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_info(&ctx->pdev->dev, "[%d][%s] hpd(%d)\n", __LINE__,
+ __func__, value);
+ atomic_set(&ctx->plugged, !!value);
+ }
+}
+
+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_info(&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 __devinit int hdmi_codec_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct hdmi_audio_context *ctx;
+ struct device_node *dev_node;
+ struct platform_device *disp_pdev;
+
+ dev_info(&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->plugged, 0);
+ platform_set_drvdata(pdev, ctx);
+
+ 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__);
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static __devexit int hdmi_codec_remove(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, " %s:%s:%d", __FILE__, __func__, __LINE__);
+ mdelay(1000);
+
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver hdmi_codec_driver = {
+ .driver = {
+ .name = "exynos-hdmi-audio-codec",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = hdmi_codec_probe,
+ .remove = __devexit_p(hdmi_codec_remove),
+};
+
+static int __init hdmi_codec_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&hdmi_codec_driver);
+ if (ret < 0)
+ return -EINVAL;
+
+ exynos_hdmi_audio_pdev = platform_device_register_simple
+ ("exynos-hdmi-audio-codec", -1, NULL, 0);
+ if (IS_ERR_OR_NULL(exynos_hdmi_audio_pdev)) {
+ ret = PTR_ERR(exynos_hdmi_audio_pdev);
+ platform_driver_unregister(&hdmi_codec_driver);
+ return ret;
+ }
+
+ return 0;
+}
+static void __exit hdmi_codec_exit(void)
+{
+ platform_driver_unregister(&hdmi_codec_driver);
+ platform_device_unregister(exynos_hdmi_audio_pdev);
+}
+
+module_init(hdmi_codec_init);
+module_exit(hdmi_codec_exit);
+
+MODULE_AUTHOR("Rahul Sharma <rahul.sharma@samsung.com>");
+MODULE_DESCRIPTION("ASoC EXYNOS HDMI codec driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
--
1.8.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [RFC PATCH 4/4] alsa/soc: add hdmi audio codec based on cdf
2013-01-13 12:52 ` [RFC PATCH 4/4] alsa/soc: add hdmi audio codec based on cdf Rahul Sharma
@ 2013-01-14 5:43 ` Sachin Kamat
2013-01-14 6:04 ` Rahul Sharma
0 siblings, 1 reply; 7+ messages in thread
From: Sachin Kamat @ 2013-01-14 5:43 UTC (permalink / raw)
To: Rahul Sharma
Cc: linux-media, dri-devel, tomi.valkeinen, laurent.pinchart,
inki.dae, r.sh.open, joshi, alsa-devel, Mark Brown
+CC: ALSA mailing list, Mark Brown
On 13 January 2013 18:22, Rahul Sharma <rahul.sharma@samsung.com> wrote:
> 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 | 4 +
> sound/soc/codecs/Makefile | 2 +
> sound/soc/codecs/exynos_hdmi_audio.c | 307 +++++++++++++++++++++++++++++++++++
> 3 files changed, 313 insertions(+)
> create mode 100644 sound/soc/codecs/exynos_hdmi_audio.c
>
> diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
> index b92759a..93f3f6b 100644
> --- a/sound/soc/codecs/Kconfig
> +++ b/sound/soc/codecs/Kconfig
> @@ -496,3 +496,7 @@ config SND_SOC_ML26124
>
> config SND_SOC_TPA6130A2
> tristate
> +
> +config SND_SOC_EXYNOS_HDMI_AUDIO
> + tristate
> + default y
Do you want to enable this by default? Shouldn't this be depending on
HDMI support?
> diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
> index 9bd4d95..bfe93e6 100644
> --- a/sound/soc/codecs/Makefile
> +++ b/sound/soc/codecs/Makefile
> @@ -112,6 +112,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
> @@ -230,6 +231,7 @@ obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
> obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
> obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
> obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
> +obj-$(CONFIG_SND_SOC_EXYNOS_HDMI_AUDIO) += 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..50e8564
> --- /dev/null
> +++ b/sound/soc/codecs/exynos_hdmi_audio.c
> @@ -0,0 +1,307 @@
> +/*
> + * ALSA SoC codec driver for HDMI audio on Samsung Exynos processors.
> + * Copyright (C) 2012 Samsung corp.
Copyright (c) 2012 (-13?) Samsung Electronics Co., Ltd.
> + * 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 <sound/soc.h>
> +#include <video/display.h>
> +#include <video/exynos_hdmi.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +
> +#undef dev_info
> +
> +#define dev_info(dev, format, arg...) \
> + dev_printk(KERN_CRIT, dev, format, ##arg)
You may directly use dev_crit instead of dev_printk.
> +
> +static struct snd_soc_codec_driver hdmi_codec;
> +
> +/* platform device pointer for eynos hdmi audio device. */
> +static struct platform_device *exynos_hdmi_audio_pdev;
> +
> +struct hdmi_audio_context {
> + struct platform_device *pdev;
> + atomic_t plugged;
> + struct display_entity_audio_params audio_params;
> + struct display_entity *entity;
> + struct display_entity_notifier notf;
> + struct display_event_subscriber subscriber;
> +};
> +
> +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_info(codec->dev, "[%d] %s\n", __LINE__, __func__);
How about making this a debug message as it does not convey anything useful?
> +
> + 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;
> + }
> +
> + /* checking here to cache audioparms for hpd plug handling */
> + if (!atomic_read(&ctx->plugged))
> + return -EINVAL;
> +
> + ret =
> + display_entity_hdmi_init_audio(ctx->entity, &ctx->audio_params);
> + 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_info(codec->dev, "[%d] %s\n", __LINE__, __func__);
ditto
> +
> + /* checking here to cache audioparms for hpd plug handling */
> + if (!atomic_read(&ctx->plugged))
> + return -EINVAL;
> +
> + switch (cmd) {
> + case SNDRV_PCM_TRIGGER_START:
> + 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",
> + .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_info(&ctx->pdev->dev, "[%d][%s] hpd(%d)\n", __LINE__,
> + __func__, value);
> + atomic_set(&ctx->plugged, !!value);
> + }
> +}
> +
> +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_info(&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 __devinit int hdmi_codec_probe(struct platform_device *pdev)
__devinit is not necessary.
+{
> + int ret;
> + struct hdmi_audio_context *ctx;
> + struct device_node *dev_node;
> + struct platform_device *disp_pdev;
> +
> + dev_info(&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->plugged, 0);
> + platform_set_drvdata(pdev, ctx);
> +
> + 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__);
> + return -EINVAL;
> + }
> + return ret;
> +}
> +
> +static __devexit int hdmi_codec_remove(struct platform_device *pdev)
__devexit is not necessary.
> +{
> + dev_info(&pdev->dev, " %s:%s:%d", __FILE__, __func__, __LINE__);
> + mdelay(1000);
> +
> + snd_soc_unregister_codec(&pdev->dev);
> + return 0;
> +}
> +
> +static struct platform_driver hdmi_codec_driver = {
> + .driver = {
> + .name = "exynos-hdmi-audio-codec",
> + .owner = THIS_MODULE,
> + },
> +
> + .probe = hdmi_codec_probe,
> + .remove = __devexit_p(hdmi_codec_remove),
> +};
> +
> +static int __init hdmi_codec_init(void)
> +{
> + int ret;
> +
> + ret = platform_driver_register(&hdmi_codec_driver);
> + if (ret < 0)
> + return -EINVAL;
> +
> + exynos_hdmi_audio_pdev = platform_device_register_simple
> + ("exynos-hdmi-audio-codec", -1, NULL, 0);
> + if (IS_ERR_OR_NULL(exynos_hdmi_audio_pdev)) {
> + ret = PTR_ERR(exynos_hdmi_audio_pdev);
> + platform_driver_unregister(&hdmi_codec_driver);
> + return ret;
> + }
> +
> + return 0;
> +}
> +static void __exit hdmi_codec_exit(void)
> +{
> + platform_driver_unregister(&hdmi_codec_driver);
> + platform_device_unregister(exynos_hdmi_audio_pdev);
> +}
> +
> +module_init(hdmi_codec_init);
> +module_exit(hdmi_codec_exit);
> +
> +MODULE_AUTHOR("Rahul Sharma <rahul.sharma@samsung.com>");
> +MODULE_DESCRIPTION("ASoC EXYNOS HDMI codec driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:" DRV_NAME);
> --
> 1.8.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
With warm regards,
Sachin
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC PATCH 4/4] alsa/soc: add hdmi audio codec based on cdf
2013-01-14 5:43 ` Sachin Kamat
@ 2013-01-14 6:04 ` Rahul Sharma
0 siblings, 0 replies; 7+ messages in thread
From: Rahul Sharma @ 2013-01-14 6:04 UTC (permalink / raw)
To: Sachin Kamat
Cc: Rahul Sharma, linux-media, dri-devel, tomi.valkeinen,
laurent.pinchart, inki.dae, joshi, alsa-devel, Mark Brown
Thanks Sachin,
On Mon, Jan 14, 2013 at 11:13 AM, Sachin Kamat <sachin.kamat@linaro.org> wrote:
> +CC: ALSA mailing list, Mark Brown
>
> On 13 January 2013 18:22, Rahul Sharma <rahul.sharma@samsung.com> wrote:
>> 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 | 4 +
>> sound/soc/codecs/Makefile | 2 +
>> sound/soc/codecs/exynos_hdmi_audio.c | 307 +++++++++++++++++++++++++++++++++++
>> 3 files changed, 313 insertions(+)
>> create mode 100644 sound/soc/codecs/exynos_hdmi_audio.c
>>
>> diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
>> index b92759a..93f3f6b 100644
>> --- a/sound/soc/codecs/Kconfig
>> +++ b/sound/soc/codecs/Kconfig
>> @@ -496,3 +496,7 @@ config SND_SOC_ML26124
>>
>> config SND_SOC_TPA6130A2
>> tristate
>> +
>> +config SND_SOC_EXYNOS_HDMI_AUDIO
>> + tristate
>> + default y
>
> Do you want to enable this by default? Shouldn't this be depending on
> HDMI support?
>
Yes, it should depend on Exynos HDMI support. I will add it in v2.
>> diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
>> index 9bd4d95..bfe93e6 100644
>> --- a/sound/soc/codecs/Makefile
>> +++ b/sound/soc/codecs/Makefile
>> @@ -112,6 +112,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
>> @@ -230,6 +231,7 @@ obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
>> obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
>> obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
>> obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
>> +obj-$(CONFIG_SND_SOC_EXYNOS_HDMI_AUDIO) += 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..50e8564
>> --- /dev/null
>> +++ b/sound/soc/codecs/exynos_hdmi_audio.c
>> @@ -0,0 +1,307 @@
>> +/*
>> + * ALSA SoC codec driver for HDMI audio on Samsung Exynos processors.
>> + * Copyright (C) 2012 Samsung corp.
>
> Copyright (c) 2012 (-13?) Samsung Electronics Co., Ltd.
>
I will correct this.
>
>> + * 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 <sound/soc.h>
>> +#include <video/display.h>
>> +#include <video/exynos_hdmi.h>
>> +#include <sound/pcm.h>
>> +#include <sound/pcm_params.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/platform_device.h>
>> +
>> +#undef dev_info
>> +
>> +#define dev_info(dev, format, arg...) \
>> + dev_printk(KERN_CRIT, dev, format, ##arg)
>
> You may directly use dev_crit instead of dev_printk.
>
I will clean this in v2.
>> +
>> +static struct snd_soc_codec_driver hdmi_codec;
>> +
>> +/* platform device pointer for eynos hdmi audio device. */
>> +static struct platform_device *exynos_hdmi_audio_pdev;
>> +
>> +struct hdmi_audio_context {
>> + struct platform_device *pdev;
>> + atomic_t plugged;
>> + struct display_entity_audio_params audio_params;
>> + struct display_entity *entity;
>> + struct display_entity_notifier notf;
>> + struct display_event_subscriber subscriber;
>> +};
>> +
>> +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_info(codec->dev, "[%d] %s\n", __LINE__, __func__);
>
> How about making this a debug message as it does not convey anything useful?
ok.
>
>> +
>> + 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;
>> + }
>> +
>> + /* checking here to cache audioparms for hpd plug handling */
>> + if (!atomic_read(&ctx->plugged))
>> + return -EINVAL;
>> +
>> + ret =
>> + display_entity_hdmi_init_audio(ctx->entity, &ctx->audio_params);
>> + 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_info(codec->dev, "[%d] %s\n", __LINE__, __func__);
>
> ditto
>
>> +
>> + /* checking here to cache audioparms for hpd plug handling */
>> + if (!atomic_read(&ctx->plugged))
>> + return -EINVAL;
>> +
>> + switch (cmd) {
>> + case SNDRV_PCM_TRIGGER_START:
>> + 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",
>> + .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_info(&ctx->pdev->dev, "[%d][%s] hpd(%d)\n", __LINE__,
>> + __func__, value);
>> + atomic_set(&ctx->plugged, !!value);
>> + }
>> +}
>> +
>> +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_info(&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 __devinit int hdmi_codec_probe(struct platform_device *pdev)
>
> __devinit is not necessary.
ok. I will correct these.
regards,
Rahul Sharma.
>
> +{
>> + int ret;
>> + struct hdmi_audio_context *ctx;
>> + struct device_node *dev_node;
>> + struct platform_device *disp_pdev;
>> +
>> + dev_info(&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->plugged, 0);
>> + platform_set_drvdata(pdev, ctx);
>> +
>> + 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__);
>> + return -EINVAL;
>> + }
>> + return ret;
>> +}
>> +
>> +static __devexit int hdmi_codec_remove(struct platform_device *pdev)
>
> __devexit is not necessary.
>
>
>> +{
>> + dev_info(&pdev->dev, " %s:%s:%d", __FILE__, __func__, __LINE__);
>> + mdelay(1000);
>> +
>> + snd_soc_unregister_codec(&pdev->dev);
>> + return 0;
>> +}
>> +
>> +static struct platform_driver hdmi_codec_driver = {
>> + .driver = {
>> + .name = "exynos-hdmi-audio-codec",
>> + .owner = THIS_MODULE,
>> + },
>> +
>> + .probe = hdmi_codec_probe,
>> + .remove = __devexit_p(hdmi_codec_remove),
>> +};
>> +
>> +static int __init hdmi_codec_init(void)
>> +{
>> + int ret;
>> +
>> + ret = platform_driver_register(&hdmi_codec_driver);
>> + if (ret < 0)
>> + return -EINVAL;
>> +
>> + exynos_hdmi_audio_pdev = platform_device_register_simple
>> + ("exynos-hdmi-audio-codec", -1, NULL, 0);
>> + if (IS_ERR_OR_NULL(exynos_hdmi_audio_pdev)) {
>> + ret = PTR_ERR(exynos_hdmi_audio_pdev);
>> + platform_driver_unregister(&hdmi_codec_driver);
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +static void __exit hdmi_codec_exit(void)
>> +{
>> + platform_driver_unregister(&hdmi_codec_driver);
>> + platform_device_unregister(exynos_hdmi_audio_pdev);
>> +}
>> +
>> +module_init(hdmi_codec_init);
>> +module_exit(hdmi_codec_exit);
>> +
>> +MODULE_AUTHOR("Rahul Sharma <rahul.sharma@samsung.com>");
>> +MODULE_DESCRIPTION("ASoC EXYNOS HDMI codec driver");
>> +MODULE_LICENSE("GPL");
>> +MODULE_ALIAS("platform:" DRV_NAME);
>> --
>> 1.8.0
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-media" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
>
>
> --
> With warm regards,
> Sachin
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2013-01-14 6:04 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-01-13 12:52 [RFC PATCH 0/4] exynos-drm-hdmi driver to CDF complaint display driver Rahul Sharma
2013-01-13 12:52 ` [RFC PATCH 1/4] video: display: add event handling, set mode and hdmi ops to cdf core Rahul Sharma
2013-01-13 12:52 ` [RFC PATCH 2/4] drm/edid: temporarily exposing generic edid-read interface from drm Rahul Sharma
2013-01-13 12:52 ` [RFC PATCH 3/4] drm/exynos: moved drm hdmi driver to cdf framework Rahul Sharma
2013-01-13 12:52 ` [RFC PATCH 4/4] alsa/soc: add hdmi audio codec based on cdf Rahul Sharma
2013-01-14 5:43 ` Sachin Kamat
2013-01-14 6:04 ` Rahul Sharma
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).