aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/xilinx/xilinx_drm_encoder.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/xilinx/xilinx_drm_encoder.c')
-rw-r--r--drivers/gpu/drm/xilinx/xilinx_drm_encoder.c240
1 files changed, 240 insertions, 0 deletions
diff --git a/drivers/gpu/drm/xilinx/xilinx_drm_encoder.c b/drivers/gpu/drm/xilinx/xilinx_drm_encoder.c
new file mode 100644
index 000000000000..ca3f9f112162
--- /dev/null
+++ b/drivers/gpu/drm/xilinx/xilinx_drm_encoder.c
@@ -0,0 +1,240 @@
+/*
+ * Xilinx DRM encoder driver for Xilinx
+ *
+ * Copyright (C) 2013 Xilinx, Inc.
+ *
+ * Author: Hyun Woo Kwon <hyunk@xilinx.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_encoder_slave.h>
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include "xilinx_drm_drv.h"
+#include "xilinx_drm_encoder.h"
+
+struct xilinx_drm_encoder {
+ struct drm_encoder_slave slave;
+ struct device *dev;
+ int dpms;
+};
+
+#define to_xilinx_encoder(x) \
+ container_of(x, struct xilinx_drm_encoder, slave)
+
+/* set encoder dpms */
+static void xilinx_drm_encoder_dpms(struct drm_encoder *base_encoder, int dpms)
+{
+ struct xilinx_drm_encoder *encoder;
+ struct drm_encoder_slave *encoder_slave;
+ const struct drm_encoder_slave_funcs *encoder_sfuncs;
+
+ encoder_slave = to_encoder_slave(base_encoder);
+ encoder_sfuncs = encoder_slave->slave_funcs;
+ encoder = to_xilinx_encoder(encoder_slave);
+
+ DRM_DEBUG_KMS("dpms: %d -> %d\n", encoder->dpms, dpms);
+
+ if (encoder->dpms == dpms)
+ return;
+
+ encoder->dpms = dpms;
+ if (encoder_sfuncs->dpms)
+ encoder_sfuncs->dpms(base_encoder, dpms);
+}
+
+/* adjust a mode if needed */
+static bool
+xilinx_drm_encoder_mode_fixup(struct drm_encoder *base_encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_encoder_slave *encoder_slave;
+ const struct drm_encoder_slave_funcs *encoder_sfuncs = NULL;
+ bool ret = true;
+
+ encoder_slave = to_encoder_slave(base_encoder);
+ encoder_sfuncs = encoder_slave->slave_funcs;
+ if (encoder_sfuncs->mode_fixup)
+ ret = encoder_sfuncs->mode_fixup(base_encoder, mode,
+ adjusted_mode);
+
+ return ret;
+}
+
+/* set mode to xilinx encoder */
+static void xilinx_drm_encoder_mode_set(struct drm_encoder *base_encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_encoder_slave *encoder_slave;
+ const struct drm_encoder_slave_funcs *encoder_sfuncs;
+
+ DRM_DEBUG_KMS("h: %d, v: %d\n",
+ adjusted_mode->hdisplay, adjusted_mode->vdisplay);
+ DRM_DEBUG_KMS("refresh: %d, pclock: %d khz\n",
+ adjusted_mode->vrefresh, adjusted_mode->clock);
+
+ encoder_slave = to_encoder_slave(base_encoder);
+ encoder_sfuncs = encoder_slave->slave_funcs;
+ if (encoder_sfuncs->mode_set)
+ encoder_sfuncs->mode_set(base_encoder, mode, adjusted_mode);
+}
+
+/* apply mode to encoder pipe */
+static void xilinx_drm_encoder_commit(struct drm_encoder *base_encoder)
+{
+ /* start encoder with new mode */
+ xilinx_drm_encoder_dpms(base_encoder, DRM_MODE_DPMS_ON);
+}
+
+/* prepare encoder */
+static void xilinx_drm_encoder_prepare(struct drm_encoder *base_encoder)
+{
+ xilinx_drm_encoder_dpms(base_encoder, DRM_MODE_DPMS_OFF);
+}
+
+/* get crtc */
+static struct drm_crtc *
+xilinx_drm_encoder_get_crtc(struct drm_encoder *base_encoder)
+{
+ return base_encoder->crtc;
+}
+
+static const struct drm_encoder_helper_funcs xilinx_drm_encoder_helper_funcs = {
+ .dpms = xilinx_drm_encoder_dpms,
+ .mode_fixup = xilinx_drm_encoder_mode_fixup,
+ .mode_set = xilinx_drm_encoder_mode_set,
+ .prepare = xilinx_drm_encoder_prepare,
+ .commit = xilinx_drm_encoder_commit,
+ .get_crtc = xilinx_drm_encoder_get_crtc,
+};
+
+/* destroy encoder */
+void xilinx_drm_encoder_destroy(struct drm_encoder *base_encoder)
+{
+ struct xilinx_drm_encoder *encoder;
+ struct drm_encoder_slave *encoder_slave;
+
+ encoder_slave = to_encoder_slave(base_encoder);
+ encoder = to_xilinx_encoder(encoder_slave);
+
+ /* make sure encoder is off */
+ xilinx_drm_encoder_dpms(base_encoder, DRM_MODE_DPMS_OFF);
+
+ drm_encoder_cleanup(base_encoder);
+ put_device(encoder->dev);
+}
+
+static const struct drm_encoder_funcs xilinx_drm_encoder_funcs = {
+ .destroy = xilinx_drm_encoder_destroy,
+};
+
+/* create encoder */
+struct drm_encoder *xilinx_drm_encoder_create(struct drm_device *drm,
+ struct device_node *node)
+{
+ struct xilinx_drm_encoder *encoder;
+ struct i2c_client *i2c_slv;
+ struct i2c_driver *i2c_driver;
+ struct drm_i2c_encoder_driver *drm_i2c_driver;
+ struct device_driver *device_driver;
+ struct platform_device *platform_slv;
+ struct platform_driver *platform_driver;
+ struct drm_platform_encoder_driver *drm_platform_driver;
+ int ret = 0;
+
+ encoder = devm_kzalloc(drm->dev, sizeof(*encoder), GFP_KERNEL);
+ if (!encoder)
+ return ERR_PTR(-ENOMEM);
+
+ encoder->dpms = DRM_MODE_DPMS_OFF;
+
+ /* FIXME: Use DT to figure out crtcs / clones */
+ encoder->slave.base.possible_crtcs = 1;
+ encoder->slave.base.possible_clones = ~0;
+ ret = drm_encoder_init(drm, &encoder->slave.base,
+ &xilinx_drm_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS, NULL);
+ if (ret) {
+ DRM_ERROR("failed to initialize drm encoder\n");
+ return ERR_PTR(ret);
+ }
+
+ drm_encoder_helper_add(&encoder->slave.base,
+ &xilinx_drm_encoder_helper_funcs);
+
+ /* initialize slave encoder */
+ i2c_slv = of_find_i2c_device_by_node(node);
+ if (i2c_slv && i2c_slv->dev.driver) {
+ i2c_driver = to_i2c_driver(i2c_slv->dev.driver);
+ drm_i2c_driver = to_drm_i2c_encoder_driver(i2c_driver);
+ if (!drm_i2c_driver || !drm_i2c_driver->encoder_init) {
+ DRM_DEBUG_KMS("failed to initialize i2c slave\n");
+ ret = -EPROBE_DEFER;
+ goto err_out;
+ }
+
+ encoder->dev = &i2c_slv->dev;
+ ret = drm_i2c_driver->encoder_init(i2c_slv, drm,
+ &encoder->slave);
+ } else {
+ platform_slv = of_find_device_by_node(node);
+ if (!platform_slv) {
+ DRM_DEBUG_KMS("failed to get an encoder slv\n");
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ device_driver = platform_slv->dev.driver;
+ if (!device_driver) {
+ DRM_DEBUG_KMS("failed to get device driver\n");
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ platform_driver = to_platform_driver(device_driver);
+ drm_platform_driver =
+ to_drm_platform_encoder_driver(platform_driver);
+ if (!drm_platform_driver ||
+ !drm_platform_driver->encoder_init) {
+ DRM_DEBUG_KMS("failed to initialize platform slave\n");
+ ret = -EPROBE_DEFER;
+ goto err_out;
+ }
+
+ encoder->dev = &platform_slv->dev;
+ ret = drm_platform_driver->encoder_init(platform_slv, drm,
+ &encoder->slave);
+ }
+
+ if (ret) {
+ DRM_ERROR("failed to initialize encoder slave\n");
+ goto err_out;
+ }
+
+ if (!encoder->slave.slave_funcs) {
+ DRM_ERROR("there's no encoder slave function\n");
+ ret = -ENODEV;
+ goto err_out;
+ }
+
+ return &encoder->slave.base;
+
+err_out:
+ return ERR_PTR(ret);
+}