/*
 *
 * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
 *
 * This program is free software and is provided to you under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation, and any use by you of this program is subject to the terms
 * of such GNU licence.
 *
 * A copy of the licence is included with the program, and can also be obtained
 * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 *
 */



/**
 * pl111_drm_connector.c
 * Implementation of the connector functions for PL111 DRM
 */
#include <linux/amba/bus.h>
#include <linux/amba/clcd.h>
#include <linux/version.h>
#include <linux/shmem_fs.h>
#include <linux/dma-buf.h>
#include <linux/module.h>

#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>

#include "pl111_drm.h"


static struct {
	int w, h, type;
} pl111_drm_modes[] = {
	{ 640, 480,  DRM_MODE_TYPE_PREFERRED},
	{ 800, 600,  0},
	{1024, 768,  0},
	{  -1,  -1, -1}
};

void pl111_connector_destroy(struct drm_connector *connector)
{
	struct pl111_drm_connector *pl111_connector =
				PL111_CONNECTOR_FROM_CONNECTOR(connector);

	DRM_DEBUG_KMS("DRM %s on connector=%p\n", __func__, connector);

	drm_sysfs_connector_remove(connector);
	drm_connector_cleanup(connector);
	kfree(pl111_connector);
}

enum drm_connector_status pl111_connector_detect(struct drm_connector
							*connector, bool force)
{
	DRM_DEBUG_KMS("DRM %s on connector=%p\n", __func__, connector);
	return connector_status_connected;
}

void pl111_connector_dpms(struct drm_connector *connector, int mode)
{
	DRM_DEBUG_KMS("DRM %s on connector=%p\n", __func__, connector);
}

struct drm_encoder *
pl111_connector_helper_best_encoder(struct drm_connector *connector)
{
	DRM_DEBUG_KMS("DRM %s on connector=%p\n", __func__, connector);

	if (connector->encoder != NULL) {
		return connector->encoder; /* Return attached encoder */
	} else {
		/*
		 * If there is no attached encoder we choose the best candidate
		 * from the list.
		 * For PL111 there is only one encoder so we return the first
		 * one we find.
		 * Other h/w would require a suitable criterion below.
		 */
		struct drm_encoder *encoder = NULL;
		struct drm_device *dev = connector->dev;

		list_for_each_entry(encoder, &dev->mode_config.encoder_list,
					head) {
			if (1) { /* criterion ? */
				break;
			}
		}
		return encoder; /* return best candidate encoder */
	}
}

int pl111_connector_helper_get_modes(struct drm_connector *connector)
{
	int i = 0;
	int count = 0;

	DRM_DEBUG_KMS("DRM %s on connector=%p\n", __func__, connector);

	while (pl111_drm_modes[i].w != -1) {
		struct drm_display_mode *mode =
				drm_mode_find_dmt(connector->dev,
						pl111_drm_modes[i].w,
						pl111_drm_modes[i].h,
						60
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
						, false
#endif
						);

		if (mode != NULL) {
			mode->type |= pl111_drm_modes[i].type;
			drm_mode_probed_add(connector, mode);
			count++;
		}

		i++;
	}

	DRM_DEBUG_KMS("found %d modes\n", count);

	return count;
}

int pl111_connector_helper_mode_valid(struct drm_connector *connector,
					struct drm_display_mode *mode)
{
	DRM_DEBUG_KMS("DRM %s on connector=%p\n", __func__, connector);
	return MODE_OK;
}

const struct drm_connector_funcs connector_funcs = {
	.fill_modes = drm_helper_probe_single_connector_modes,
	.destroy = pl111_connector_destroy,
	.detect = pl111_connector_detect,
	.dpms = pl111_connector_dpms,
};

const struct drm_connector_helper_funcs connector_helper_funcs = {
	.get_modes = pl111_connector_helper_get_modes,
	.mode_valid = pl111_connector_helper_mode_valid,
	.best_encoder = pl111_connector_helper_best_encoder,
};

struct pl111_drm_connector *pl111_connector_create(struct drm_device *dev)
{
	struct pl111_drm_connector *pl111_connector;

	pl111_connector = kzalloc(sizeof(struct pl111_drm_connector),
					GFP_KERNEL);

	if (pl111_connector == NULL) {
		pr_err("Failed to allocated pl111_drm_connector\n");
		return NULL;
	}

	drm_connector_init(dev, &pl111_connector->connector, &connector_funcs,
				DRM_MODE_CONNECTOR_DVII);

	drm_connector_helper_add(&pl111_connector->connector,
					&connector_helper_funcs);

	drm_sysfs_connector_add(&pl111_connector->connector);

	return pl111_connector;
}

