3D plot from lm model with a nonlinear parameter
If I understand correctly, shouldn't ls$coefficients[4] * x2 be ls$coefficients[4] * x2 * x2?

These details could be easily avoided if instead of writing your function to evaluate the fit with new data, you used predict.lm. This is what I think the code should look like, making other small improvements I will comment about below:

disp.density &lt;- 100L
qsec.density &lt;- 100L
disp.range &lt;- with(mtcars, seq(from       = min(disp),
                               to         = max(disp),
                               length.out = disp.density))
qsec.range &lt;- with(mtcars, seq(from       = min(qsec),
                               to         = max(qsec),
                               length.out = qsec.density))
data.grid &lt;- expand.grid(disp = disp.range,
                         qsec = qsec.range)

fit &lt;- lm(mpg ~ disp + qsec, data = mtcars)
predicted.vec &lt;- predict(ls, data.grid)
predicted.mat &lt;- matrix(predicted.vec, nrow = disp.density,
                                       ncol = qsec.density,
                                   dimnames = list(disp.range,
                                                   qsec.range))
library(???)
persp3D(z = predicted.mat, ticktype = "detailed")


And the same code should still work if you replace the formula mpg ~ disp + qsec with mpg ~ disp + poly(qsec, degree = 2). Some more notes:


It is recommended you avoid variable names like ls and t since they are very basic functions from R. Or you run the risk of shadowing them.
disp and qsec are not integers so using min(mtcars$disp):max(mtcars$disp) is a bit unpredictable. I feel it is better to use seq like I did, and with control over the grid density.
I was not able to test my code because you did not mention the package name for persp3D so it is possible it will need some adjustment. In the future, please make sure to give us everything we need to run your code.