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);
|
||
|
}
|