140 lines
		
	
	
	
		
			3.6 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			140 lines
		
	
	
	
		
			3.6 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  * 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 <drm/drmP.h>
 | ||
|  | #include <drm/drm_crtc_helper.h>
 | ||
|  | #include <drm/drm_edid.h>
 | ||
|  | #include <drm/drm_encoder_slave.h>
 | ||
|  | #include "armada_drm.h"
 | ||
|  | #include "armada_output.h"
 | ||
|  | #include "armada_slave.h"
 | ||
|  | 
 | ||
|  | static int armada_drm_slave_get_modes(struct drm_connector *conn) | ||
|  | { | ||
|  | 	struct drm_encoder *enc = armada_drm_connector_encoder(conn); | ||
|  | 	int count = 0; | ||
|  | 
 | ||
|  | 	if (enc) { | ||
|  | 		struct drm_encoder_slave *slave = to_encoder_slave(enc); | ||
|  | 
 | ||
|  | 		count = slave->slave_funcs->get_modes(enc, conn); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return count; | ||
|  | } | ||
|  | 
 | ||
|  | static void armada_drm_slave_destroy(struct drm_encoder *enc) | ||
|  | { | ||
|  | 	struct drm_encoder_slave *slave = to_encoder_slave(enc); | ||
|  | 	struct i2c_client *client = drm_i2c_encoder_get_client(enc); | ||
|  | 
 | ||
|  | 	if (slave->slave_funcs) | ||
|  | 		slave->slave_funcs->destroy(enc); | ||
|  | 	if (client) | ||
|  | 		i2c_put_adapter(client->adapter); | ||
|  | 
 | ||
|  | 	drm_encoder_cleanup(&slave->base); | ||
|  | 	kfree(slave); | ||
|  | } | ||
|  | 
 | ||
|  | static const struct drm_encoder_funcs armada_drm_slave_encoder_funcs = { | ||
|  | 	.destroy	= armada_drm_slave_destroy, | ||
|  | }; | ||
|  | 
 | ||
|  | static const struct drm_connector_helper_funcs armada_drm_slave_helper_funcs = { | ||
|  | 	.get_modes	= armada_drm_slave_get_modes, | ||
|  | 	.mode_valid	= armada_drm_slave_encoder_mode_valid, | ||
|  | 	.best_encoder	= armada_drm_connector_encoder, | ||
|  | }; | ||
|  | 
 | ||
|  | static const struct drm_encoder_helper_funcs drm_slave_encoder_helpers = { | ||
|  | 	.dpms = drm_i2c_encoder_dpms, | ||
|  | 	.save = drm_i2c_encoder_save, | ||
|  | 	.restore = drm_i2c_encoder_restore, | ||
|  | 	.mode_fixup = drm_i2c_encoder_mode_fixup, | ||
|  | 	.prepare = drm_i2c_encoder_prepare, | ||
|  | 	.commit = drm_i2c_encoder_commit, | ||
|  | 	.mode_set = drm_i2c_encoder_mode_set, | ||
|  | 	.detect = drm_i2c_encoder_detect, | ||
|  | }; | ||
|  | 
 | ||
|  | static int | ||
|  | armada_drm_conn_slave_create(struct drm_connector *conn, const void *data) | ||
|  | { | ||
|  | 	const struct armada_drm_slave_config *config = data; | ||
|  | 	struct drm_encoder_slave *slave; | ||
|  | 	struct i2c_adapter *adap; | ||
|  | 	int ret; | ||
|  | 
 | ||
|  | 	conn->interlace_allowed = config->interlace_allowed; | ||
|  | 	conn->doublescan_allowed = config->doublescan_allowed; | ||
|  | 	conn->polled = config->polled; | ||
|  | 
 | ||
|  | 	drm_connector_helper_add(conn, &armada_drm_slave_helper_funcs); | ||
|  | 
 | ||
|  | 	slave = kzalloc(sizeof(*slave), GFP_KERNEL); | ||
|  | 	if (!slave) | ||
|  | 		return -ENOMEM; | ||
|  | 
 | ||
|  | 	slave->base.possible_crtcs = config->crtcs; | ||
|  | 
 | ||
|  | 	adap = i2c_get_adapter(config->i2c_adapter_id); | ||
|  | 	if (!adap) { | ||
|  | 		kfree(slave); | ||
|  | 		return -EPROBE_DEFER; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	ret = drm_encoder_init(conn->dev, &slave->base, | ||
|  | 			       &armada_drm_slave_encoder_funcs, | ||
|  | 			       DRM_MODE_ENCODER_TMDS); | ||
|  | 	if (ret) { | ||
|  | 		DRM_ERROR("unable to init encoder\n"); | ||
|  | 		i2c_put_adapter(adap); | ||
|  | 		kfree(slave); | ||
|  | 		return ret; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	ret = drm_i2c_encoder_init(conn->dev, slave, adap, &config->info); | ||
|  | 	i2c_put_adapter(adap); | ||
|  | 	if (ret) { | ||
|  | 		DRM_ERROR("unable to init encoder slave\n"); | ||
|  | 		armada_drm_slave_destroy(&slave->base); | ||
|  | 		return ret; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	drm_encoder_helper_add(&slave->base, &drm_slave_encoder_helpers); | ||
|  | 
 | ||
|  | 	ret = slave->slave_funcs->create_resources(&slave->base, conn); | ||
|  | 	if (ret) { | ||
|  | 		armada_drm_slave_destroy(&slave->base); | ||
|  | 		return ret; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	ret = drm_mode_connector_attach_encoder(conn, &slave->base); | ||
|  | 	if (ret) { | ||
|  | 		armada_drm_slave_destroy(&slave->base); | ||
|  | 		return ret; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	conn->encoder = &slave->base; | ||
|  | 
 | ||
|  | 	return ret; | ||
|  | } | ||
|  | 
 | ||
|  | static const struct armada_output_type armada_drm_conn_slave = { | ||
|  | 	.connector_type	= DRM_MODE_CONNECTOR_HDMIA, | ||
|  | 	.create		= armada_drm_conn_slave_create, | ||
|  | 	.set_property	= armada_drm_slave_encoder_set_property, | ||
|  | }; | ||
|  | 
 | ||
|  | int armada_drm_connector_slave_create(struct drm_device *dev, | ||
|  | 	const struct armada_drm_slave_config *config) | ||
|  | { | ||
|  | 	return armada_output_create(dev, &armada_drm_conn_slave, config); | ||
|  | } |