From patchwork Thu May 16 19:27:38 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Russell King X-Patchwork-Id: 2580621 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by patchwork1.kernel.org (Postfix) with ESMTP id D02C43FE1F for ; Thu, 16 May 2013 22:51:20 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id C82F0E6553 for ; Thu, 16 May 2013 15:51:20 -0700 (PDT) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from caramon.arm.linux.org.uk (caramon.arm.linux.org.uk [78.32.30.218]) by gabe.freedesktop.org (Postfix) with ESMTP id 8524EE6517 for ; Thu, 16 May 2013 13:23:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=arm.linux.org.uk; s=caramon; h=Date:Sender:Message-Id:Subject:Cc:To:From:References:In-Reply-To; bh=g/pK9iw1S8aGeWwkD8VvVBVIZx0cEYHy5tbsE28hzYs=; b=nRfXfPV9vAwK6tSAGclyaHxPWt10+JOc4tynev/yyRm2VGTLZCsFikv4PQzJl+wVRk8dPpFOFY1kdmCIBFIOsGJVTp75XzhoVZQiUhAfMljivJ21owmj8FdbP7y6fH4Nxf/JsEZnnAD+xPR8Oym0qD3Pa34LlftovYuM35AKrIk=; Received: from e0022681537dd.dyn.arm.linux.org.uk ([2002:4e20:1eda:1:222:68ff:fe15:37dd]:34594 helo=rmk-PC.arm.linux.org.uk) by caramon.arm.linux.org.uk with esmtpsa (TLSv1:AES256-SHA:256) (Exim 4.76) (envelope-from ) id 1Ud3q1-0007m8-Ez; Thu, 16 May 2013 20:27:41 +0100 Received: from rmk by rmk-PC.arm.linux.org.uk with local (Exim 4.76) (envelope-from ) id 1Ud3py-0001r4-Lm; Thu, 16 May 2013 20:27:39 +0100 In-Reply-To: <20130516192510.GV18614@n2100.arm.linux.org.uk> References: <20130516192510.GV18614@n2100.arm.linux.org.uk> From: Russell King To: linux-arm-kernel@lists.infradead.org Subject: [RFC 7/8] DRM: Dove: add support for drm tda19988 driver Message-Id: Date: Thu, 16 May 2013 20:27:38 +0100 X-Mailman-Approved-At: Thu, 16 May 2013 15:37:33 -0700 Cc: Jason Cooper , dri-devel@lists.freedesktop.org, Darren Etheridge , Sebastian Hesselbarth X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: dri-devel-bounces+patchwork-dri-devel=patchwork.kernel.org@lists.freedesktop.org Errors-To: dri-devel-bounces+patchwork-dri-devel=patchwork.kernel.org@lists.freedesktop.org Signed-off-by: Russell King --- drivers/gpu/drm/dove/Kconfig | 9 ++ drivers/gpu/drm/dove/Makefile | 2 + drivers/gpu/drm/dove/dove_drm.h | 1 + drivers/gpu/drm/dove/dove_drv.c | 4 + drivers/gpu/drm/dove/dove_tda19988.c | 249 ++++++++++++++++++++++++++++++++++ 5 files changed, 265 insertions(+), 0 deletions(-) create mode 100644 drivers/gpu/drm/dove/dove_tda19988.c diff --git a/drivers/gpu/drm/dove/Kconfig b/drivers/gpu/drm/dove/Kconfig index 24844b6..718d3c5 100644 --- a/drivers/gpu/drm/dove/Kconfig +++ b/drivers/gpu/drm/dove/Kconfig @@ -16,6 +16,15 @@ config DRM_DOVE if DRM_DOVE != n +config DRM_DOVE_TDA1998X + bool "Support TDA1998X HDMI output" + depends on I2C + depends on DRM_I2C_NXP_TDA998X = y + default y + help + Support the TDA1998x HDMI output device found on the Solid-Run + CuBox. + config DRM_DOVE_CURSOR bool "Enable Dove DRM hardware cursor support" diff --git a/drivers/gpu/drm/dove/Makefile b/drivers/gpu/drm/dove/Makefile index a2326c4..65c701e 100644 --- a/drivers/gpu/drm/dove/Makefile +++ b/drivers/gpu/drm/dove/Makefile @@ -4,4 +4,6 @@ dove-y := dove_crtc.o dove_drv.o dove_fb.o dove_fbdev.o \ dove_gem.o dove_output.o dove_overlay.o dove-$(CONFIG_DEBUG_FS) += dove_debugfs.o +dove-$(CONFIG_DRM_DOVE_TDA1998X) += dove_tda19988.o + obj-$(CONFIG_DRM_DOVE) := dove.o diff --git a/drivers/gpu/drm/dove/dove_drm.h b/drivers/gpu/drm/dove/dove_drm.h index d00a9d6..825c553 100644 --- a/drivers/gpu/drm/dove/dove_drm.h +++ b/drivers/gpu/drm/dove/dove_drm.h @@ -57,6 +57,7 @@ void dove_overlay_destroy(struct drm_device *); void dove_drm_overlay_off(struct drm_device *, struct dove_overlay *); struct drm_connector *dove_drm_tda19988_create(struct drm_device *dev); +struct drm_connector *dove_drm_tda19988_nxp_create(struct drm_device *dev); int dove_drm_debugfs_init(struct drm_minor *); void dove_drm_debugfs_cleanup(struct drm_minor *); diff --git a/drivers/gpu/drm/dove/dove_drv.c b/drivers/gpu/drm/dove/dove_drv.c index 98cb25f..1792f00 100644 --- a/drivers/gpu/drm/dove/dove_drv.c +++ b/drivers/gpu/drm/dove/dove_drv.c @@ -88,6 +88,10 @@ static int dove_drm_load(struct drm_device *dev, unsigned long flags) } } +#ifdef CONFIG_DRM_DOVE_TDA1998X + dove_drm_tda19988_create(dev); +#endif + ret = drm_vblank_init(dev, n); if (ret) goto err_kms; diff --git a/drivers/gpu/drm/dove/dove_tda19988.c b/drivers/gpu/drm/dove/dove_tda19988.c new file mode 100644 index 0000000..26ef2a2 --- /dev/null +++ b/drivers/gpu/drm/dove/dove_tda19988.c @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2012 Russell King + * Rewritten from the dovefb driver, and Armada510 manuals. + * + * 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 "drmP.h" +#include "drm_edid.h" +#include "drm_crtc_helper.h" +#include "dove_output.h" +#include "dove_drm.h" +#include +#include + +struct dove_drm_tda19988_encoder { + struct drm_encoder_slave slave; + struct drm_encoder_helper_funcs encoder_helpers; +}; +#define to_tda19988_encoder(enc) container_of(to_encoder_slave(enc), struct dove_drm_tda19988_encoder, slave) + +static int dove_drm_connector_tda19988_mode_valid(struct drm_connector *conn, + struct drm_display_mode *mode) +{ + struct drm_encoder *enc = conn->encoder; + struct dove_drm_tda19988_encoder *tenc = to_tda19988_encoder(enc); + + return tenc->slave.slave_funcs->mode_valid(enc, mode); +} + +static int dove_drm_connector_tda19988_get_modes(struct drm_connector *conn) +{ + struct drm_display_mode *mode; + struct drm_encoder *enc = conn->encoder; + struct dove_drm_tda19988_encoder *tenc = to_tda19988_encoder(enc); + int count = tenc->slave.slave_funcs->get_modes(enc, conn); + + if (count) + return count; + + mode = drm_mode_create(conn->dev); + mode->type = DRM_MODE_TYPE_DRIVER; + mode->clock = 74250; + mode->vrefresh = 60; + mode->hdisplay = 1280; + mode->hsync_start = mode->hdisplay + 110; + mode->hsync_end = mode->hsync_start + 40; + mode->htotal = mode->hsync_end + 220; + mode->vdisplay = 720; + mode->vsync_start = mode->vdisplay + 5; + mode->vsync_end = mode->vsync_start + 5; + mode->vtotal = mode->vsync_end + 20; + mode->flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC; + + drm_mode_set_name(mode); + drm_mode_probed_add(conn, mode); + + return 1; +} + +static bool dove_drm_tda19988_enc_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted) +{ + adjusted->flags &= ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC | + DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC | + DRM_MODE_FLAG_PCSYNC | DRM_MODE_FLAG_NCSYNC); + + /* The TDA19988 always requires negative VSYNC? */ + adjusted->flags |= DRM_MODE_FLAG_NVSYNC; + + /* The TDA19988 requires positive HSYNC on 1080p or 720p */ + if ((adjusted->hdisplay == 1920 && adjusted->vdisplay == 1080) || + (adjusted->hdisplay == 1280 && adjusted->vdisplay == 720)) + adjusted->flags |= DRM_MODE_FLAG_PHSYNC; + else + adjusted->flags |= DRM_MODE_FLAG_NHSYNC; + + return true; +} + +static void dove_drm_tda19988_enc_destroy(struct drm_encoder *enc) +{ + struct dove_drm_tda19988_encoder *tenc = to_tda19988_encoder(enc); + + if (tenc->slave.slave_funcs) + tenc->slave.slave_funcs->destroy(enc); + + drm_encoder_cleanup(&tenc->slave.base); + kfree(tenc); +} + +static const struct drm_encoder_funcs dove_drm_tda19988_enc_funcs = { + .destroy = dove_drm_tda19988_enc_destroy, +}; + +static const struct drm_connector_helper_funcs dove_drm_conn_tda19988_helper_funcs = { + .get_modes = dove_drm_connector_tda19988_get_modes, + .mode_valid = dove_drm_connector_tda19988_mode_valid, + .best_encoder = dove_drm_connector_best_encoder, +}; + +static enum drm_connector_status dove_drm_conn_tda19988_detect( + struct drm_connector *conn, bool force) +{ + struct drm_encoder *enc = conn->encoder; + struct drm_encoder_helper_funcs *funcs = enc->helper_private; + enum drm_connector_status status = funcs->detect(enc, conn); + + /* + * Treat all disconnected status as unknown, otherwise we fail + * to initialize when we have no "connected" output devices. + */ + if (status == connector_status_disconnected) + status = connector_status_unknown; + + return status; +} + +static struct i2c_board_info info[] = { + { + .type = "tda998x", + .addr = 0x70, + }, + { } +}; + +static struct tda998x_encoder_params params = { +#if 0 + /* This is with a post mux value of 0x12, which is what the nxp driver uses + VIP_MUX_G_B | VIP_MUX_B_R | VIP_MUX_R_G = 0x00 | 0x02 | 0x10 + LCD out Pins VIP Int VP + R:7:0 VPC7:0 23:16 7:0[R] + G:15:8 VPB7:0 15:8 23:16[G] + B:23:16 VPA7:0 7:0 15:8[B] + */ + .swap_a = 0, + .swap_b = 1, + .swap_c = 2, + .swap_d = 3, + .swap_e = 4, + .swap_f = 5, +#else + /* With 0x24, there is no translation between vp_out and int_vp + FB LCD out Pins VIP Int Vp + R:23:16 R:7:0 VPC7:0 7:0 7:0[R] + G:15:8 G:15:8 VPB7:0 23:16 23:16[G] + B:7:0 B:23:16 VPA7:0 15:8 15:8[B] + */ + .swap_a = 2, + .swap_b = 3, + .swap_c = 4, + .swap_d = 5, + .swap_e = 0, + .swap_f = 1, +#endif + .audio_cfg = BIT(2), + .audio_frame[1] = 1, + .audio_format = AFMT_SPDIF, + .audio_sample_rate = 44100, +}; + +static int dove_drm_conn_tda19988_create(struct drm_connector *conn, + struct drm_encoder **enc_ret) +{ + struct dove_drm_tda19988_encoder *tenc; + struct drm_encoder_slave_funcs *sfuncs; + struct i2c_adapter *adap; + int ret; + + drm_connector_helper_add(conn, &dove_drm_conn_tda19988_helper_funcs); + + tenc = kzalloc(sizeof(*tenc), GFP_KERNEL); + if (!tenc) + return -ENOMEM; + + tenc->slave.base.possible_crtcs = 1 << 0; + + adap = i2c_get_adapter(0); + if (!adap) { + kfree(tenc); + return -EPROBE_DEFER; + } + + ret = drm_encoder_init(conn->dev, &tenc->slave.base, + &dove_drm_tda19988_enc_funcs, + DRM_MODE_ENCODER_TMDS); + if (ret) { + DRM_ERROR("unable to init encoder\n"); + i2c_put_adapter(adap); + kfree(tenc); + return ret; + } + + ret = drm_i2c_encoder_init(conn->dev, &tenc->slave, adap, info); + i2c_put_adapter(adap); + if (ret) { + DRM_ERROR("unable to init encoder slave\n"); + dove_drm_tda19988_enc_destroy(&tenc->slave.base); + return ret; + } + + sfuncs = tenc->slave.slave_funcs; + tenc->encoder_helpers.dpms = sfuncs->dpms; + tenc->encoder_helpers.save = sfuncs->save; + tenc->encoder_helpers.restore = sfuncs->restore; + tenc->encoder_helpers.mode_fixup = dove_drm_tda19988_enc_mode_fixup; + tenc->encoder_helpers.prepare = dove_drm_encoder_prepare; + tenc->encoder_helpers.commit = dove_drm_encoder_commit; + tenc->encoder_helpers.mode_set = sfuncs->mode_set; + tenc->encoder_helpers.detect = sfuncs->detect; + + drm_encoder_helper_add(&tenc->slave.base, &tenc->encoder_helpers); + ret = sfuncs->create_resources(&tenc->slave.base, conn); + if (ret) { + dove_drm_tda19988_enc_destroy(&tenc->slave.base); + return ret; + } + + sfuncs->set_config(&tenc->slave.base, ¶ms); + + conn->encoder = &tenc->slave.base; + *enc_ret = &tenc->slave.base; + + return ret; +} + +static int dove_drm_conn_tda19988_set_property(struct drm_connector *conn, + struct drm_property *property, uint64_t value) +{ + struct dove_drm_tda19988_encoder *tenc = + to_tda19988_encoder(conn->encoder); + struct drm_encoder_slave_funcs *sfuncs = tenc->slave.slave_funcs; + + return sfuncs->set_property(&tenc->slave.base, conn, property, value); +} + +static const struct dove_output_type dove_drm_conn_tda19988 = { + .connector_type = DRM_MODE_CONNECTOR_HDMIA, + .polled = DRM_CONNECTOR_POLL_HPD, + .detect = dove_drm_conn_tda19988_detect, + .create = dove_drm_conn_tda19988_create, + .set_property = dove_drm_conn_tda19988_set_property, +}; + +struct drm_connector *dove_drm_tda19988_create(struct drm_device *dev) +{ + return dove_output_create(dev, &dove_drm_conn_tda19988); +}