18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/
48c2ecf20Sopenharmony_ci * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/export.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h>
108c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h>
118c2ecf20Sopenharmony_ci#include <drm/drm_panel.h>
128c2ecf20Sopenharmony_ci#include <drm/drm_of.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "tidss_crtc.h"
158c2ecf20Sopenharmony_ci#include "tidss_drv.h"
168c2ecf20Sopenharmony_ci#include "tidss_encoder.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic int tidss_encoder_atomic_check(struct drm_encoder *encoder,
198c2ecf20Sopenharmony_ci				      struct drm_crtc_state *crtc_state,
208c2ecf20Sopenharmony_ci				      struct drm_connector_state *conn_state)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	struct drm_device *ddev = encoder->dev;
238c2ecf20Sopenharmony_ci	struct tidss_crtc_state *tcrtc_state = to_tidss_crtc_state(crtc_state);
248c2ecf20Sopenharmony_ci	struct drm_display_info *di = &conn_state->connector->display_info;
258c2ecf20Sopenharmony_ci	struct drm_bridge *bridge;
268c2ecf20Sopenharmony_ci	bool bus_flags_set = false;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	dev_dbg(ddev->dev, "%s\n", __func__);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	/*
318c2ecf20Sopenharmony_ci	 * Take the bus_flags from the first bridge that defines
328c2ecf20Sopenharmony_ci	 * bridge timings, or from the connector's display_info if no
338c2ecf20Sopenharmony_ci	 * bridge defines the timings.
348c2ecf20Sopenharmony_ci	 */
358c2ecf20Sopenharmony_ci	drm_for_each_bridge_in_chain(encoder, bridge) {
368c2ecf20Sopenharmony_ci		if (!bridge->timings)
378c2ecf20Sopenharmony_ci			continue;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci		tcrtc_state->bus_flags = bridge->timings->input_bus_flags;
408c2ecf20Sopenharmony_ci		bus_flags_set = true;
418c2ecf20Sopenharmony_ci		break;
428c2ecf20Sopenharmony_ci	}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	if (!di->bus_formats || di->num_bus_formats == 0)  {
458c2ecf20Sopenharmony_ci		dev_err(ddev->dev, "%s: No bus_formats in connected display\n",
468c2ecf20Sopenharmony_ci			__func__);
478c2ecf20Sopenharmony_ci		return -EINVAL;
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	// XXX any cleaner way to set bus format and flags?
518c2ecf20Sopenharmony_ci	tcrtc_state->bus_format = di->bus_formats[0];
528c2ecf20Sopenharmony_ci	if (!bus_flags_set)
538c2ecf20Sopenharmony_ci		tcrtc_state->bus_flags = di->bus_flags;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	return 0;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic void tidss_encoder_destroy(struct drm_encoder *encoder)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	drm_encoder_cleanup(encoder);
618c2ecf20Sopenharmony_ci	kfree(encoder);
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs encoder_helper_funcs = {
658c2ecf20Sopenharmony_ci	.atomic_check = tidss_encoder_atomic_check,
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic const struct drm_encoder_funcs encoder_funcs = {
698c2ecf20Sopenharmony_ci	.destroy = tidss_encoder_destroy,
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistruct drm_encoder *tidss_encoder_create(struct tidss_device *tidss,
738c2ecf20Sopenharmony_ci					 u32 encoder_type, u32 possible_crtcs)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct drm_encoder *enc;
768c2ecf20Sopenharmony_ci	int ret;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	enc = kzalloc(sizeof(*enc), GFP_KERNEL);
798c2ecf20Sopenharmony_ci	if (!enc)
808c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	enc->possible_crtcs = possible_crtcs;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	ret = drm_encoder_init(&tidss->ddev, enc, &encoder_funcs,
858c2ecf20Sopenharmony_ci			       encoder_type, NULL);
868c2ecf20Sopenharmony_ci	if (ret < 0) {
878c2ecf20Sopenharmony_ci		kfree(enc);
888c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	drm_encoder_helper_add(enc, &encoder_helper_funcs);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	dev_dbg(tidss->dev, "Encoder create done\n");
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	return enc;
968c2ecf20Sopenharmony_ci}
97