1 /*
2  *  Driver for the Conexant CX23885/7/8 PCIe bridge
3  *
4  *  Various common ioctl() support functions
5  *
6  *  Copyright (c) 2009 Andy Walls <awalls@md.metrocast.net>
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23 
24 #include "cx23885.h"
25 #include "cx23885-ioctl.h"
26 
27 #include <media/v4l2-chip-ident.h>
28 
29 int cx23885_g_chip_ident(struct file *file, void *fh,
30 			 struct v4l2_dbg_chip_ident *chip)
31 {
32 	struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
33 	int err = 0;
34 	u8 rev;
35 
36 	chip->ident = V4L2_IDENT_NONE;
37 	chip->revision = 0;
38 	switch (chip->match.type) {
39 	case V4L2_CHIP_MATCH_HOST:
40 		switch (chip->match.addr) {
41 		case 0:
42 			rev = cx_read(RDR_CFG2) & 0xff;
43 			switch (dev->pci->device) {
44 			case 0x8852:
45 				/* rev 0x04 could be '885 or '888. Pick '888. */
46 				if (rev == 0x04)
47 					chip->ident = V4L2_IDENT_CX23888;
48 				else
49 					chip->ident = V4L2_IDENT_CX23885;
50 				break;
51 			case 0x8880:
52 				if (rev == 0x0e || rev == 0x0f)
53 					chip->ident = V4L2_IDENT_CX23887;
54 				else
55 					chip->ident = V4L2_IDENT_CX23888;
56 				break;
57 			default:
58 				chip->ident = V4L2_IDENT_UNKNOWN;
59 				break;
60 			}
61 			chip->revision = (dev->pci->device << 16) | (rev << 8) |
62 					 (dev->hwrevision & 0xff);
63 			break;
64 		case 1:
65 			if (dev->v4l_device != NULL) {
66 				chip->ident = V4L2_IDENT_CX23417;
67 				chip->revision = 0;
68 			}
69 			break;
70 		case 2:
71 			/*
72 			 * The integrated IR controller on the CX23888 is
73 			 * host chip 2.  It may not be used/initialized or sd_ir
74 			 * may be pointing at the cx25840 subdevice for the
75 			 * IR controller on the CX23885.  Thus we find it
76 			 * without using the dev->sd_ir pointer.
77 			 */
78 			call_hw(dev, CX23885_HW_888_IR, core, g_chip_ident,
79 				chip);
80 			break;
81 		default:
82 			err = -EINVAL; /* per V4L2 spec */
83 			break;
84 		}
85 		break;
86 	case V4L2_CHIP_MATCH_I2C_DRIVER:
87 		/* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */
88 		call_all(dev, core, g_chip_ident, chip);
89 		break;
90 	case V4L2_CHIP_MATCH_I2C_ADDR:
91 		/*
92 		 * We could return V4L2_IDENT_UNKNOWN, but we don't do the work
93 		 * to look if a chip is at the address with no driver.  That's a
94 		 * dangerous thing to do with EEPROMs anyway.
95 		 */
96 		call_all(dev, core, g_chip_ident, chip);
97 		break;
98 	default:
99 		err = -EINVAL;
100 		break;
101 	}
102 	return err;
103 }
104 
105 #ifdef CONFIG_VIDEO_ADV_DEBUG
106 static int cx23885_g_host_register(struct cx23885_dev *dev,
107 				   struct v4l2_dbg_register *reg)
108 {
109 	if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0))
110 		return -EINVAL;
111 
112 	reg->size = 4;
113 	reg->val = cx_read(reg->reg);
114 	return 0;
115 }
116 
117 static int cx23417_g_register(struct cx23885_dev *dev,
118 			      struct v4l2_dbg_register *reg)
119 {
120 	u32 value;
121 
122 	if (dev->v4l_device == NULL)
123 		return -EINVAL;
124 
125 	if ((reg->reg & 0x3) != 0 || reg->reg >= 0x10000)
126 		return -EINVAL;
127 
128 	if (mc417_register_read(dev, (u16) reg->reg, &value))
129 		return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */
130 
131 	reg->size = 4;
132 	reg->val = value;
133 	return 0;
134 }
135 
136 int cx23885_g_register(struct file *file, void *fh,
137 		       struct v4l2_dbg_register *reg)
138 {
139 	struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
140 
141 	if (!capable(CAP_SYS_ADMIN))
142 		return -EPERM;
143 
144 	if (reg->match.type == V4L2_CHIP_MATCH_HOST) {
145 		switch (reg->match.addr) {
146 		case 0:
147 			return cx23885_g_host_register(dev, reg);
148 		case 1:
149 			return cx23417_g_register(dev, reg);
150 		default:
151 			break;
152 		}
153 	}
154 
155 	/* FIXME - any error returns should not be ignored */
156 	call_all(dev, core, g_register, reg);
157 	return 0;
158 }
159 
160 static int cx23885_s_host_register(struct cx23885_dev *dev,
161 				   const struct v4l2_dbg_register *reg)
162 {
163 	if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0))
164 		return -EINVAL;
165 
166 	cx_write(reg->reg, reg->val);
167 	return 0;
168 }
169 
170 static int cx23417_s_register(struct cx23885_dev *dev,
171 			      const struct v4l2_dbg_register *reg)
172 {
173 	if (dev->v4l_device == NULL)
174 		return -EINVAL;
175 
176 	if ((reg->reg & 0x3) != 0 || reg->reg >= 0x10000)
177 		return -EINVAL;
178 
179 	if (mc417_register_write(dev, (u16) reg->reg, (u32) reg->val))
180 		return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */
181 	return 0;
182 }
183 
184 int cx23885_s_register(struct file *file, void *fh,
185 		       const struct v4l2_dbg_register *reg)
186 {
187 	struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
188 
189 	if (!capable(CAP_SYS_ADMIN))
190 		return -EPERM;
191 
192 	if (reg->match.type == V4L2_CHIP_MATCH_HOST) {
193 		switch (reg->match.addr) {
194 		case 0:
195 			return cx23885_s_host_register(dev, reg);
196 		case 1:
197 			return cx23417_s_register(dev, reg);
198 		default:
199 			break;
200 		}
201 	}
202 
203 	/* FIXME - any error returns should not be ignored */
204 	call_all(dev, core, s_register, reg);
205 	return 0;
206 }
207 #endif
208