drm/msm/sde: add virtual writeback device and connector

Enable virtual writeback connector by allowing a property on the
connector to specify output frame buffer id, which takes in a
framebuffer object added via ADDFB2 ioctl.  A new writeback
configure ioctl is added to configure connection state and
display modes of writeback connector.

Change-Id: Ifce411a3f0798e3af7dd7f19da27d67cdd849bfb
Signed-off-by: Adrian Salido-Moreno <adrianm@codeaurora.org>
Signed-off-by: Alan Kwong <akwong@codeaurora.org>
Signed-off-by: Clarence Ip <cip@codeaurora.org>
Signed-off-by: Dhaval Patel <pdhaval@codeaurora.org>
Signed-off-by: Narendra Muppalla <narendram@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/sde/sde_formats.c b/drivers/gpu/drm/msm/sde/sde_formats.c
index 5655a8a..6d9c574 100644
--- a/drivers/gpu/drm/msm/sde/sde_formats.c
+++ b/drivers/gpu/drm/msm/sde/sde_formats.c
@@ -363,6 +363,9 @@
 	uint32_t *v_sample,
 	uint32_t *h_sample)
 {
+	if (!v_sample || !h_sample)
+		return;
+
 	switch (chroma_sample) {
 	case SDE_CHROMA_H2V1:
 		*v_sample = 1;
@@ -702,6 +705,69 @@
 	return ret;
 }
 
+static void _sde_format_calc_offset_linear(struct sde_hw_fmt_layout *source,
+		u32 x, u32 y)
+{
+	if ((x == 0) && (y == 0))
+		return;
+
+	source->plane_addr[0] += y * source->plane_pitch[0];
+
+	if (source->num_planes == 1) {
+		source->plane_addr[0] += x * source->format->bpp;
+	} else {
+		uint32_t xoff, yoff;
+		uint32_t v_subsample = 1;
+		uint32_t h_subsample = 1;
+
+		_sde_get_v_h_subsample_rate(source->format->chroma_sample,
+				&v_subsample, &h_subsample);
+
+		xoff = x / h_subsample;
+		yoff = y / v_subsample;
+
+		source->plane_addr[0] += x;
+		source->plane_addr[1] += xoff +
+				(yoff * source->plane_pitch[1]);
+		if (source->num_planes == 2) /* pseudo planar */
+			source->plane_addr[1] += xoff;
+		else /* planar */
+			source->plane_addr[2] += xoff +
+				(yoff * source->plane_pitch[2]);
+	}
+}
+
+int sde_format_populate_layout_with_roi(
+		int mmu_id,
+		struct drm_framebuffer *fb,
+		struct sde_rect *roi,
+		struct sde_hw_fmt_layout *layout)
+{
+	int ret;
+
+	ret = sde_format_populate_layout(mmu_id, fb, layout);
+	if (ret || !roi)
+		return ret;
+
+	if (!roi->w || !roi->h || (roi->x + roi->w > fb->width) ||
+			(roi->y + roi->h > fb->height)) {
+		DRM_ERROR("invalid roi=[%d,%d,%d,%d], fb=[%u,%u]\n",
+				roi->x, roi->y, roi->w, roi->h,
+				fb->width, fb->height);
+		ret = -EINVAL;
+	} else if (SDE_FORMAT_IS_LINEAR(layout->format)) {
+		_sde_format_calc_offset_linear(layout, roi->x, roi->y);
+		layout->width = roi->w;
+		layout->height = roi->h;
+	} else if (roi->x || roi->y || (roi->w != fb->width) ||
+			(roi->h != fb->height)) {
+		DRM_ERROR("non-linear layout with roi not supported\n");
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
 int sde_format_check_modified_format(
 		const struct msm_kms *kms,
 		const struct msm_format *msm_fmt,