Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[media] media: i.MX27 camera: Add resizing support

If the attached video sensor cannot provide the
requested image size, try to use resizing engine
included in the eMMa-PrP IP.

This patch supports both averaging and bilinear
algorithms.

Signed-off-by: Javier Martin <javier.martin@vista-silicon.com>
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>

authored by

Javier Martin and committed by
Mauro Carvalho Chehab
750a6dff e9de6167

+256 -4
+256 -4
drivers/media/video/mx2_camera.c
··· 19 19 #include <linux/dma-mapping.h> 20 20 #include <linux/errno.h> 21 21 #include <linux/fs.h> 22 + #include <linux/gcd.h> 22 23 #include <linux/interrupt.h> 23 24 #include <linux/kernel.h> 24 25 #include <linux/mm.h> ··· 205 204 #define PRP_INTR_LBOVF (1 << 7) 206 205 #define PRP_INTR_CH2OVF (1 << 8) 207 206 207 + /* Resizing registers */ 208 + #define PRP_RZ_VALID_TBL_LEN(x) ((x) << 24) 209 + #define PRP_RZ_VALID_BILINEAR (1 << 31) 210 + 208 211 #define MAX_VIDEO_MEM 16 212 + 213 + #define RESIZE_NUM_MIN 1 214 + #define RESIZE_NUM_MAX 20 215 + #define BC_COEF 3 216 + #define SZ_COEF (1 << BC_COEF) 217 + 218 + #define RESIZE_DIR_H 0 219 + #define RESIZE_DIR_V 1 220 + 221 + #define RESIZE_ALGO_BILINEAR 0 222 + #define RESIZE_ALGO_AVERAGING 1 209 223 210 224 struct mx2_prp_cfg { 211 225 int channel; ··· 229 213 u32 src_pixel; 230 214 u32 ch1_pixel; 231 215 u32 irq_flags; 216 + }; 217 + 218 + /* prp resizing parameters */ 219 + struct emma_prp_resize { 220 + int algo; /* type of algorithm used */ 221 + int len; /* number of coefficients */ 222 + unsigned char s[RESIZE_NUM_MAX]; /* table of coefficients */ 232 223 }; 233 224 234 225 /* prp configuration for a client-host fmt pair */ ··· 297 274 dma_addr_t discard_buffer_dma; 298 275 size_t discard_size; 299 276 struct mx2_fmt_cfg *emma_prp; 277 + struct emma_prp_resize resizing[2]; 278 + unsigned int s_width, s_height; 300 279 u32 frame_count; 301 280 struct vb2_alloc_ctx *alloc_ctx; 302 281 }; ··· 703 678 struct mx2_camera_dev *pcdev = ici->priv; 704 679 struct mx2_fmt_cfg *prp = pcdev->emma_prp; 705 680 706 - writel((icd->user_width << 16) | icd->user_height, 681 + writel((pcdev->s_width << 16) | pcdev->s_height, 707 682 pcdev->base_emma + PRP_SRC_FRAME_SIZE); 708 683 writel(prp->cfg.src_pixel, 709 684 pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL); ··· 721 696 722 697 /* Enable interrupts */ 723 698 writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL); 699 + } 700 + 701 + static void mx2_prp_resize_commit(struct mx2_camera_dev *pcdev) 702 + { 703 + int dir; 704 + 705 + for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) { 706 + unsigned char *s = pcdev->resizing[dir].s; 707 + int len = pcdev->resizing[dir].len; 708 + unsigned int coeff[2] = {0, 0}; 709 + unsigned int valid = 0; 710 + int i; 711 + 712 + if (len == 0) 713 + continue; 714 + 715 + for (i = RESIZE_NUM_MAX - 1; i >= 0; i--) { 716 + int j; 717 + 718 + j = i > 9 ? 1 : 0; 719 + coeff[j] = (coeff[j] << BC_COEF) | 720 + (s[i] & (SZ_COEF - 1)); 721 + 722 + if (i == 5 || i == 15) 723 + coeff[j] <<= 1; 724 + 725 + valid = (valid << 1) | (s[i] >> BC_COEF); 726 + } 727 + 728 + valid |= PRP_RZ_VALID_TBL_LEN(len); 729 + 730 + if (pcdev->resizing[dir].algo == RESIZE_ALGO_BILINEAR) 731 + valid |= PRP_RZ_VALID_BILINEAR; 732 + 733 + if (pcdev->emma_prp->cfg.channel == 1) { 734 + if (dir == RESIZE_DIR_H) { 735 + writel(coeff[0], pcdev->base_emma + 736 + PRP_CH1_RZ_HORI_COEF1); 737 + writel(coeff[1], pcdev->base_emma + 738 + PRP_CH1_RZ_HORI_COEF2); 739 + writel(valid, pcdev->base_emma + 740 + PRP_CH1_RZ_HORI_VALID); 741 + } else { 742 + writel(coeff[0], pcdev->base_emma + 743 + PRP_CH1_RZ_VERT_COEF1); 744 + writel(coeff[1], pcdev->base_emma + 745 + PRP_CH1_RZ_VERT_COEF2); 746 + writel(valid, pcdev->base_emma + 747 + PRP_CH1_RZ_VERT_VALID); 748 + } 749 + } else { 750 + if (dir == RESIZE_DIR_H) { 751 + writel(coeff[0], pcdev->base_emma + 752 + PRP_CH2_RZ_HORI_COEF1); 753 + writel(coeff[1], pcdev->base_emma + 754 + PRP_CH2_RZ_HORI_COEF2); 755 + writel(valid, pcdev->base_emma + 756 + PRP_CH2_RZ_HORI_VALID); 757 + } else { 758 + writel(coeff[0], pcdev->base_emma + 759 + PRP_CH2_RZ_VERT_COEF1); 760 + writel(coeff[1], pcdev->base_emma + 761 + PRP_CH2_RZ_VERT_COEF2); 762 + writel(valid, pcdev->base_emma + 763 + PRP_CH2_RZ_VERT_VALID); 764 + } 765 + } 766 + } 724 767 } 725 768 726 769 static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) ··· 856 763 pcdev->buf_discard[1].discard = true; 857 764 list_add_tail(&pcdev->buf_discard[1].queue, 858 765 &pcdev->discard); 766 + 767 + mx2_prp_resize_commit(pcdev); 859 768 860 769 mx27_camera_emma_buf_init(icd, bytesperline); 861 770 ··· 1144 1049 return formats; 1145 1050 } 1146 1051 1052 + static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev, 1053 + struct v4l2_mbus_framefmt *mf_in, 1054 + struct v4l2_pix_format *pix_out, bool apply) 1055 + { 1056 + int num, den; 1057 + unsigned long m; 1058 + int i, dir; 1059 + 1060 + for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) { 1061 + struct emma_prp_resize tmprsz; 1062 + unsigned char *s = tmprsz.s; 1063 + int len = 0; 1064 + int in, out; 1065 + 1066 + if (dir == RESIZE_DIR_H) { 1067 + in = mf_in->width; 1068 + out = pix_out->width; 1069 + } else { 1070 + in = mf_in->height; 1071 + out = pix_out->height; 1072 + } 1073 + 1074 + if (in < out) 1075 + return -EINVAL; 1076 + else if (in == out) 1077 + continue; 1078 + 1079 + /* Calculate ratio */ 1080 + m = gcd(in, out); 1081 + num = in / m; 1082 + den = out / m; 1083 + if (num > RESIZE_NUM_MAX) 1084 + return -EINVAL; 1085 + 1086 + if ((num >= 2 * den) && (den == 1) && 1087 + (num < 9) && (!(num & 0x01))) { 1088 + int sum = 0; 1089 + int j; 1090 + 1091 + /* Average scaling for >= 2:1 ratios */ 1092 + /* Support can be added for num >=9 and odd values */ 1093 + 1094 + tmprsz.algo = RESIZE_ALGO_AVERAGING; 1095 + len = num; 1096 + 1097 + for (i = 0; i < (len / 2); i++) 1098 + s[i] = 8; 1099 + 1100 + do { 1101 + for (i = 0; i < (len / 2); i++) { 1102 + s[i] = s[i] >> 1; 1103 + sum = 0; 1104 + for (j = 0; j < (len / 2); j++) 1105 + sum += s[j]; 1106 + if (sum == 4) 1107 + break; 1108 + } 1109 + } while (sum != 4); 1110 + 1111 + for (i = (len / 2); i < len; i++) 1112 + s[i] = s[len - i - 1]; 1113 + 1114 + s[len - 1] |= SZ_COEF; 1115 + } else { 1116 + /* bilinear scaling for < 2:1 ratios */ 1117 + int v; /* overflow counter */ 1118 + int coeff, nxt; /* table output */ 1119 + int in_pos_inc = 2 * den; 1120 + int out_pos = num; 1121 + int out_pos_inc = 2 * num; 1122 + int init_carry = num - den; 1123 + int carry = init_carry; 1124 + 1125 + tmprsz.algo = RESIZE_ALGO_BILINEAR; 1126 + v = den + in_pos_inc; 1127 + do { 1128 + coeff = v - out_pos; 1129 + out_pos += out_pos_inc; 1130 + carry += out_pos_inc; 1131 + for (nxt = 0; v < out_pos; nxt++) { 1132 + v += in_pos_inc; 1133 + carry -= in_pos_inc; 1134 + } 1135 + 1136 + if (len > RESIZE_NUM_MAX) 1137 + return -EINVAL; 1138 + 1139 + coeff = ((coeff << BC_COEF) + 1140 + (in_pos_inc >> 1)) / in_pos_inc; 1141 + 1142 + if (coeff >= (SZ_COEF - 1)) 1143 + coeff--; 1144 + 1145 + coeff |= SZ_COEF; 1146 + s[len] = (unsigned char)coeff; 1147 + len++; 1148 + 1149 + for (i = 1; i < nxt; i++) { 1150 + if (len >= RESIZE_NUM_MAX) 1151 + return -EINVAL; 1152 + s[len] = 0; 1153 + len++; 1154 + } 1155 + } while (carry != init_carry); 1156 + } 1157 + tmprsz.len = len; 1158 + if (dir == RESIZE_DIR_H) 1159 + mf_in->width = pix_out->width; 1160 + else 1161 + mf_in->height = pix_out->height; 1162 + 1163 + if (apply) 1164 + memcpy(&pcdev->resizing[dir], &tmprsz, sizeof(tmprsz)); 1165 + } 1166 + return 0; 1167 + } 1168 + 1147 1169 static int mx2_camera_set_fmt(struct soc_camera_device *icd, 1148 1170 struct v4l2_format *f) 1149 1171 { ··· 1271 1059 struct v4l2_pix_format *pix = &f->fmt.pix; 1272 1060 struct v4l2_mbus_framefmt mf; 1273 1061 int ret; 1062 + 1063 + dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", 1064 + __func__, pix->width, pix->height); 1274 1065 1275 1066 xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); 1276 1067 if (!xlate) { ··· 1292 1077 if (ret < 0 && ret != -ENOIOCTLCMD) 1293 1078 return ret; 1294 1079 1080 + /* Store width and height returned by the sensor for resizing */ 1081 + pcdev->s_width = mf.width; 1082 + pcdev->s_height = mf.height; 1083 + dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n", 1084 + __func__, pcdev->s_width, pcdev->s_height); 1085 + 1086 + pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, 1087 + xlate->host_fmt->fourcc); 1088 + 1089 + memset(pcdev->resizing, 0, sizeof(pcdev->resizing)); 1090 + if ((mf.width != pix->width || mf.height != pix->height) && 1091 + pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { 1092 + if (mx2_emmaprp_resize(pcdev, &mf, pix, true) < 0) 1093 + dev_dbg(icd->parent, "%s: can't resize\n", __func__); 1094 + } 1095 + 1295 1096 if (mf.code != xlate->code) 1296 1097 return -EINVAL; 1297 1098 ··· 1317 1086 pix->colorspace = mf.colorspace; 1318 1087 icd->current_fmt = xlate; 1319 1088 1320 - if (cpu_is_mx27()) 1321 - pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, 1322 - xlate->host_fmt->fourcc); 1089 + dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n", 1090 + __func__, pix->width, pix->height); 1323 1091 1324 1092 return 0; 1325 1093 } ··· 1331 1101 struct v4l2_pix_format *pix = &f->fmt.pix; 1332 1102 struct v4l2_mbus_framefmt mf; 1333 1103 __u32 pixfmt = pix->pixelformat; 1104 + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); 1105 + struct mx2_camera_dev *pcdev = ici->priv; 1334 1106 unsigned int width_limit; 1335 1107 int ret; 1108 + 1109 + dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", 1110 + __func__, pix->width, pix->height); 1336 1111 1337 1112 xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); 1338 1113 if (pixfmt && !xlate) { ··· 1388 1153 if (ret < 0) 1389 1154 return ret; 1390 1155 1156 + dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n", 1157 + __func__, pcdev->s_width, pcdev->s_height); 1158 + 1159 + /* If the sensor does not support image size try PrP resizing */ 1160 + pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, 1161 + xlate->host_fmt->fourcc); 1162 + 1163 + memset(pcdev->resizing, 0, sizeof(pcdev->resizing)); 1164 + if ((mf.width != pix->width || mf.height != pix->height) && 1165 + pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { 1166 + if (mx2_emmaprp_resize(pcdev, &mf, pix, false) < 0) 1167 + dev_dbg(icd->parent, "%s: can't resize\n", __func__); 1168 + } 1169 + 1391 1170 if (mf.field == V4L2_FIELD_ANY) 1392 1171 mf.field = V4L2_FIELD_NONE; 1393 1172 /* ··· 1419 1170 pix->height = mf.height; 1420 1171 pix->field = mf.field; 1421 1172 pix->colorspace = mf.colorspace; 1173 + 1174 + dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n", 1175 + __func__, pix->width, pix->height); 1422 1176 1423 1177 return 0; 1424 1178 }